[Zope-CVS] SVN: ldappas/trunk/ Implemented search interfaces, and added a title attribute to the plugin.

Florent Guillaume fg at nuxeo.com
Wed Oct 13 16:49:59 EDT 2004


Log message for revision 28124:
  Implemented search interfaces, and added a title attribute to the plugin.
  It is now possible give grant to LDAP principals and authenticate using them.
  

Changed:
  U   ldappas/trunk/authentication.py
  U   ldappas/trunk/configure.zcml
  U   ldappas/trunk/interfaces.py

-=-
Modified: ldappas/trunk/authentication.py
===================================================================
--- ldappas/trunk/authentication.py	2004-10-13 20:43:46 UTC (rev 28123)
+++ ldappas/trunk/authentication.py	2004-10-13 20:49:58 UTC (rev 28124)
@@ -22,7 +22,9 @@
 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 ldapadapter.interfaces import ILDAPAdapter
 from interfaces import ILDAPAuthentication
 
@@ -40,7 +42,7 @@
     information, and additional authentication-specific informations.
     """
 
-    implements(IAuthenticationPlugin, ILDAPAuthentication)
+    implements(ILDAPAuthentication, IAuthenticationPlugin, IQuerySchemaSearch)
 
     adapterName = ''
     searchBase = ''
@@ -48,6 +50,7 @@
     loginAttribute = ''
     principalIdPrefix = ''
     idAttribute = ''
+    titleAttribute = ''
 
     def __init__(self):
         pass
@@ -73,6 +76,7 @@
         >>> auth.loginAttribute = 'cn'
         >>> auth.principalIdPrefix = ''
         >>> auth.idAttribute = 'uid'
+        >>> auth.titleAttribute = 'sn'
         >>> da = auth.getLDAPAdapter()
         >>> authCreds = auth.authenticateCredentials
 
@@ -105,22 +109,23 @@
 
         Authentication succeeds if you provide the correct password.
 
-        >>> authCreds({'login': 'ok', 'password': '42pw'})
-        (u'42', {'login': 'ok'})
+        >>> 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'})
-        (u'ldap.ok', {'login': 'ok'})
+        >>> 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'})
-        (u'ldap.uid=42,dc=test', {'login': 'ok'})
+        >>> authCreds({'login': 'ok', 'password': '42pw'})[0]
+        u'ldap.uid=42,dc=test'
 
         If the id attribute returns several values, the first one is
         used.
@@ -128,8 +133,8 @@
         >>> auth.idAttribute = 'mult'
         >>> conn.search('dc=test', 'sub', '(cn=ok)')[0][1]['mult']
         [u'm1', u'm2']
-        >>> authCreds({'login': 'ok', 'password': '42pw'})
-        (u'ldap.m1', {'login': 'ok'})
+        >>> authCreds({'login': 'ok', 'password': '42pw'})[0]
+        u'ldap.m1'
 
         Authentication fails if the id attribute is not present:
 
@@ -196,7 +201,158 @@
         # Check authentication.
         try:
             conn = da.connect(dn, password)
-        except InvalidCredentials:
+        except (ServerDown, InvalidCredentials):
             return None
 
-        return id, {'login': login}
+        return id, self.getInfoFromEntry(dn, entry)
+
+    def getInfoFromEntry(self, dn, entry):
+        try:
+            title = entry[self.titleAttribute][0]
+        except (KeyError, IndexError):
+            title = dn
+        return {'login': entry[self.loginAttribute][0],
+                'title': title,
+                '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']
+        """
+        da = self.getLDAPAdapter()
+        if da is None:
+            return ()
+        try:
+            conn = da.connect()
+        except ServerDown:
+            return ()
+
+        # Build the filter based on the query
+        filter_elems = []
+        for key, value in query.items():
+            if not value:
+                continue
+            filter_elems.append(filter_format('(%s=*%s*)',
+                                              (key, value)))
+        filter = ''.join(filter_elems)
+        if len(filter_elems) > 1:
+            filter = '(&%s)' % filter
+
+        if not filter:
+            filter = '(objectClass=*)'
+
+        try:
+            res = conn.search(self.searchBase, self.searchScope, filter=filter,
+                              attrs=[self.idAttribute])
+        except NoSuchObject:
+            return ()
+
+        prefix = self.principalIdPrefix
+        infos = []
+        for dn, entry in res:
+            try:
+                infos.append(prefix+entry[self.idAttribute][0])
+            except (KeyError, IndexError):
+                pass
+
+        if start is None:
+            start = 0
+        if batch_size is not None:
+            return infos[start:start+batch_size]
+        else:
+            return infos[start:]

Modified: ldappas/trunk/configure.zcml
===================================================================
--- ldappas/trunk/configure.zcml	2004-10-13 20:43:46 UTC (rev 28123)
+++ ldappas/trunk/configure.zcml	2004-10-13 20:49:58 UTC (rev 28124)
@@ -10,6 +10,10 @@
         interface="zope.app.annotation.interfaces.IAttributeAnnotatable"
         />
 
+    <implements
+        interface="zope.app.pas.interfaces.ISearchableAuthenticationPlugin"
+        />
+
     <require
         permission="zope.ManageServices"
         interface="zope.app.pas.interfaces.IAuthenticationPlugin"

Modified: ldappas/trunk/interfaces.py
===================================================================
--- ldappas/trunk/interfaces.py	2004-10-13 20:43:46 UTC (rev 28123)
+++ ldappas/trunk/interfaces.py	2004-10-13 20:49:58 UTC (rev 28124)
@@ -56,9 +56,18 @@
         )
     idAttribute = TextLine(
         title=_("Id attribute"),
-        description=_(u"The LDAP attribute used to determine principal ids."),
+        description=_(
+            u"The LDAP attribute used to determine a principal's id."),
         constraint=re.compile("[a-zA-Z][-a-zA-Z0-9]*$").match,
         default=u'uid',
         required=True,
         )
+    titleAttribute = TextLine(
+        title=_("Title attribute"),
+        description=_(
+            u"The LDAP attribute used to determine a principal's title."),
+        constraint=re.compile("[a-zA-Z][-a-zA-Z0-9]*$").match,
+        default=u'cn',
+        required=True,
+        )
     #searchObjectClasses



More information about the Zope-CVS mailing list