[Checkins] SVN: Sandbox/ulif/grok-adminui/s Added session based login.

Uli Fouquet uli at gnufix.de
Tue Jul 24 08:22:49 EDT 2007


Log message for revision 78307:
  Added session based login.

Changed:
  U   Sandbox/ulif/grok-adminui/setup.py
  U   Sandbox/ulif/grok-adminui/src/grok/admin/__init__.py
  U   Sandbox/ulif/grok-adminui/src/grok/admin/view.py
  A   Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/loginform.pt
  U   Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt
  U   Sandbox/ulif/grok-adminui/src/grok/ftests/admin/apps.py

-=-
Modified: Sandbox/ulif/grok-adminui/setup.py
===================================================================
--- Sandbox/ulif/grok-adminui/setup.py	2007-07-24 11:24:41 UTC (rev 78306)
+++ Sandbox/ulif/grok-adminui/setup.py	2007-07-24 12:22:48 UTC (rev 78307)
@@ -22,6 +22,9 @@
                       'ZODB3',
                       'zope.annotation',
                       'zope.app.apidoc',
+                      'zope.app.applicationcontrol',
+                      'zope.app.appsetup',
+                      'zope.app.authentication',
                       'zope.app.catalog',
                       'zope.app.component',
                       'zope.app.container',
@@ -31,7 +34,11 @@
                       'zope.app.publication',
                       'zope.app.publisher',
                       'zope.app.renderer',
+                      'zope.app.security',
+                      'zope.app.securitypolicy',
                       'zope.app.testing',
+                      'zope.app.twisted',
+                      'zope.app.zcmlfiles',
                       'zope.component',
                       'zope.configuration',
                       'zope.dottedname',
@@ -41,15 +48,13 @@
                       'zope.interface',
                       'zope.lifecycleevent',
                       'zope.pagetemplate',
+                      'zope.proxy',
                       'zope.publisher',
                       'zope.schema',
                       'zope.security',
                       'zope.testing',
                       'zope.traversing',
                       'zope.testbrowser',
-                      'zope.app.twisted',
-                      'zope.app.securitypolicy',
-                      'zope.app.zcmlfiles',
                       'zc.catalog',
                       'z3c.flashmessage >=1.0dev-r77761',
                       ],

Modified: Sandbox/ulif/grok-adminui/src/grok/admin/__init__.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/__init__.py	2007-07-24 11:24:41 UTC (rev 78306)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/__init__.py	2007-07-24 12:22:48 UTC (rev 78307)
@@ -1 +1,131 @@
-#make this a package
+"""Initialize grok admin application.
+
+The grok admin application provides a session based login, which
+eventually must be enabled using Pluggable Authentication
+Utilities. This is done here.
+"""
+
+from zope.component import adapter, provideHandler
+from zope.app.appsetup.interfaces import IDatabaseOpenedWithRootEvent
+
+
+def getPrincipalCredentialsFromZCML():
+    """Read all principals' attributes from site.zcml.
+    """
+    import xml.sax
+    from zope.app.appsetup.appsetup import getConfigSource
+
+    class SAXPrincipalFinder(xml.sax.ContentHandler):
+        """Parse an XML file and get attributes of ``principal`` tags.
+
+        The principal tags of site.xml contain the credentials of
+        principals as attributes. The attributes usually are 'id',
+        'login', 'password', 'title' and other more. And usually only
+        one pricipal is defined: the manager.
+        """
+        result = []
+
+        def startElement(self, name, attrs):
+            if name != 'principal':
+                return
+            self.result.append(dict(attrs.copy()))
+
+    site_zcml_file = getConfigSource()
+    principal_finder = SAXPrincipalFinder()
+    xml.sax.parse(site_zcml_file, principal_finder)
+    return principal_finder.result
+
+
+def setupSessionAuthentication(root_folder=None,
+                               principal_credentials=[{u'id': u'zope.manager',
+                                                      u'login': u'grok',
+                                                      u'password': u'grok',
+                                                      u'title': u'Manager'
+                                                      }],
+                               auth_foldername=u'authentication',
+                               userfolder_name=u'Users',
+                               userfolder_prefix=u'grokadmin'
+                               ):
+    """Add session authentication PAU to root_folder.
+
+    Add a PluggableAuthentication in site manager of
+    root_folder. ``auth_foldername`` gives the name of the PAU to
+    install, userfolder_prefix the prefix of the authenticator plugin
+    (a simple ``PrincipalFolder``), which will be created in the PAU
+    and gets name ``userfolder_name``. ``principal_credentials`` is a
+    list of dicts with, well, principal_credentials. The keys ``id``,
+    ``login``, ``password`` and ``title`` are required for each
+    element of this list.
+    """
+    from zope.component import getUtilitiesFor
+    from zope.security.proxy import removeSecurityProxy
+    from zope.app.security.interfaces import IAuthentication
+    from zope.app.securitypolicy.interfaces import IPrincipalRoleManager
+    from zope.app.securitypolicy.interfaces import IRole
+    from zope.app.authentication import PluggableAuthentication
+    from zope.app.authentication.interfaces import IAuthenticatorPlugin
+    from zope.app.authentication.principalfolder import PrincipalFolder
+    from zope.app.authentication.principalfolder import InternalPrincipal
+
+    sm = root_folder.getSiteManager()
+    if auth_foldername in sm.keys():
+        # There is already a folder of this name.
+        return
+
+    pau = PluggableAuthentication()
+    users = PrincipalFolder(userfolder_prefix)
+
+    # Add users into principals folder to enable login...
+    for user in principal_credentials:
+        # XXX make sure, the keys exist...
+        user['id'] = user['id'].rsplit('.',1)[-1]
+        user_title = user['title']
+        principal = InternalPrincipal(user['login'],
+                                      user['password'],
+                                      user['title'])
+        users[user['id']] = principal
+
+    # Configure the PAU...
+    pau.authenticatorPlugins = (userfolder_name,)
+    pau.credentialsPlugins = ("No Challenge if Authenticated",
+                              "Session Credentials")
+
+    # Add the pau and its plugin to the root_folder...
+    sm[auth_foldername] = pau
+    sm[auth_foldername][userfolder_name] = users
+    pau.authenticatorPlugins = (users.__name__,)
+
+    # Register the PAU with the site...
+    sm.registerUtility(pau, IAuthentication)
+    sm.registerUtility(users, IAuthenticatorPlugin, name=userfolder_name)
+
+    # Add manager roles to new users...
+    # XXX the real roles could be obtained from site.zcml.
+    role_ids = [name for name, util in getUtilitiesFor(IRole, root_folder)]
+    user_ids = [users.prefix + p['id'] for p in principal_credentials]
+    role_manager = IPrincipalRoleManager(root_folder)
+    role_manager = removeSecurityProxy(role_manager)
+    for role in role_ids:
+        for user_id in user_ids:
+            role_manager.assignRoleToPrincipal(role,user_id)
+
+
+
+# If a new database is created, initialize a session based
+# authentication.
+#
+# First create an eventhandler `adminSetup`, that is
+# called, whenever a database is opened...
+ at adapter(IDatabaseOpenedWithRootEvent)
+def adminSetup(event):
+    from zope.app.appsetup.bootstrap import getInformationFromEvent
+    
+    db, connection, root, root_folder = getInformationFromEvent(event)
+    principal_credentials = getPrincipalCredentialsFromZCML()
+    setupSessionAuthentication(root_folder = root_folder,
+                               principal_credentials = principal_credentials)
+
+
+# ...then install the event handler:
+provideHandler(adminSetup)
+

Modified: Sandbox/ulif/grok-adminui/src/grok/admin/view.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view.py	2007-07-24 11:24:41 UTC (rev 78306)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view.py	2007-07-24 12:22:48 UTC (rev 78307)
@@ -25,6 +25,7 @@
 from zope.app.apidoc.codemodule.text import TextFile
 from zope.app.apidoc.codemodule.zcml import ZCMLFile
 
+from zope.app.security.interfaces import IUnauthenticatedPrincipal
 
 from zope.proxy import removeAllProxies
 
@@ -202,6 +203,28 @@
         self.redirect(self.url('applications'))
 
 
+class loginForm(GAIAView):
+    """A login screen for session based authentication.
+
+    To activate loginForm, i.e. session based authentication, an
+    appropriate PluggableAuthenticationUtility (PAU) must be set up in
+    the applications root folder (which happens here to be the global
+    root folder). The setup is done for the admin app in __init__.py.
+    """
+    # 'loginForm.html' is the page template name, that standard
+    # session based authentication looks for. The form must provide an
+    # input field 'login' for the username and another input field
+    # 'password'.
+    grok.name('loginForm.html')
+
+    def update(self, login=None, password=None, camefrom=None):
+        request = self.request
+        if (not IUnauthenticatedPrincipal.providedBy(request.principal)):
+            camefrom = request.get('camefrom', '.')
+            self.redirect(camefrom)
+        return
+
+
 class Applications(GAIAView):
     """View for application management."""
 

Added: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/loginform.pt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/loginform.pt	                        (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/loginform.pt	2007-07-24 12:22:48 UTC (rev 78307)
@@ -0,0 +1,32 @@
+<html metal:use-macro="context/@@macros/gaia-page">
+  <head>
+    <title metal:fill-slot="title">Grok Login</title>
+  </head>
+  <body>
+    <div metal:fill-slot="menu-links" />
+    <div metal:fill-slot="content">
+      <h1>Welcome to Grok</h1>
+      <form method="post">
+	<fieldset>
+	  <legend>Login</legend>
+
+	  <table>
+	    <tr>
+	      <td><label for="login">Username:</label></td>
+	      <td><input id="login" type="text" name="login" /></td>
+	    </tr>
+	    <tr>
+	      <td><label for="password">Password:</label></td>
+	      <td><input id="password" type="password" name="password" /></td>
+	    </tr>
+	    <tr>
+	      <td></td>
+	      <td><input type="submit" value="Login"/></td>
+	    </tr>
+	  </table>
+
+	</fieldset>
+      </form>
+    </div>
+  </body>
+</html>
\ No newline at end of file

Modified: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt	2007-07-24 11:24:41 UTC (rev 78306)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt	2007-07-24 12:22:48 UTC (rev 78307)
@@ -28,7 +28,9 @@
         <img alt="grok_relax_image" src="images/grok-relax5.gif"
           tal:attributes="src view/static/grok-relax5.gif" />
       </div>
-      <div id="menu-links" tal:define="currview python:view.url()">
+      <div id="menu-links" 
+	   metal:define-slot="menu-links"
+	   tal:define="currview python:view.url()">
         <span class="menu-link-inactive"
           tal:define="target string:${view/root_url}/applications">
           <a href="applications"

Modified: Sandbox/ulif/grok-adminui/src/grok/ftests/admin/apps.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/ftests/admin/apps.py	2007-07-24 11:24:41 UTC (rev 78306)
+++ Sandbox/ulif/grok-adminui/src/grok/ftests/admin/apps.py	2007-07-24 12:22:48 UTC (rev 78307)
@@ -1,15 +1,64 @@
 """
-  
-We fetch the standard page, which should provide us a menu to get all
-installable grok applications/components.
 
   >>> import grok
   >>> grok.grok('grok.ftests.admin.apps')
 
+First setup the pluggable authentication system for session based
+authentication. This is normaly invoked by an event
+handler. Unfortunately the event handler seems not to be called, if
+the ftesting setup is set up. We therefore set up the PAU manually.
+
+  >>> root = getRootFolder()
+  >>> root is not None
+  True
+
+  >>> import grok.admin
+  >>> principal_credentials = grok.admin.getPrincipalCredentialsFromZCML()
+  >>> principal_credentials
+  [{u'login': u'mgr', u'password': u'mgrpw', u'id': u'zope.mgr', u'title': u'Manager'}]
+
+  >>> grok.admin.setupSessionAuthentication(root_folder = root, principal_credentials = principal_credentials)
+
+We should get a login page if trying to get something unauthenticated.
+
   >>> from zope.testbrowser.testing import Browser
   >>> browser = Browser()
+  >>> browser.handleErrors = True
+  >>> browser.open("http://localhost/")
+
+  >>> print browser.contents
+  <html xmlns="http://www.w3.org/1999/xhtml">
+  ... <title>Grok Login</title>
+  ...
+
+Now try to log in using *wrong* credentials
+
+  >>> browser.getControl(name='login').value = 'dumbtry'
+  >>> browser.getControl('Login').click()
+  >>> print browser.contents
+  <html xmlns="http://www.w3.org/1999/xhtml">
+  ... <title>Grok Login</title>
+  ...
+
+Okay, we got the login screen again. What about the correct credentials?
+
+  >>> browser.getControl(name='login').value = 'mgr'
+  >>> browser.getControl(name='password').value = 'mgrpw'
+  >>> browser.getControl('Login').click()
+  >>> print browser.contents
+  <html xmlns="http://www.w3.org/1999/xhtml">
+  ... <title>grok administration interface</title>
+  ...
+
+Fine. Now we are authorized and can do, whatever we want. To stay
+authenticated, we set a header here.
+  
   >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
-  >>> browser.handleErrors = False
+
+  
+We fetch the standard page, which should provide us a menu to get all
+installable grok applications/components.
+
   >>> browser.open("http://localhost/")
   >>> print browser.contents
   <html xmlns="http://www.w3.org/1999/xhtml">



More information about the Checkins mailing list