[Checkins] SVN: grokui.admin/trunk/ Merge changes from ulif-securitynotifications into trunk.
Uli Fouquet
uli at gnufix.de
Tue Dec 23 05:52:37 EST 2008
Log message for revision 94273:
Merge changes from ulif-securitynotifications into trunk.
Changed:
U grokui.admin/trunk/CHANGES.txt
U grokui.admin/trunk/src/grokui/admin/README.txt
A grokui.admin/trunk/src/grokui/admin/interfaces.py
A grokui.admin/trunk/src/grokui/admin/security.py
A grokui.admin/trunk/src/grokui/admin/tests/infoviews.py
A grokui.admin/trunk/src/grokui/admin/tests/releaseinfo/
A grokui.admin/trunk/src/grokui/admin/tests/security.py
U grokui.admin/trunk/src/grokui/admin/tests/server.py
U grokui.admin/trunk/src/grokui/admin/tests/test_grokadmin_functional.py
U grokui.admin/trunk/src/grokui/admin/utilities.py
U grokui.admin/trunk/src/grokui/admin/view.py
U grokui.admin/trunk/src/grokui/admin/view_templates/grokadminmacros.pt
U grokui.admin/trunk/src/grokui/admin/view_templates/server.pt
-=-
Modified: grokui.admin/trunk/CHANGES.txt
===================================================================
--- grokui.admin/trunk/CHANGES.txt 2008-12-23 10:39:05 UTC (rev 94272)
+++ grokui.admin/trunk/CHANGES.txt 2008-12-23 10:52:36 UTC (rev 94273)
@@ -4,6 +4,21 @@
0.4 (unreleased)
================
+Feature changes
+---------------
+
+* Added a security notifier to inform users when security issues are
+ published on http://grok.zope.org. The notifier must be explicitly
+ enabled. You can also run your own site/directory to place security
+ notifications.
+
+* Added info views to get important information easier with tools like
+ ``curl``. Supported infos:
+
+ - Grok version used
+
+ - Current security notification (if any).
+
0.3 (2008-12-13)
================
Modified: grokui.admin/trunk/src/grokui/admin/README.txt
===================================================================
--- grokui.admin/trunk/src/grokui/admin/README.txt 2008-12-23 10:39:05 UTC (rev 94272)
+++ grokui.admin/trunk/src/grokui/admin/README.txt 2008-12-23 10:52:36 UTC (rev 94273)
@@ -38,7 +38,7 @@
Applications
-============
+------------
* List of all instanciated applications
@@ -60,8 +60,13 @@
Server
-======
+------
+* Set security notifications. Those are by default disabled, because
+ they mean home-calling functionality you may do not want. You can
+ enable/disable those notifications or set a URL to retrieve
+ information about security related problems.
+
* Start/Restart the server. Caution! This does not work, if the server
was started in 'foreground mode' (with 'zopectl fg').
@@ -83,7 +88,7 @@
Documentation
-=============
+-------------
* From here you get starting points to the more elaborated
documentation features of Grok, namely:
@@ -97,7 +102,26 @@
gives documentation to classes, packages and other things, which
are not instances.
+Maintaining grok installations with the admin UI
+================================================
+There are some special info views available especially for the use of
+system administrators that want to automate Grok administration in
+some aspects. They provide minimal information about certain topics.
+
+Currently the following infos are available this way:
+
+ - The grok version working in background::
+
+ curl -q -s -u admin:admin "http://localhost:8080/@@grokadmin/@@version"
+
+ - The security notification (if any)::
+
+ curl -q -s -u admin:admin "http://localhost:8080/@@grokadmin/@@secnote"
+
+Beside this you can pack the ZODB databases as described above.
+
+
Bugs, Caveats and Ways to Get Help
==================================
Copied: grokui.admin/trunk/src/grokui/admin/interfaces.py (from rev 94272, grokui.admin/branches/ulif-securitynotifications/src/grokui/admin/interfaces.py)
===================================================================
--- grokui.admin/trunk/src/grokui/admin/interfaces.py (rev 0)
+++ grokui.admin/trunk/src/grokui/admin/interfaces.py 2008-12-23 10:52:36 UTC (rev 94273)
@@ -0,0 +1,21 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Interfaces for the admin UI."""
+
+from zope.interface import Interface
+
+class ISecurityNotifier(Interface):
+ """A notifier the looks up security warnings somewhere.
+ """
+
Copied: grokui.admin/trunk/src/grokui/admin/security.py (from rev 94272, grokui.admin/branches/ulif-securitynotifications/src/grokui/admin/security.py)
===================================================================
--- grokui.admin/trunk/src/grokui/admin/security.py (rev 0)
+++ grokui.admin/trunk/src/grokui/admin/security.py 2008-12-23 10:52:36 UTC (rev 94273)
@@ -0,0 +1,189 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Security notifications for `grokui.admin`.
+
+The machinery to do home-calling security notifications.
+"""
+import grok
+import time
+import urllib2
+import urlparse
+from zope.app.appsetup.interfaces import IDatabaseOpenedWithRootEvent
+from zope.app.folder.interfaces import IRootFolder
+from zope.component import adapter, provideHandler
+from persistent import Persistent
+from grokui.admin.interfaces import ISecurityNotifier
+from grokui.admin.utilities import getVersion, TimeoutableHTTPHandler
+
+class SecurityScreen(grok.ViewletManager):
+ """A viewlet manager that keeps security related notifications.
+ """
+ grok.name('grokadmin_security')
+ grok.context(IRootFolder)
+
+class SecurityNotificationViewlet(grok.Viewlet):
+ """Viewlet displaying notifications from a local `SecurityNotifier`.
+ """
+ grok.context(IRootFolder)
+
+ @property
+ def security_notifier(self):
+ """Get a local security notifier.
+
+ The security notifier is installed as a local utility by an
+ event handler triggered on startup.
+ """
+ site = grok.getSite()
+ site_manager = site.getSiteManager()
+ return site_manager.queryUtility(ISecurityNotifier, default=None)
+
+ def render(self):
+ return self.security_notifier.getNotification()
+
+class SecurityNotifier(Persistent):
+ """A security notifier.
+
+ It can be placed in a site to store notification dates and other
+ data persistently.
+ """
+
+ grok.implements(ISecurityNotifier)
+
+ VERSION = 1 # for possibly updates/downgrades
+ MSG_DISABLED = u'Security notifications are disabled.'
+ DEFAULT_URL = 'http://grok.zope.org/releaseinfo/'
+
+ lookup_url = DEFAULT_URL
+ last_lookup = None # When did we do the last lookup?
+ lookup_timeout = 2 # Number of seconds to wait
+ last_display = None # When did we display the last time?
+ enabled = False # By default we disable the notfier.
+
+ lookup_frequency = 3600 * 3 # Lookup every three hours.
+ display_frequency = 3600 * 3 # Display warnings every three hours.
+
+ _message = u''
+ _warningstate = False
+
+ def enable(self):
+ """Enable security notifications.
+ """
+ self.enabled = True
+ return
+
+ def disable(self):
+ """Disable security notifications.
+ """
+ self.enabled = False
+ return
+
+ def getNotification(self):
+ """Get the current security notification.
+ """
+ if self.enabled is False:
+ return self.MSG_DISABLED
+ self.updateMessage()
+ return self._message
+
+ def isWarning(self):
+ self.updateMessage()
+ return self._warningstate
+
+ def updateMessage(self):
+ """Update the security message.
+ """
+ if self.enabled is False:
+ return
+ if self.last_lookup is not None:
+ if time.time() - self.lookup_frequency < self.last_lookup:
+ return
+ self.fetchMessage()
+ return
+
+ def fetchMessage(self):
+ """Possibly fetch security notfications from grok.zope.org.
+ """
+ if self.enabled is False:
+ # Safety belt.
+ return
+ version = getVersion('grok')
+ filename = 'grok-%s.security.txt' % version
+ url = urlparse.urljoin(self.lookup_url, filename)
+ # We create a HTTP handler with a timeout.
+ http_handler = TimeoutableHTTPHandler(timeout=self.lookup_timeout)
+ opener = urllib2.build_opener(http_handler)
+ req = urllib2.Request(url)
+ try:
+ self._message = opener.open(req).read()
+ self._warningstate = True
+ except (urllib2.HTTPError, OSError), e:
+ if (getattr(e, 'code', None) == 404) or (
+ getattr(e, 'errno', None) == 2):
+ # No security warning found, good message.
+ self._message = u''
+ self._warningstate = False
+ except:
+ # An unexpected problem occured...
+ pass
+ if self._message == self.MSG_DISABLED:
+ self._message = u''
+ self.last_lookup = time.time()
+ return
+
+ def setLookupURL(self, url):
+ """Set the url to lookup notifications.
+ """
+ self.lookup_url = url
+ self.last_lookup = None
+ return
+
+ def display(self):
+ """Display the message.
+
+ In fact we only keep track of timestamps of display actions.
+ """
+ self.last_display = time.time()
+ return
+
+def setupSecurityNotification(site):
+ """Setup a SecurityNotifier as persistent utility.
+
+ The utility is installed as a local and persistent utility. It is
+ local to `site` and installed under the name
+ ``grokadmin_security`` in the site manager of `site`.
+
+ It can be retrieved with a call like::
+
+ site.getSiteManager().getUtiliy(ISecurityNotifier)
+
+ See also ``security.py`` in ``tests``.
+ """
+ site_manager = site.getSiteManager()
+ if 'grokadmin_security' not in site_manager:
+ site_manager['grokadmin_security'] = SecurityNotifier()
+ utility = site_manager['grokadmin_security']
+ site_manager.registerUtility(utility, ISecurityNotifier, name=u'')
+ return
+
+ at adapter(IDatabaseOpenedWithRootEvent)
+def securitySetupHandler(event):
+ """Call security notification setup as soon as DB is ready.
+ """
+ from zope.app.appsetup.bootstrap import getInformationFromEvent
+
+ db, connection, root, root_folder = getInformationFromEvent(event)
+ setupSecurityNotification(root_folder)
+
+# ...then install the event handler:
+provideHandler(securitySetupHandler)
Copied: grokui.admin/trunk/src/grokui/admin/tests/infoviews.py (from rev 94272, grokui.admin/branches/ulif-securitynotifications/src/grokui/admin/tests/infoviews.py)
===================================================================
--- grokui.admin/trunk/src/grokui/admin/tests/infoviews.py (rev 0)
+++ grokui.admin/trunk/src/grokui/admin/tests/infoviews.py 2008-12-23 10:52:36 UTC (rev 94273)
@@ -0,0 +1,64 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+Information views
+*****************
+
+`grokui.admin` provides info views that can be fetched with `curl` or
+similar tools to get a quick overview over the installation status.
+
+They provide minimal information bits about for example the current
+grok version and are provided for site administrators that want to
+keep track of the site status via usual system administration tools.
+
+We must have a browser available::
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+
+We must be authenticated to fetch those infos::
+
+ >>> browser.open('http://localhost/@@grokadmin/@@version')
+ Traceback (most recent call last):
+ ...
+ HTTPError: HTTP Error 401: Unauthorized
+
+Getting the current grok version
+--------------------------------
+
+When we are authenticated, we can retrieve the grok version used::
+
+ >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+ >>> browser.open('http://localhost/@@grokadmin/@@version')
+ >>> print browser.contents
+ grok ...
+
+The returned string has the format 'grok <MAJ>.<MIN>[.<BUGFIX>]' with
+a major release number (<MAJ>), a minor release number (<MIN>) and an
+oiptional bugfix release number like 'grok 0.14.1'::
+
+ >>> import re
+ >>> re.match('^grok \d+\.\d+(\.\d+)?.*$', browser.contents)
+ <_sre.SRE_Match object at 0x...>
+
+Getting the current security notification
+-----------------------------------------
+
+We can get the current security notification::
+
+ >>> browser.open('http://localhost/@@grokadmin/@@secnote')
+ >>> print browser.contents
+ Security notifications are disabled.
+
+"""
Copied: grokui.admin/trunk/src/grokui/admin/tests/security.py (from rev 94272, grokui.admin/branches/ulif-securitynotifications/src/grokui/admin/tests/security.py)
===================================================================
--- grokui.admin/trunk/src/grokui/admin/tests/security.py (rev 0)
+++ grokui.admin/trunk/src/grokui/admin/tests/security.py 2008-12-23 10:52:36 UTC (rev 94273)
@@ -0,0 +1,245 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+Tests for security notifications.
+
+The `SecurityNotifier`
+======================
+
+A security notifier is an object that checks an URL for security
+warnings and delivers them. It keeps track of lookup-dates etc., so
+that lookups are not performed too often.
+
+Because this is a 'calling-home' feature, it is disabled by
+default. SecurityNotifiers know about their status (enabled or
+disabled) and do no lookups when disabled.
+
+Security notifications are handled by a `SecurityNotifier`::
+
+ >>> from grokui.admin.security import SecurityNotifier
+ >>> sn = SecurityNotifier()
+
+Instances provide the `ISecurityNotifier` interface.
+
+ >>> from grokui.admin.interfaces import ISecurityNotifier
+ >>> ISecurityNotifier.providedBy(sn)
+ True
+
+
+Enabling and disabling the notifier
+-----------------------------------
+
+By default a security notifier is not enabled::
+
+ >>> sn.enabled
+ False
+
+We enable it::
+
+ >>> sn.enable()
+ >>> sn.enabled
+ True
+
+and disable again::
+
+ >>> sn.disable()
+ >>> sn.enabled
+ False
+
+While being disabled, the notifier will do no lookups, even if
+`updateMessage` or similar methods are called.
+
+Getting notifications
+---------------------
+
+We can get a notification, of course. Asking for that will not trigger
+a lookup, while the notifier is disabled::
+
+ >>> sn.getNotification()
+ u'Security notifications are disabled.'
+
+Even an explicit lookup request will not do lookups, while the
+notifier is not enabled::
+
+ >>> sn.updateMessage()
+ >>> sn.getNotification()
+ u'Security notifications are disabled.'
+
+
+Where to look for notifications
+-------------------------------
+
+When we want to do real lookups, then by default the Grok site is
+asked::
+
+ >>> sn.lookup_url
+ 'http://grok.zope.org/releaseinfo/'
+
+But we can change the place to look for security warnings. We prepared
+a local directory with some warnings, which we will use as our
+information source::
+
+ >>> import os.path
+ >>> fake_source = os.path.join(os.path.dirname(__file__), 'releaseinfo')
+ >>> fake_source_url = 'file://%s' % fake_source + os.path.sep
+ >>> sn.setLookupURL(fake_source_url)
+
+Now we can safely enable the notifier and see, whether there are infos
+for us. It is sufficient to call `getNotification()` as this will
+update the stored information automatically.
+
+Before we really start, we will have a look at the lookup timestamp,
+that stores our last tries::
+
+ >>> last_lookup = sn.last_lookup
+ >>> last_lookup is None
+ True
+
+ >>> sn.enable()
+ >>> note = sn.getNotification()
+ >>> note
+ u''
+
+Ah, there is no security warning for our version. So let us create
+one::
+
+ >>> from grokui.admin.utilities import getVersion
+ >>> version = getVersion('grok')
+ >>> fake_warning_file = 'grok-%s.security.txt' % version
+ >>> fake_warning_file = os.path.join(fake_source, fake_warning_file)
+ >>> open(fake_warning_file, 'w').write('You better smash %s' % version)
+
+When we now ask the security notifier again::
+
+ >>> sn.getNotification()
+ u''
+
+We got the same answer as before. Why? The lookups are done only in
+certain intervals to reduce the amount of outgoing traffic. When we
+fix the lookup timestamp, we get the real value::
+
+ >>> sn.last_lookup = None
+ >>> sn.getNotification()
+ 'You better smash ...'
+
+To decide, whether the delivered string is actually a warning, we can
+call the `isWarning` method::
+
+ >>> sn.isWarning()
+ True
+
+
+`SecurityNotifier` in `grokui.admin`
+====================================
+
+In `grokui.admin` the security notifier is installed at startup as
+local utility, that can be looked up by the `ISecurityNotifer`
+interface.
+
+Currently, as `grokui.admin` is merely a collection of views bound to
+root folders, also the security notification utility is normally
+managed by the local site manager of the root folder::
+
+ >>> root = getRootFolder()
+ >>> sm = root.getSiteManager()
+
+Now we can lookup the utility::
+
+ >>> from grokui.admin.interfaces import ISecurityNotifier
+ >>> notifier = sm.getUtility(ISecurityNotifier)
+ >>> notifier
+ <grokui.admin.security.SecurityNotifier object at 0x...>
+
+The utility is local, because different root folders might want
+different settings for security notifications.
+
+The utility is persistent, so that the settings are preserved when
+shutting down.
+
+Immediately after startup, the notifier exists, but is disabled::
+
+ >>> notifier.enabled
+ False
+
+We can get notifications, of course::
+
+ >>> notifier.getNotification()
+ u'Security notifications are disabled.'
+
+We can check in a formal way, whether the current notification is a
+warning::
+
+ >>> notifier.isWarning()
+ False
+
+The notifier we got here is the same as when using the UI. We log into
+the admin screen to set a new notifier URL::
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+ >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+ >>> browser.open('http://localhost/@@server')
+
+On the server administration page we can see the status of our
+notifier (enabled or disabled)::
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ... Status: Security notifications are disabled
+ ...
+
+We can also see the current message which also informs us, if security
+notifications are disabled. This message is displayed on (nearly)
+every `grokui.admin` page::
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...<div id="securitynotifications">Security notifications are disabled.</div>
+ ...
+
+But we are not bound to the default URL to do lookups. We can set
+another one ourselves::
+
+ >>> browser.getControl(name='secnotesource').value=fake_source_url
+ >>> browser.getControl('Set URL').click()
+
+Now, as we set a lookup URL which we can control better, we can enable
+the security notifications::
+
+ >>> browser.getControl('Enable').click()
+
+The result of the lookup again is displayed. This time we get a 'real'
+result::
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...<div id="securitynotifications">You better smash ...</div>
+ ...
+
+We can of course disable security notifications at any time::
+
+ >>> browser.getControl('Disable').click()
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...<div id="securitynotifications">Security notifications are disabled.</div>
+ ...
+
+Clean up::
+
+ >>> import os
+ >>> os.unlink(fake_warning_file)
+
+
+
+"""
Modified: grokui.admin/trunk/src/grokui/admin/tests/server.py
===================================================================
--- grokui.admin/trunk/src/grokui/admin/tests/server.py 2008-12-23 10:39:05 UTC (rev 94272)
+++ grokui.admin/trunk/src/grokui/admin/tests/server.py 2008-12-23 10:52:36 UTC (rev 94273)
@@ -55,7 +55,7 @@
If we submit that message, it should appear in the page::
- >>> msg_form = browser.getForm(index=2)
+ >>> msg_form = browser.getForm(index=3)
>>> msg_form.submit()
>>> print browser.contents
<html xmlns="http://www.w3.org/1999/xhtml">
Modified: grokui.admin/trunk/src/grokui/admin/tests/test_grokadmin_functional.py
===================================================================
--- grokui.admin/trunk/src/grokui/admin/tests/test_grokadmin_functional.py 2008-12-23 10:39:05 UTC (rev 94272)
+++ grokui.admin/trunk/src/grokui/admin/tests/test_grokadmin_functional.py 2008-12-23 10:52:36 UTC (rev 94273)
@@ -15,6 +15,10 @@
def setUp(test):
FunctionalTestSetup().setUp()
+ # In functional tests no IDatabaseOpenedWithRootEvent are fired. We
+ # therefore have to setup security notifications manually
+ from grokui.admin.security import setupSecurityNotification
+ setupSecurityNotification(getRootFolder())
def tearDown(test):
FunctionalTestSetup().tearDown()
Modified: grokui.admin/trunk/src/grokui/admin/utilities.py
===================================================================
--- grokui.admin/trunk/src/grokui/admin/utilities.py 2008-12-23 10:39:05 UTC (rev 94272)
+++ grokui.admin/trunk/src/grokui/admin/utilities.py 2008-12-23 10:52:36 UTC (rev 94273)
@@ -13,8 +13,12 @@
##############################################################################
"""Helper functions for grok admin.
"""
+import httplib
+import pkg_resources
import re
+import socket
import urllib
+import urllib2
from zope.tal.taldefs import attrEscape
from urlparse import urlparse, urlunparse
@@ -267,3 +271,62 @@
or item for item in v]
url += '?' + urllib.urlencode(data, doseq=True)
return url
+
+def getVersion(pkgname):
+ """Determine the version of `pkgname` used in background.
+ """
+ info = pkg_resources.get_distribution(pkgname)
+ if info.has_version and info.version:
+ return info.version
+ return None
+
+class TimeoutableHTTPConnection(httplib.HTTPConnection):
+ """A customised HTTPConnection allowing a per-connection
+ timeout, specified at construction.
+ """
+
+ def __init__(self, host, port=None, strict=None, timeout=None):
+ httplib.HTTPConnection.__init__(self, host, port,
+ strict)
+ self.timeout = timeout
+
+ def connect(self):
+ """Override HTTPConnection.connect to connect to
+ host/port specified in __init__."""
+
+ msg = "getaddrinfo returns an empty list"
+ for res in socket.getaddrinfo(self.host, self.port,
+ 0, socket.SOCK_STREAM):
+ af, socktype, proto, canonname, sa = res
+ try:
+ self.sock = socket.socket(af, socktype, proto)
+ if self.timeout: # this is the new bit
+ self.sock.settimeout(self.timeout)
+ self.sock.connect(sa)
+ except socket.error, msg:
+ if self.sock:
+ self.sock.close()
+ self.sock = None
+ continue
+ break
+ if not self.sock:
+ raise socket.error, msg
+
+class TimeoutableHTTPHandler(urllib2.HTTPHandler):
+ """A customised HTTPHandler which times out connection
+ after the duration specified at construction.
+ """
+
+ def __init__(self, timeout=None):
+ urllib2.HTTPHandler.__init__(self)
+ self.timeout = timeout
+
+ def http_open(self, req):
+ """Override http_open."""
+
+ def makeConnection(host, port=None, strict=None):
+ return TimeoutableHTTPConnection(host, port, strict,
+ timeout = self.timeout)
+
+ return self.do_open(makeConnection, req)
+
Modified: grokui.admin/trunk/src/grokui/admin/view.py
===================================================================
--- grokui.admin/trunk/src/grokui/admin/view.py 2008-12-23 10:39:05 UTC (rev 94272)
+++ grokui.admin/trunk/src/grokui/admin/view.py 2008-12-23 10:52:36 UTC (rev 94273)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2007 Zope Corporation and Contributors.
+# Copyright (c) 2007-2008 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -16,17 +16,21 @@
import grok
import os
import inspect
+import time
from urllib import urlencode
+from grokui.admin.interfaces import ISecurityNotifier
+
from grokui.admin import docgrok
from grokui.admin.docgrok import DocGrok, DocGrokPackage, DocGrokModule
from grokui.admin.docgrok import DocGrokTextFile, DocGrokGrokApplication
from grokui.admin.docgrok import DocGrokClass, DocGrokInterface, getItemLink
from grokui.admin.objectinfo import ZopeObjectInfo
+from grokui.admin.security import SecurityNotifier
from grokui.admin.utilities import getPathLinksForObject, getPathLinksForClass
from grokui.admin.utilities import getPathLinksForDottedName, getParentURL
-from grokui.admin.utilities import getURLWithParams
+from grokui.admin.utilities import getURLWithParams, getVersion
from ZODB.broken import Broken
from ZODB.interfaces import IDatabase
@@ -63,6 +67,40 @@
class ManageApplications(grok.Permission):
grok.name('grok.ManageApplications')
+class GrokAdminInfoView(grok.View):
+ """A base to provide machinereadable views.
+ """
+ grok.name('grokadmin')
+ grok.require('grok.ManageApplications')
+
+ def render(self):
+ return u'go to @@version or @@secnotes'
+
+class GrokAdminVersion(grok.View):
+ """Display grok version.
+
+ Call this view via http://localhost:8080/@@grokadmin/@@version
+ """
+ grok.name('version')
+ grok.context(GrokAdminInfoView)
+ grok.require('grok.ManageApplications')
+ def render(self):
+ return u'grok %s' % (getVersion('grok'),)
+
+class GrokAdminSecurityNotes(grok.View):
+ """Display current security notification.
+
+ Call this view via http://localhost:8080/@@grokadmin/@@secnote
+ """
+ grok.name('secnote')
+ grok.context(GrokAdminInfoView)
+ grok.require('grok.ManageApplications')
+ def render(self):
+ site = grok.getSite()
+ site_manager = site.getSiteManager()
+ notifier = site_manager.queryUtility(ISecurityNotifier, default=None)
+ return notifier.getNotification()
+
class Add(grok.View):
"""Add an application.
"""
@@ -157,6 +195,14 @@
"""
+ @property
+ def grok_version(self):
+ return getVersion('grok')
+
+ @property
+ def grokuiadmin_version(self):
+ return getVersion('grokui.admin')
+
def root_url(self, name=None):
obj = self.context
result = ""
@@ -441,6 +487,36 @@
grok.require('grok.ManageApplications')
@property
+ def security_notifier_url(self):
+ """Get the URL to look up for security warnings.
+ """
+ return self.security_notifier.lookup_url
+
+ @property
+ def security_notifier(self):
+ """Get a local security notifier.
+
+ The security notifier is installed as a local utility by an
+ event handler in the security module.
+ """
+ site = grok.getSite()
+ site_manager = site.getSiteManager()
+ return site_manager.queryUtility(ISecurityNotifier, default=None)
+
+ @property
+ def secnotes_enabled(self):
+ if self.security_notifier is None:
+ # Safety belt if installation of notifier failed
+ return False
+ return self.security_notifier.enabled
+
+ @property
+ def secnotes_message(self):
+ if self.security_notifier is None:
+ return u'Security notifier is not installed.'
+ return self.security_notifier.getNotification()
+
+ @property
def server_control(self):
return zope.component.queryUtility(IServerControl, '', None)
@@ -458,14 +534,35 @@
if messages:
return messages[0]
+ def updateSecurityNotifier(self, setsecnotes=None, setsecnotesource=None,
+ secnotesource=None):
+ if self.security_notifier is None:
+ return
+ if setsecnotesource is not None:
+ self.security_notifier.setLookupURL(secnotesource)
+ if setsecnotes is not None:
+ if self.security_notifier.enabled is True:
+ self.security_notifier.disable()
+ else:
+ self.security_notifier.enable()
+ if self.secnotes_enabled is False:
+ return
+ return
+
def update(self, time=None, restart=None, shutdown=None,
- admin_message=None, submitted=False,
- dbName="", pack=None, days=0):
+ setsecnotes=None, secnotesource=None, setsecnotesource=None,
+ admin_message=None, submitted=False, dbName="", pack=None,
+ days=0):
# Packing control
if pack is not None:
return self.pack(dbName, days)
+ # Security notification control
+ self.updateSecurityNotifier(setsecnotes, setsecnotesource,
+ secnotesource)
+
+
if not submitted:
return
Modified: grokui.admin/trunk/src/grokui/admin/view_templates/grokadminmacros.pt
===================================================================
--- grokui.admin/trunk/src/grokui/admin/view_templates/grokadminmacros.pt 2008-12-23 10:39:05 UTC (rev 94272)
+++ grokui.admin/trunk/src/grokui/admin/view_templates/grokadminmacros.pt 2008-12-23 10:52:36 UTC (rev 94273)
@@ -71,13 +71,8 @@
Server Control
</span>
</span>
-<!--
<a href=""
- tal:attributes="href view/root_url">Debug</a>
--->
-
- <a href=""
tal:attributes="href string:${view/root_url}/docgrok/">
<span tal:attributes="class python:view.in_docgrok() and 'emph'"
>Documentation</span>
@@ -88,6 +83,10 @@
<div id="messages" tal:content="structure context/@@messages" />
+ <div id="securitynotifications"
+ tal:content="structure provider:grokadmin_security"
+ tal:on-error="nothing" />
+
<div metal:define-slot="content">
<h1>Welcome to Grok!</h1>
Modified: grokui.admin/trunk/src/grokui/admin/view_templates/server.pt
===================================================================
--- grokui.admin/trunk/src/grokui/admin/view_templates/server.pt 2008-12-23 10:39:05 UTC (rev 94272)
+++ grokui.admin/trunk/src/grokui/admin/view_templates/server.pt 2008-12-23 10:52:36 UTC (rev 94273)
@@ -2,6 +2,51 @@
<div metal:fill-slot="content">
<h1>Manage your Zope 3 instance</h1>
+ <fieldset>
+ <legend>Security notifications</legend>
+ <form method="post" action=""
+ tal:attributes="action
+ string:${context/@@absolute_url}/server">
+ <div>
+ Running Grok <span tal:replace="view/grok_version" /> /
+ grokui.admin <span tal:replace="view/grokuiadmin_version" />
+ </div>
+ <div> </div>
+ <div>
+ You can be notified if serious security-related issues were
+ discovered that concern your installation.
+ </div>
+ <div>
+ Note, that if you enable this, HTTP-lookups of the grok site
+ will happen to check, whether there are any published
+ security issues for the installed version of grok.
+ </div>
+ <div> </div>
+ <div>
+ <label for="secnotesource">URL to lookup:</label>
+ <input type="text" size="30" name="secnotesource"
+ tal:attributes="value view/security_notifier_url" />
+ <input class="button" type="submit" name="setsecnotesource"
+ value="Set URL" />
+ </div>
+ <div> </div>
+ <div tal:condition="view/secnotes_enabled">
+ <span class="emph">
+ Status: Security notifications are enabled
+ </span>
+ <input class="button" type="submit" name="setsecnotes"
+ value="Disable" />
+ </div>
+ <div tal:condition="not: view/secnotes_enabled">
+ <span class="emph">
+ Status: Security notifications are disabled
+ </span>
+ <input class="button" type="submit" name="setsecnotes"
+ value="Enable" />
+ </div>
+ </form>
+ </fieldset>
+
<form method="post" action=""
tal:attributes="action string:${context/@@absolute_url}/server">
<fieldset>
More information about the Checkins
mailing list