SVN: ldappas/trunk/ Updated and cleaned the pacakge to work with the latest PAU. Moved all

Stephan Richter <[email protected]>
Newsgroups gmane.comp.web.zope.public-cvs
Message-ID <[email protected]>
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')

_______________________________________________
Zope-CVS maillist  -  [email protected]
http://mail.zope.org/mailman/listinfo/zope-cvs

Zope CVS instructions: http://dev.zope.org/CVS
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.