[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