[Zope-CVS] SVN: ldappas/trunk/ Updated and cleaned the pacakge to work with the latest PAU. Moved all

Stephan Richter srichter at cosmos.phy.tufts.edu
Thu May 19 15:52:14 EDT 2005


Log message for revision 30424:
  Updated and cleaned the pacakge to work with the latest PAU. Moved all 
  tests to README.txt and applied some more modern coding styles. Thanks a 
  lot to Florent for the great FakeLDAPAdapter code that made it very easy 
  to test the code! Oh yeah, I added an integration test with PAU in the 
  README.txt fiel as well.
  
  

Changed:
  U   ldappas/trunk/README.txt
  U   ldappas/trunk/__init__.py
  U   ldappas/trunk/authentication.py
  U   ldappas/trunk/configure.zcml
  U   ldappas/trunk/interfaces.py
  D   ldappas/trunk/tests/
  A   ldappas/trunk/tests.py

-=-
Modified: ldappas/trunk/README.txt
===================================================================
--- ldappas/trunk/README.txt	2005-05-19 19:45:54 UTC (rev 30423)
+++ ldappas/trunk/README.txt	2005-05-19 19:52:14 UTC (rev 30424)
@@ -1,23 +1,214 @@
-LDAP PAS Plugin
-===============
+==========================
+LDAP Authentication Plugin
+==========================
 
-This is a plugins for PAS to deal with LDAP. It depends on the
-`ldapadapter` module (which itself depends on `python-ldap`).
+This is a plugin for the pluggable authentication utility (located at
+``zope.app.authentication``) to deal with LDAP principal sources. It depends
+on the ``ldapadapter`` module (which itself depends on `python-ldap`).
 
 Authentication and Search
 -------------------------
 
-This plugin allows one to authenticate and make searches. It is
-configured with:
+This plugin allows one to authenticate and make searches. It is configured
+with:
 
-- LDAP adapter to use.
+- A LDAP adapter to use.
 
 - The search base and scope.
 
 - The attributes for principal login, id and title (which means you can
   have a login different than the id used by zope to represent the
-  principal), for instance the login attribute can be 'cn' while the id
-  attribute can be 'uid'.
+  principal), for instance the login attribute can be `cn` while the id
+  attribute can be `uid`.
 
 - The prefix to add to the principal id (so that each authentication
   source can have a different namespace for its ids).
+
+The authentication utility is located in
+
+  >>> import ldappas.authentication
+
+As mentioned before, we need a LDAP Adatper. To avoid needing to connect to a
+real LDAP server for these tests, a fake LDAP adapter was developed and has
+already been registered under the name `fake_ldap_adapter`.
+
+Now that the LDAP adapter is regsitered, we can instanticate the LDAP
+authentication:
+
+  >>> auth = ldappas.authentication.LDAPAuthentication()
+  >>> auth.adapterName = 'fake_ldap_adapter'
+  >>> auth.searchBase = 'dc=test'
+  >>> auth.searchScope = 'sub'
+  >>> auth.loginAttribute = 'cn'
+  >>> auth.principalIdPrefix = ''
+  >>> auth.idAttribute = 'uid'
+  >>> auth.titleAttribute = 'sn'
+  >>> da = auth.getLDAPAdapter()
+
+The first task is to authenticate a set of credentials. Incorrect credentials
+types are rejected.
+
+  >>> auth.authenticateCredentials(123) is None
+  True
+  >>> auth.authenticateCredentials({'glop': 'bzz'}) is None
+  True
+
+You cannot authenticate if the search returns several results.
+
+  >>> len(da.connect().search('dc=test', 'sub', '(cn=many)')) > 1
+  True
+  >>> auth.authenticateCredentials({'login': 'many', 'password': 'p'}) is None
+  True
+
+You cannot authenticate if the search returns nothing.
+
+  >>> conn = da.connect()
+  >>> len(conn.search('dc=test', 'sub', '(cn=none)')) == 0
+  True
+  >>> auth.authenticateCredentials({'login': 'none', 'password': 'p'}) is None
+  True
+
+You cannot authenticate with the wrong password.
+
+  >>> auth.authenticateCredentials({'login': 'ok', 'password': 'hm'}) is None
+  True
+
+Authentication succeeds if you provide the correct password.
+
+  >>> principal = auth.authenticateCredentials({'login': 'ok', 
+  ...                                          'password': '42pw'})
+  >>> principal, principal.login, principal.title, principal.description
+  (PrincipalInfo(u'42'), u'ok', u'the question', u'the question')
+
+The id returned comes from a configurable attribute, and can be prefixed so
+that it is unique.
+
+  >>> auth.principalIdPrefix = 'ldap.'
+  >>> auth.idAttribute = 'cn'
+  >>> auth.authenticateCredentials({'login': 'ok', 'password': '42pw'})
+  PrincipalInfo(u'ldap.ok')
+
+The id attribute `dn` can be specified to use the full dn as id.
+
+  >>> auth.idAttribute = 'dn'
+  >>> auth.authenticateCredentials({'login': 'ok', 'password': '42pw'})
+  PrincipalInfo(u'ldap.uid=42,dc=test')
+
+If the id attribute returns several values, the first one is used.
+
+  >>> auth.idAttribute = 'mult'
+  >>> conn.search('dc=test', 'sub', '(cn=ok)')[0][1]['mult']
+  [u'm1', u'm2']
+  >>> auth.authenticateCredentials({'login': 'ok', 'password': '42pw'})
+  PrincipalInfo(u'ldap.m1')
+
+Authentication fails if the id attribute is not present:
+
+  >>> auth.idAttribute = 'nonesuch'
+  >>> conn.search('dc=test', 'sub', '(cn=ok)')[0][1]['nonesuch']
+  Traceback (most recent call last):
+  ...
+  KeyError: 'nonesuch'
+  >>> auth.authenticateCredentials({'login': 'ok', 'password': '42pw'}) is None
+  True
+
+You cannot authenticate if the server to which the adapter connects is down.
+
+  >>> da._isDown = True
+  >>> auth.authenticateCredentials({'login': 'ok', 'password': '42pw'}) is None
+  True
+  >>> da._isDown = False
+
+You cannot authenticate if the plugin has a bad configuration.
+
+  >>> auth.searchBase = 'dc=bzzt'
+  >>> auth.authenticateCredentials({'login': 'ok', 'password': '42pw'}) is None
+  True
+
+In user interfaces, you commonly want to search through the available
+principals for managment purposes. The authentication plugin provides an API
+for searching through the principals. An empty search returns everything.
+
+  >>> auth.idAttribute = 'uid'
+  >>> auth.searchBase = 'dc=test'
+  >>> auth.search({})
+  [u'ldap.1', u'ldap.2', u'ldap.42']
+
+A search for a specific entry returns it.
+
+  >>> auth.search({'cn': 'many'})
+  [u'ldap.1', u'ldap.2']
+
+You can have multiple search criteria, they are ANDed.
+
+  >>> auth.search({'cn': 'many', 'sn': 'mr2'})
+  [u'ldap.2']
+
+Batching can be used to restrict the result range.
+
+  >>> auth.search({}, start=1)
+  [u'ldap.2', u'ldap.42']
+  >>> auth.search({}, start=1, batch_size=1)
+  [u'ldap.2']
+  >>> auth.search({}, batch_size=2)
+  [u'ldap.1', u'ldap.2']
+
+
+Integration with the Pluggable Authenitcation Utility
+-----------------------------------------------------
+
+Now that we have see how the LDAP authenication plugin behaves autonomously,
+let's have a brief look on how the plugin behaves inside the authentication
+utility. The first step is to register the LDAP authentication plugin as an
+authenticator utility:
+
+  >>> from zope.component import provideUtility
+  >>> from zope.app.authentication.interfaces import IAuthenticatorPlugin
+  >>> provideUtility(auth, provides=IAuthenticatorPlugin,
+  ...                name='ldap-authenticator')
+
+We also need a credentials plugin that will extract the credentials from the
+request:
+
+  >>> import zope.interface
+  >>> from zope.app.authentication.interfaces import ICredentialsPlugin
+
+  >>> class MyCredentialsPlugin:
+  ...
+  ...     zope.interface.implements(ICredentialsPlugin)
+  ...
+  ...     def extractCredentials(self, request):
+  ...         return request.get('credentials')
+  ...
+  ...     def challenge(self, request):
+  ...         pass # challenge is a no-op for this plugin
+  ...
+  ...     def logout(request):
+  ...         pass # logout is a no-op for this plugin
+
+  >>> provideUtility(MyCredentialsPlugin(), name='simple-creds')
+
+Now, as we have seen the authenticator is only responsible for providing
+principal information, but not to generate the principals itself. Thus we have
+to register an adapter that can create principals from principal infos:
+
+  >>> from zope.component import provideAdapter
+  >>> from zope.app.authentication import principalfolder
+  >>> provideAdapter(principalfolder.AuthenticatedPrincipalFactory)
+
+We are finally ready to create a pluggable authentication utility and register
+the two plugins with it:
+
+  >>> from zope.app import authentication
+  >>> pau = authentication.PluggableAuthentication()
+  >>> pau.credentialsPlugins = ('simple-creds', )
+  >>> pau.authenticatorPlugins = ('ldap-authenticator', )
+
+And now we can just authenticate a user using LDAP:
+
+  >>> auth.idAttribute = 'cn'
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest(credentials={'login': 'ok', 'password': '42pw'})
+  >>> principal = pau.authenticate(request)
+  >>> principal
+  Principal(u'ldap.ok')

Modified: ldappas/trunk/__init__.py
===================================================================
--- ldappas/trunk/__init__.py	2005-05-19 19:45:54 UTC (rev 30423)
+++ ldappas/trunk/__init__.py	2005-05-19 19:52:14 UTC (rev 30424)
@@ -1,17 +1 @@
-##############################################################################
-#
-# Copyright (c) 2004 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
-#
-##############################################################################
-"""LDAP PAS Plugins packages.
-
-$Id$
-"""
+# Make a package.

Modified: ldappas/trunk/authentication.py
===================================================================
--- ldappas/trunk/authentication.py	2005-05-19 19:45:54 UTC (rev 30423)
+++ ldappas/trunk/authentication.py	2005-05-19 19:52:14 UTC (rev 30424)
@@ -15,25 +15,56 @@
 
 $Id$
 """
+import zope.schema
+import zope.interface
+from persistent import Persistent
 
 from zope.app import zapi
-from persistent import Persistent
+from zope.app import authentication
 from zope.app.container.contained import Contained
-from zope.interface import implements
 
-from zope.interface import Interface
-from zope.schema import TextLine
-from zope.app.pas.interfaces import IAuthenticationPlugin
-from zope.app.pas.interfaces import IQuerySchemaSearch
+from ldap.filter import filter_format
+from ldapadapter.interfaces import ServerDown
+from ldapadapter.interfaces import InvalidCredentials
+from ldapadapter.interfaces import NoSuchObject
 from ldapadapter.interfaces import ILDAPAdapter
-from interfaces import ILDAPAuthentication
+from ldappas.interfaces import ILDAPAuthentication
 
-from ldap.filter import filter_format
-from ldapadapter.exceptions import ServerDown
-from ldapadapter.exceptions import InvalidCredentials
-from ldapadapter.exceptions import NoSuchObject
 
+class ILDAPSearchSchema(zope.interface.Interface):
+    """A LDAP-specific schema for searching for principals."""
 
+    uid = zope.schema.TextLine(
+        title=u'uid',
+        required=False)
+
+    cn = zope.schema.TextLine(
+        title=u'cn',
+        required=False)
+
+    givenName = zope.schema.TextLine(
+        title=u'givenName',
+        required=False)
+
+    sn = zope.schema.TextLine(
+        title=u'sn',
+        required=False)
+
+
+class PrincipalInfo:
+    """An implementation of IPrincipalInfo used by the principal folder."""
+    zope.interface.implements(authentication.interfaces.IPrincipalInfo)
+
+    def __init__(self, id, login='', title='', description=''):
+        self.id = id
+        self.login = login
+        self.title = title
+        self.description = description
+
+    def __repr__(self):
+        return 'PrincipalInfo(%r)' % self.id
+
+
 class LDAPAuthentication(Persistent, Contained):
     """A Persistent LDAP Authentication plugin for PAS.
 
@@ -42,7 +73,11 @@
     information, and additional authentication-specific informations.
     """
 
-    implements(ILDAPAuthentication, IAuthenticationPlugin, IQuerySchemaSearch)
+    zope.interface.implements(
+        ILDAPAuthentication,
+        authentication.interfaces.IAuthenticatorPlugin,
+        authentication.interfaces.IQueriableAuthenticator,
+        authentication.interfaces.IQuerySchemaSearch)
 
     adapterName = ''
     searchBase = ''
@@ -52,8 +87,7 @@
     idAttribute = ''
     titleAttribute = ''
 
-    def __init__(self):
-        pass
+    schema = ILDAPSearchSchema
 
     def getLDAPAdapter(self):
         """Get the LDAP adapter according to our configuration.
@@ -64,103 +98,8 @@
         return da
 
     def authenticateCredentials(self, credentials):
-        r"""See zope.app.pas.interfaces.IAuthenticationPlugin.
+        """See zope.app.authentication.interfaces.IAuthenticationPlugin."""
 
-        An LDAP Adapter has to be registered, we'll use a fake one
-        (registered by the test framework).
-
-        >>> auth = LDAPAuthentication()
-        >>> auth.adapterName = 'fake_ldap_adapter'
-        >>> auth.searchBase = 'dc=test'
-        >>> auth.searchScope = 'sub'
-        >>> auth.loginAttribute = 'cn'
-        >>> auth.principalIdPrefix = ''
-        >>> auth.idAttribute = 'uid'
-        >>> auth.titleAttribute = 'sn'
-        >>> da = auth.getLDAPAdapter()
-        >>> authCreds = auth.authenticateCredentials
-
-        Incorrect credentials types are rejected.
-
-        >>> authCreds(123) is None
-        True
-        >>> authCreds({'glop': 'bzz'}) is None
-        True
-
-        You cannot authenticate if the search returns several results.
-
-        >>> len(da.connect().search('dc=test', 'sub', '(cn=many)')) > 1
-        True
-        >>> authCreds({'login': 'many', 'password': 'p'}) is None
-        True
-
-        You cannot authenticate if the search returns nothing.
-
-        >>> conn = da.connect()
-        >>> len(conn.search('dc=test', 'sub', '(cn=none)')) == 0
-        True
-        >>> authCreds({'login': 'none', 'password': 'p'}) is None
-        True
-
-        You cannot authenticate with the wrong password.
-
-        >>> authCreds({'login': 'ok', 'password': 'hm'}) is None
-        True
-
-        Authentication succeeds if you provide the correct password.
-
-        >>> id, info = authCreds({'login': 'ok', 'password': '42pw'})
-        >>> id, info['login'], info['title'], info['description']
-        (u'42', u'ok', u'the question', u'the question')
-
-        The id returned comes from a configurable attribute, and can be
-        prefixed so that it is unique.
-
-        >>> auth.principalIdPrefix = 'ldap.'
-        >>> auth.idAttribute = 'cn'
-        >>> authCreds({'login': 'ok', 'password': '42pw'})[0]
-        u'ldap.ok'
-
-        The id attribute 'dn' can be specified to use the full dn as id.
-
-        >>> auth.idAttribute = 'dn'
-        >>> authCreds({'login': 'ok', 'password': '42pw'})[0]
-        u'ldap.uid=42,dc=test'
-
-        If the id attribute returns several values, the first one is
-        used.
-
-        >>> auth.idAttribute = 'mult'
-        >>> conn.search('dc=test', 'sub', '(cn=ok)')[0][1]['mult']
-        [u'm1', u'm2']
-        >>> authCreds({'login': 'ok', 'password': '42pw'})[0]
-        u'ldap.m1'
-
-        Authentication fails if the id attribute is not present:
-
-        >>> auth.idAttribute = 'nonesuch'
-        >>> conn.search('dc=test', 'sub', '(cn=ok)')[0][1]['nonesuch']
-        Traceback (most recent call last):
-        ...
-        KeyError: 'nonesuch'
-        >>> authCreds({'login': 'ok', 'password': '42pw'}) is None
-        True
-
-        You cannot authenticate if the server to which the adapter
-        connects is down.
-
-        >>> da._isDown = True
-        >>> authCreds({'login': 'ok', 'password': '42pw'}) is None
-        True
-        >>> da._isDown = False
-
-        You cannot authenticate if the plugin has a bad configuration.
-
-        >>> auth.searchBase = 'dc=bzzt'
-        >>> authCreds({'login': 'ok', 'password': '42pw'}) is None
-        True
-        """
-
         if not isinstance(credentials, dict):
             return None
         if not ('login' in credentials and 'password' in credentials):
@@ -204,7 +143,7 @@
         except (ServerDown, InvalidCredentials):
             return None
 
-        return id, self.getInfoFromEntry(dn, entry)
+        return PrincipalInfo(id, **self.getInfoFromEntry(dn, entry))
 
     def getInfoFromEntry(self, dn, entry):
         try:
@@ -216,104 +155,8 @@
                 'description': title,
                 }
 
-    def get(self, id):
-        """See zope.app.pas.interfaces.IPrincipalSearchPlugin.
-
-        >>> auth = LDAPAuthentication()
-        >>> auth.adapterName = 'fake_ldap_adapter'
-        >>> auth.loginAttribute = 'cn'
-        >>> auth.principalIdPrefix = 'ldap.'
-        >>> auth.idAttribute = 'uid'
-        >>> auth.titleAttribute = 'sn'
-
-        If the id is not in this plugin, return nothing.
-
-        >>> auth.get('42') is None
-        True
-
-        Otherwise return the info if we have it.
-
-        >>> auth.get('ldap.123') is None
-        True
-        >>> info = auth.get('ldap.42')
-        >>> info['login'], info['title'], info['description']
-        (u'ok', u'the question', u'the question')
-        """
-        if not id.startswith(self.principalIdPrefix):
-            return None
-        id = id[len(self.principalIdPrefix):]
-
-        da = self.getLDAPAdapter()
-        if da is None:
-            return None
-        try:
-            conn = da.connect()
-        except ServerDown:
-            return None
-
-        filter = filter_format('(%s=%s)', (self.idAttribute, id))
-        try:
-            res = conn.search(self.searchBase, self.searchScope,
-                              filter=filter)
-        except NoSuchObject:
-            return None
-
-        if len(res) != 1:
-            # Search returned no result or too many.
-            return None
-        dn, entry = res[0]
-
-        return self.getInfoFromEntry(dn, entry)
-
-    class schema(Interface):
-        """See of zope.app.pas.interfaces.IQuerySchemaSearch.
-        """
-        uid = TextLine(
-            title=u'uid',
-            required=False)
-        cn = TextLine(
-            title=u'cn',
-            required=False)
-        givenName = TextLine(
-            title=u'givenName',
-            required=False)
-        sn = TextLine(
-            title=u'sn',
-            required=False)
-
     def search(self, query, start=None, batch_size=None):
-        """See zope.app.pas.interfaces.IQuerySchemaSearch.
-
-        >>> auth = LDAPAuthentication()
-        >>> auth.adapterName = 'fake_ldap_adapter'
-        >>> auth.loginAttribute = 'cn'
-        >>> auth.principalIdPrefix = 'ldap.'
-        >>> auth.idAttribute = 'uid'
-
-        An empty search returns everything.
-
-        >>> auth.search({})
-        [u'ldap.1', u'ldap.2', u'ldap.42']
-
-        A search for a specific entry returns it.
-
-        >>> auth.search({'cn': 'many'})
-        [u'ldap.1', u'ldap.2']
-
-        You can have multiple search criteria, they are ANDed.
-
-        >>> auth.search({'cn': 'many', 'sn': 'mr2'})
-        [u'ldap.2']
-
-        Batching can be used to restrict the result range.
-
-        >>> auth.search({}, start=1)
-        [u'ldap.2', u'ldap.42']
-        >>> auth.search({}, start=1, batch_size=1)
-        [u'ldap.2']
-        >>> auth.search({}, batch_size=2)
-        [u'ldap.1', u'ldap.2']
-        """
+        """See zope.app.authentication.interfaces.IQuerySchemaSearch."""
         da = self.getLDAPAdapter()
         if da is None:
             return ()

Modified: ldappas/trunk/configure.zcml
===================================================================
--- ldappas/trunk/configure.zcml	2005-05-19 19:45:54 UTC (rev 30423)
+++ ldappas/trunk/configure.zcml	2005-05-19 19:52:14 UTC (rev 30424)
@@ -9,46 +9,51 @@
     <implements
         interface="zope.app.annotation.interfaces.IAttributeAnnotatable"
         />
-
-    <implements
-        interface="zope.app.pas.interfaces.ISearchableAuthenticationPlugin"
-        />
-
     <require
-        permission="zope.ManageServices"
-        interface="zope.app.pas.interfaces.IAuthenticationPlugin"
-        />
-
-    <require
-        permission="zope.ManageServices"
+        permission="zope.ManageSite"
         interface="ldappas.interfaces.ILDAPAuthentication"
         set_schema="ldappas.interfaces.ILDAPAuthentication"
         />
 
   </localUtility>
 
-  <browser:addMenuItem
-      title="PAS LDAP Authentication Plugin"
-      description="A PAS LDAP Authentication Plugin"
-      class=".authentication.LDAPAuthentication"
-      permission="zope.ManageServices"
-      view="addPASLDAPAuthenticationPlugin"
-      />
-
   <browser:addform
       schema="ldappas.interfaces.ILDAPAuthentication"
-      label="Add a PAS LDAP Authentication Plugin"
+      label="Add LDAP Authentication Plugin"
       content_factory=".authentication.LDAPAuthentication"
-      name="addPASLDAPAuthenticationPlugin"
-      permission="zope.ManageServices"
+      name="addLDAPAuthentication.html"
+      permission="zope.ManageSite"
       />
 
+  <browser:addMenuItem
+      title="LDAP Authentication"
+      description="A LDAP Authentication Plugin"
+      class=".authentication.LDAPAuthentication"
+      permission="zope.ManageSite"
+      view="addLDAPAuthentication.html"
+      />
+
   <browser:editform
       schema="ldappas.interfaces.ILDAPAuthentication"
       name="edit.html"
+      title="Edit"
+      permission="zope.ManageSite"
       menu="zmi_views"
-      title="Edit"
-      permission="zope.ManageServices"
       />
 
+  <!-- Registering documentation with API doc -->
+  <configure
+      xmlns:apidoc="http://namespaces.zope.org/apidoc"
+      xmlns:zcml="http://namespaces.zope.org/zcml"
+      zcml:condition="have apidoc">
+
+    <apidoc:bookchapter
+        id="ldapauth"
+        title="LDAP Authentication"
+        doc_path="README.txt"
+        parent="security/authentication"
+        />
+
+  </configure>
+
 </configure>

Modified: ldappas/trunk/interfaces.py
===================================================================
--- ldappas/trunk/interfaces.py	2005-05-19 19:45:54 UTC (rev 30423)
+++ ldappas/trunk/interfaces.py	2005-05-19 19:52:14 UTC (rev 30424)
@@ -11,50 +11,57 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""LDAP PAS Plugin interfaces
+"""LDAP Pluggable Authentication Plugin interfaces
 
 $Id$
 """
+import re
+import zope.interface
+import zope.schema
 
-import re
 from zope.app.i18n import ZopeMessageIDFactory as _
-from zope.interface import Interface
-from zope.schema import Choice
-from zope.schema import TextLine
 
-class ILDAPAuthentication(Interface):
-    adapterName = Choice(
+
+class ILDAPAuthentication(zope.interface.Interface):
+    """LDAP-specifc Autentication Plugin for the Pluggable Authentication."""
+
+    adapterName = zope.schema.Choice(
         title=_(u"LDAP Adapter Name"),
         description=_(u"The LDAP adapter name for the connection to be used."),
         vocabulary="LDAP Adapter Names",
         required=True,
         )
-    searchBase = TextLine(
+
+    searchBase = zope.schema.TextLine(
         title=_("Search base"),
         description=_(u"The LDAP search base where principals are found."),
         default=u'dc=example,dc=org',
         required=True,
         )
-    searchScope = TextLine(
+
+    searchScope = zope.schema.TextLine(
         title=_("Search scope"),
         description=_(u"The LDAP search scope used to find principals."),
         default=u'sub',
         required=True,
         )
-    loginAttribute = TextLine(
+
+    loginAttribute = zope.schema.TextLine(
         title=_("Login attribute"),
         description=_(u"The LDAP attribute used to find principals."),
         constraint=re.compile("[a-zA-Z][-a-zA-Z0-9]*$").match,
         default=u'uid',
         required=True,
         )
-    principalIdPrefix = TextLine(
+
+    principalIdPrefix = zope.schema.TextLine(
         title=_("Principal id prefix"),
         description=_(u"The prefix to add to all principal ids."),
         default=u'ldap.',
         required=False,
         )
-    idAttribute = TextLine(
+
+    idAttribute = zope.schema.TextLine(
         title=_("Id attribute"),
         description=_(
             u"The LDAP attribute used to determine a principal's id."),
@@ -62,7 +69,8 @@
         default=u'uid',
         required=True,
         )
-    titleAttribute = TextLine(
+
+    titleAttribute = zope.schema.TextLine(
         title=_("Title attribute"),
         description=_(
             u"The LDAP attribute used to determine a principal's title."),
@@ -70,4 +78,5 @@
         default=u'cn',
         required=True,
         )
-    #searchObjectClasses
+
+    # searchObjectClasses

Copied: ldappas/trunk/tests.py (from rev 30417, ldappas/trunk/tests/test_authentication.py)
===================================================================
--- ldappas/trunk/tests/test_authentication.py	2005-05-19 11:16:24 UTC (rev 30417)
+++ ldappas/trunk/tests.py	2005-05-19 19:52:14 UTC (rev 30424)
@@ -0,0 +1,90 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+"""LDAP PAS Plugins tests
+
+$Id$
+"""
+import unittest
+
+import zope.interface
+from zope.testing import doctest, doctestunit
+from zope.app import zapi
+from zope.app.testing import setup
+
+import ldapadapter.interfaces
+
+class FakeLDAPAdapter:
+    zope.interface.implements(ldapadapter.interfaces.ILDAPAdapter)
+    _isDown = False
+    def connect(self, dn=None, password=None):
+        if self._isDown:
+            raise ldapadapter.interfaces.ServerDown
+        if not dn and not password:
+            return FakeLDAPConnection()
+        if dn == 'uid=42,dc=test' and password == '42pw':
+            return FakeLDAPConnection()
+        raise ldapadapter.interfaces.InvalidCredentials
+
+class FakeLDAPConnection:
+    def search(self, base, scope='sub', filter='(objectClass=*)', attrs=[]):
+        dn1 = u'uid=1,dc=test'
+        entry1 = {'cn': [u'many'],
+                  'uid': [u'1'],
+                  'sn': [u'mr1'],
+                  }
+        dn2 = u'uid=2,dc=test'
+        entry2 = {'cn': [u'many'],
+                  'uid': [u'2'],
+                  'sn': [u'mr2'],
+                  }
+        dn42 = u'uid=42,dc=test'
+        entry42 = {'cn': [u'ok'],
+                   'uid': [u'42'],
+                   'sn': [u'the question'],
+                   'mult': [u'm1', u'm2'],
+                   }
+        if base.endswith('dc=bzzt'):
+            raise ldapadapter.interfaces.NoSuchObject
+        if filter == '(cn=none)':
+            return []
+        if filter in ('(cn=many)', '(cn=*many*)'):
+            return [(dn1, entry1), (dn2, entry2)]
+        if filter == '(cn=ok)' or filter == '(uid=42)':
+            return [(dn42, entry42)]
+        if filter in ('(&(sn=*mr2*)(cn=*many*))', '(&(cn=*many*)(sn=*mr2*))'):
+            return [(dn2, entry2)]
+        if filter == '(objectClass=*)':
+            return [(dn1, entry1), (dn2, entry2), (dn42, entry42)]
+        return []
+
+
+def setUp(test):
+    root = setup.placefulSetUp(site=True)
+    sm = root.getSiteManager()
+    setup.addUtility(sm, 'fake_ldap_adapter',
+                     ldapadapter.interfaces.ILDAPAdapter, FakeLDAPAdapter())
+
+def tearDown(test):
+    setup.placefulTearDown()
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite('README.txt',
+                             setUp=setUp, tearDown=tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')



More information about the Zope-CVS mailing list