[gen, shared] More work on LDAP.
This commit is contained in:
parent
1be7d9f0ab
commit
79d89aca2b
|
@ -368,7 +368,12 @@ class User(Model):
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class LdapConfig:
|
class LdapConfig:
|
||||||
'''Parameters for authenticating users to an external LDAP.'''
|
'''Parameters for authenticating users to an LDAP server.'''
|
||||||
|
ldapAttributes = { 'loginAttribute':None, 'emailAttribute':'email',
|
||||||
|
'fullNameAttribute':'title',
|
||||||
|
'firstNameAttribute':'firstName',
|
||||||
|
'lastNameAttribute':'name' }
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.server = '' # Name of the LDAP server
|
self.server = '' # Name of the LDAP server
|
||||||
self.port = None # Port for this server.
|
self.port = None # Port for this server.
|
||||||
|
@ -378,7 +383,17 @@ class LdapConfig:
|
||||||
self.adminPassword = ''
|
self.adminPassword = ''
|
||||||
# LDAP attribute to use as login for authenticating users.
|
# LDAP attribute to use as login for authenticating users.
|
||||||
self.loginAttribute = 'dn' # Can also be "mail", "sAMAccountName", "cn"
|
self.loginAttribute = 'dn' # Can also be "mail", "sAMAccountName", "cn"
|
||||||
self.baseDn = '' # Base distinguished name where to find users.
|
# LDAP attributes for storing email
|
||||||
|
self.emailAttribute = None
|
||||||
|
# LDAP attribute for storing full name (first + last name)
|
||||||
|
self.fullNameAttribute = None
|
||||||
|
# Alternately, LDAP attributes for storing 1st & last names separately.
|
||||||
|
self.firstNameAttribute = None
|
||||||
|
self.lastNameAttribute = None
|
||||||
|
# LDAP classes defining the users stored in the LDAP.
|
||||||
|
self.userClasses = ('top', 'person')
|
||||||
|
self.baseDn = '' # Base DN where to find users in the LDAP.
|
||||||
|
self.scope = 'SUBTREE' # Scope of the search within self.baseDn
|
||||||
|
|
||||||
def getServerUri(self):
|
def getServerUri(self):
|
||||||
'''Returns the complete URI for accessing the LDAP, ie
|
'''Returns the complete URI for accessing the LDAP, ie
|
||||||
|
@ -386,6 +401,37 @@ class LdapConfig:
|
||||||
port = self.port or 389
|
port = self.port or 389
|
||||||
return 'ldap://%s:%d' % (self.server, port)
|
return 'ldap://%s:%d' % (self.server, port)
|
||||||
|
|
||||||
|
def getUserFilterValues(self, login):
|
||||||
|
'''Gets the filter values required to perform a query for finding user
|
||||||
|
corresponding to p_login in the LDAP.'''
|
||||||
|
res = [(self.loginAttribute, login)]
|
||||||
|
for userClass in self.userClasses:
|
||||||
|
res.append( ('objectClass', userClass) )
|
||||||
|
return res
|
||||||
|
|
||||||
|
def getUserAttributes(self):
|
||||||
|
'''Gets the attributes we want to get from the LDAP for characterizing
|
||||||
|
a user.'''
|
||||||
|
res = [self.loginAttribute]
|
||||||
|
for name in self.ldapAttributes.iterkeys():
|
||||||
|
if getattr(self, name):
|
||||||
|
res.append(getattr(self, name))
|
||||||
|
return res
|
||||||
|
|
||||||
|
def getUserParams(self, ldapData):
|
||||||
|
'''Formats the user-related p_ldapData retrieved from the ldap, as a
|
||||||
|
dict of params usable for creating or updating the corresponding
|
||||||
|
Appy user.'''
|
||||||
|
res = {}
|
||||||
|
for name, appyName in self.ldapAttributes.iteritems():
|
||||||
|
if not appyName: continue
|
||||||
|
# Get the name of the attribute as known in the LDAP.
|
||||||
|
ldapName = getattr(self, name)
|
||||||
|
if not ldapName: continue
|
||||||
|
if ldapData.has_key(ldapName) and ldapData[ldapName]:
|
||||||
|
res[appyName] = ldapData[ldapName]
|
||||||
|
return res
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class Config:
|
class Config:
|
||||||
'''If you want to specify some configuration parameters for appy.gen and
|
'''If you want to specify some configuration parameters for appy.gen and
|
||||||
|
@ -397,7 +443,6 @@ class Config:
|
||||||
class Config(appy.gen.Config):
|
class Config(appy.gen.Config):
|
||||||
langages = ('en', 'fr')
|
langages = ('en', 'fr')
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# For every language code that you specify in this list, appy.gen will
|
# For every language code that you specify in this list, appy.gen will
|
||||||
# produce and maintain translation files.
|
# produce and maintain translation files.
|
||||||
languages = ['en']
|
languages = ['en']
|
||||||
|
|
34
gen/ldap.py
34
gen/ldap.py
|
@ -1,34 +0,0 @@
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
try:
|
|
||||||
import ldap
|
|
||||||
except ImportError:
|
|
||||||
# For people that do not care about ldap.
|
|
||||||
ldap = None
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
def connect(serverUri, login, password):
|
|
||||||
'''Tries to connect to some LDAP server whose UIR is p_serverUri, using
|
|
||||||
p_login and p_password as credentials.'''
|
|
||||||
try:
|
|
||||||
server = ldap.initialize(serverUri)
|
|
||||||
server.simple_bind(login, password)
|
|
||||||
return True, server, None
|
|
||||||
except ldap.LDAPError, le:
|
|
||||||
return False, None, str(le)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
def authenticate(login, password, ldapConfig, tool):
|
|
||||||
'''Tries to authenticate user p_login in the LDAP.'''
|
|
||||||
# Connect to the ldap server.
|
|
||||||
serverUri = cfg.getServerUri()
|
|
||||||
success, server, msg = connect(serverUri, cfg.adminLogin, cfg.adminPassword)
|
|
||||||
# Manage a connection error.
|
|
||||||
if not success:
|
|
||||||
tool.log('%s: connect error (%s).' % (serverUri, msg))
|
|
||||||
return
|
|
||||||
# Do p_login and p_password correspond to a user in the LDAP?
|
|
||||||
try:
|
|
||||||
pass
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
# ------------------------------------------------------------------------------
|
|
|
@ -1,8 +1,8 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import os, os.path, sys, re, time, random, types
|
import os, os.path, sys, re, time, random, types, base64
|
||||||
from appy import Object
|
from appy import Object
|
||||||
import appy.gen
|
import appy.gen
|
||||||
from appy.gen import Search, UiSearch, String, Page, ldap
|
from appy.gen import Search, UiSearch, String, Page
|
||||||
from appy.gen.layout import ColumnLayout
|
from appy.gen.layout import ColumnLayout
|
||||||
from appy.gen import utils as gutils
|
from appy.gen import utils as gutils
|
||||||
from appy.gen.mixins import BaseMixin
|
from appy.gen.mixins import BaseMixin
|
||||||
|
@ -12,6 +12,7 @@ from appy.gen.mail import sendMail
|
||||||
from appy.shared import mimeTypes
|
from appy.shared import mimeTypes
|
||||||
from appy.shared import utils as sutils
|
from appy.shared import utils as sutils
|
||||||
from appy.shared.data import languages
|
from appy.shared.data import languages
|
||||||
|
from appy.shared.ldap_connector import LdapConnector
|
||||||
try:
|
try:
|
||||||
from AccessControl.ZopeSecurityPolicy import _noroles
|
from AccessControl.ZopeSecurityPolicy import _noroles
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -979,21 +980,106 @@ class ToolMixin(BaseMixin):
|
||||||
'''Returns the encrypted version of clear p_password.'''
|
'''Returns the encrypted version of clear p_password.'''
|
||||||
return self.acl_users._encryptPassword(password)
|
return self.acl_users._encryptPassword(password)
|
||||||
|
|
||||||
def _zopeAuthenticate(self, request):
|
def getUser(self, authentify=False):
|
||||||
'''Performs the Zope-level authentication. Returns True if
|
'''Gets the current user. If p_authentify is True, in addition to
|
||||||
authentication succeeds.'''
|
finding the logged user and returning it (=identification), we check
|
||||||
user = self.acl_users.validate(request)
|
if found credentials are valid (=authentification).'''
|
||||||
return user.getUserName() != 'Anonymous User'
|
# I. Identify the user (=find its login and password). If identification
|
||||||
|
# fails, if we don't need to authentify the user (p_authentify is
|
||||||
|
# False), we consider that the current user is one of those special
|
||||||
|
# users: anon (corresponds to an anonymous user) or system (the
|
||||||
|
# technical user representing the system itself, running at startup or
|
||||||
|
# in batch mode).
|
||||||
|
tool = self.appy()
|
||||||
|
req = tool.request
|
||||||
|
# Try first to return the user that can be cached on the request. In
|
||||||
|
# this case, we suppose authentification has previously been done, and
|
||||||
|
# we just return the cached user.
|
||||||
|
if hasattr(req, 'user'): return req.user
|
||||||
|
login = password = None
|
||||||
|
isSpecial = False
|
||||||
|
# Ia. Identify the user from http basic authentication.
|
||||||
|
if getattr(req, '_auth', None):
|
||||||
|
# HTTP basic authentication credentials are present (used when
|
||||||
|
# connecting to the ZMI). Decode it.
|
||||||
|
creds = req._auth
|
||||||
|
if creds.lower().startswith('basic '):
|
||||||
|
try:
|
||||||
|
creds = creds.split(' ')[-1]
|
||||||
|
login, password = base64.decodestring(creds).split(':', 1)
|
||||||
|
except Exception, e:
|
||||||
|
pass
|
||||||
|
# Ib. Identify the user from the authentication cookie.
|
||||||
|
if not login:
|
||||||
|
login, password = gutils.readCookie(req)
|
||||||
|
# Ic. Identify the user from the authentication form.
|
||||||
|
if not login:
|
||||||
|
login = req.get('__ac_name', None)
|
||||||
|
password = req.get('__ac_password', None)
|
||||||
|
# Stop the identification process here if we needed to authentify the
|
||||||
|
# user: this user does not exist.
|
||||||
|
if not login and authentify: return
|
||||||
|
# Id. All the identification methods failed. So identify the user as
|
||||||
|
# "anon" or "system".
|
||||||
|
if not login and not authentify:
|
||||||
|
# If we have a real request object, it is the anonymous user.
|
||||||
|
login = (req.__class__.__name__ == 'Object') and 'system' or 'anon'
|
||||||
|
isSpecial = True
|
||||||
|
# Now, get the User instance from a query in the catalog.
|
||||||
|
user = tool.search1('User', noSecurity=True, login=login)
|
||||||
|
# It is possible that we find no user here: it happens before users
|
||||||
|
# "anon" and "system" are created, at first startup.
|
||||||
|
if not user: return
|
||||||
|
# Authentify the user if required
|
||||||
|
if authentify and not isSpecial:
|
||||||
|
if not user.checkPassword(password):
|
||||||
|
# Disable the authentication cookie.
|
||||||
|
req.RESPONSE.expireCookie('_appy_', path='/')
|
||||||
|
return
|
||||||
|
# Create an authentication cookie for this user.
|
||||||
|
gutils.writeCookie(login, password, req)
|
||||||
|
# Cache the user and some precomputed values, for performance.
|
||||||
|
req.user = user
|
||||||
|
req.userRoles = user.getRoles()
|
||||||
|
req.userLogins = user.getLogins()
|
||||||
|
req.zopeUser = user.getZopeUser()
|
||||||
|
return user
|
||||||
|
|
||||||
def _ldapAuthenticate(self, login, password):
|
def _ldapAuthenticate(self, login, password):
|
||||||
'''Performs a LDAP-based authentication. Returns True if authentication
|
'''Performs a LDAP-based authentication. Returns True if authentication
|
||||||
succeeds.'''
|
succeeds.'''
|
||||||
# Check if LDAP is configured.
|
# Check if LDAP is configured.
|
||||||
ldapConfig = self.getProductConfig(True).ldap
|
cfg = self.getProductConfig(True).ldap
|
||||||
if not ldapConfig: return
|
if not cfg: return
|
||||||
user = ldap.authenticate(login, password, ldapConfig, self)
|
# Get a connector to the LDAP server and connect to the LDAP server.
|
||||||
if not user: return
|
serverUri = cfg.getServerUri()
|
||||||
return True
|
connector = LdapConnector(serverUri, tool=self)
|
||||||
|
success, msg = connector.connect(cfg.adminLogin, cfg.adminPassword)
|
||||||
|
if not success: return
|
||||||
|
# Check if the user corresponding to p_login exists in the LDAP.
|
||||||
|
filter = connector.getFilter(cfg.getUserFilterValues(login))
|
||||||
|
params = cfg.getUserAttributes()
|
||||||
|
ldapData = connector.search(cfg.baseDn, cfg.scope, filter, params)
|
||||||
|
if not ldapData: return
|
||||||
|
# The user exists. Try to connect to the LDAP with this user in order
|
||||||
|
# to validate its password.
|
||||||
|
userConnector = LdapConnector(serverUri, tool=self)
|
||||||
|
success, msg = userConnector.connect(ldapData[0][0], password)
|
||||||
|
if not success: return
|
||||||
|
# The password is correct. We can create/update our local user
|
||||||
|
# corresponding to this LDAP user.
|
||||||
|
userParams = cfg.getUserParams(ldapData)
|
||||||
|
user = self.search1('User', noSecurity=True, login=login)
|
||||||
|
tool = self
|
||||||
|
if user:
|
||||||
|
# Update the user with fresh info about him from the LDAP
|
||||||
|
for name, value in userParams.iteritems():
|
||||||
|
setattr(user, name, value)
|
||||||
|
user.reindex()
|
||||||
|
else:
|
||||||
|
# Create the user
|
||||||
|
user = tool.create('users', login=login, source='ldap',**userParams)
|
||||||
|
return user
|
||||||
|
|
||||||
def performLogin(self):
|
def performLogin(self):
|
||||||
'''Logs the user in.'''
|
'''Logs the user in.'''
|
||||||
|
@ -1004,16 +1090,12 @@ class ToolMixin(BaseMixin):
|
||||||
if jsEnabled and not cookiesEnabled:
|
if jsEnabled and not cookiesEnabled:
|
||||||
msg = self.translate('enable_cookies')
|
msg = self.translate('enable_cookies')
|
||||||
return self.goto(urlBack, msg)
|
return self.goto(urlBack, msg)
|
||||||
# Extract the login and password, and create an authentication cookie
|
# Authenticate the user.
|
||||||
login = rq.get('__ac_name', '')
|
login = rq.get('__ac_name', None)
|
||||||
password = rq.get('__ac_password', '')
|
if self.getUser(authentify=True):
|
||||||
gutils.writeCookie(login, password, rq)
|
|
||||||
# Perform the Zope-level authentication
|
|
||||||
if self._zopeAuthenticate(rq) or self._ldapAuthenticate(login,password):
|
|
||||||
msg = self.translate('login_ok')
|
msg = self.translate('login_ok')
|
||||||
logMsg = 'User "%s" logged in.' % login
|
logMsg = 'User "%s" logged in.' % login
|
||||||
else:
|
else:
|
||||||
rq.RESPONSE.expireCookie('_appy_', path='/')
|
|
||||||
msg = self.translate('login_ko')
|
msg = self.translate('login_ko')
|
||||||
logMsg = 'Authentication failed with login "%s".' % login
|
logMsg = 'Authentication failed with login "%s".' % login
|
||||||
self.log(logMsg)
|
self.log(logMsg)
|
||||||
|
@ -1051,33 +1133,19 @@ class ToolMixin(BaseMixin):
|
||||||
# a is the object the object was accessed through
|
# a is the object the object was accessed through
|
||||||
# c is the physical container of the object
|
# c is the physical container of the object
|
||||||
a, c, n, v = self._getobcontext(v, request)
|
a, c, n, v = self._getobcontext(v, request)
|
||||||
print c
|
# Identify and authentify the user
|
||||||
# Try to get user name and password from basic authentication
|
user = self.getParentNode().config.getUser(authentify=True)
|
||||||
login, password = self.identify(auth)
|
if not user:
|
||||||
if not login:
|
|
||||||
# Try to get them from a cookie
|
|
||||||
login, password = gutils.readCookie(request)
|
|
||||||
if not login:
|
|
||||||
# Maybe the user just entered his credentials. The cookie could
|
|
||||||
# have been set in the response, but is not in the request.
|
|
||||||
login = request.get('__ac_name', None)
|
|
||||||
password = request.get('__ac_password', None)
|
|
||||||
# Try to authenticate this user
|
|
||||||
user = self.authenticate(login, password, request)
|
|
||||||
emergency = self._emergency_user
|
|
||||||
if emergency and (user is emergency):
|
|
||||||
# It is the emergency user.
|
|
||||||
return emergency.__of__(self)
|
|
||||||
elif user is None:
|
|
||||||
# Login and/or password incorrect. Try to authorize and return the
|
# Login and/or password incorrect. Try to authorize and return the
|
||||||
# anonymous user.
|
# anonymous user.
|
||||||
if self.authorize(self._nobody, a, c, n, v, roles):
|
if self.authorize(self._nobody, a, c, n, v, roles):
|
||||||
return self._nobody.__of__(self)
|
return self._nobody.__of__(self)
|
||||||
else:
|
else:
|
||||||
return # Anonymous can't acces this object
|
return
|
||||||
else:
|
else:
|
||||||
# We found a user and his password was correct. Try to authorize him
|
# We found a user and his password was correct. Try to authorize him
|
||||||
# against the published object.
|
# against the published object.
|
||||||
|
user = user.getZopeUser()
|
||||||
if self.authorize(user, a, c, n, v, roles):
|
if self.authorize(user, a, c, n, v, roles):
|
||||||
return user.__of__(self)
|
return user.__of__(self)
|
||||||
# That didn't work. Try to authorize the anonymous user.
|
# That didn't work. Try to authorize the anonymous user.
|
||||||
|
@ -1090,30 +1158,6 @@ class ToolMixin(BaseMixin):
|
||||||
from AccessControl.User import BasicUserFolder
|
from AccessControl.User import BasicUserFolder
|
||||||
BasicUserFolder.validate = validate
|
BasicUserFolder.validate = validate
|
||||||
|
|
||||||
def getUser(self):
|
|
||||||
'''Gets the User instance (Appy wrapper) corresponding to the current
|
|
||||||
user.'''
|
|
||||||
tool = self.appy()
|
|
||||||
rq = tool.request
|
|
||||||
# Try first to return the user that can be cached on the request.
|
|
||||||
if hasattr(rq, 'user'): return rq.user
|
|
||||||
# Get the user login from the authentication cookie.
|
|
||||||
login, password = gutils.readCookie(rq)
|
|
||||||
if not login: # It is the anonymous user or the system.
|
|
||||||
# If we have a real request object, it is the anonymous user.
|
|
||||||
login = (rq.__class__.__name__ == 'Object') and 'system' or 'anon'
|
|
||||||
# Get the User object from a query in the catalog.
|
|
||||||
user = tool.search1('User', noSecurity=True, login=login)
|
|
||||||
# It is possible that we find no user here: it happens before users
|
|
||||||
# "anon" and "system" are created, at first Zope startup.
|
|
||||||
if not user: return
|
|
||||||
rq.user = user
|
|
||||||
# Precompute some values or this user for performance reasons.
|
|
||||||
rq.userRoles = user.getRoles()
|
|
||||||
rq.userLogins = user.getLogins()
|
|
||||||
rq.zopeUser = user.getZopeUser()
|
|
||||||
return user
|
|
||||||
|
|
||||||
def getUserLine(self):
|
def getUserLine(self):
|
||||||
'''Returns a info about the currently logged user as a 2-tuple: first
|
'''Returns a info about the currently logged user as a 2-tuple: first
|
||||||
elem is the one-line user info as shown on every page; second line is
|
elem is the one-line user info as shown on every page; second line is
|
||||||
|
|
|
@ -141,7 +141,8 @@ class ModelClass:
|
||||||
# The User class ---------------------------------------------------------------
|
# The User class ---------------------------------------------------------------
|
||||||
class User(ModelClass):
|
class User(ModelClass):
|
||||||
_appy_attributes = ['title', 'name', 'firstName', 'login', 'password1',
|
_appy_attributes = ['title', 'name', 'firstName', 'login', 'password1',
|
||||||
'password2', 'email', 'roles', 'groups', 'toTool']
|
'password2', 'email', 'roles', 'source', 'groups',
|
||||||
|
'toTool']
|
||||||
# All methods defined below are fake. Real versions are in the wrapper.
|
# All methods defined below are fake. Real versions are in the wrapper.
|
||||||
title = gen.String(show=False, indexed=True)
|
title = gen.String(show=False, indexed=True)
|
||||||
gm = {'group': 'main', 'width': 25}
|
gm = {'group': 'main', 'width': 25}
|
||||||
|
@ -150,6 +151,9 @@ class User(ModelClass):
|
||||||
firstName = gen.String(show=showName, **gm)
|
firstName = gen.String(show=showName, **gm)
|
||||||
def showEmail(self): pass
|
def showEmail(self): pass
|
||||||
email = gen.String(show=showEmail, **gm)
|
email = gen.String(show=showEmail, **gm)
|
||||||
|
# Where is this user stored? By default, in the ZODB. But the user can be
|
||||||
|
# stored in an external LDAP (source='ldap').
|
||||||
|
source = gen.String(show=False, default='zodb', layouts='f', **gm)
|
||||||
gm['multiplicity'] = (1,1)
|
gm['multiplicity'] = (1,1)
|
||||||
def showLogin(self): pass
|
def showLogin(self): pass
|
||||||
def validateLogin(self): pass
|
def validateLogin(self): pass
|
||||||
|
@ -164,9 +168,6 @@ class User(ModelClass):
|
||||||
def showRoles(self): pass
|
def showRoles(self): pass
|
||||||
roles = gen.String(show=showRoles, indexed=True,
|
roles = gen.String(show=showRoles, indexed=True,
|
||||||
validator=gen.Selection('getGrantableRoles'), **gm)
|
validator=gen.Selection('getGrantableRoles'), **gm)
|
||||||
# Where is this user stored? By default, in the ZODB. But the user can be
|
|
||||||
# stored in an external LDAP.
|
|
||||||
source = gen.String(show=False, default='zodb', layouts='f', **gm)
|
|
||||||
|
|
||||||
# The Group class --------------------------------------------------------------
|
# The Group class --------------------------------------------------------------
|
||||||
class Group(ModelClass):
|
class Group(ModelClass):
|
||||||
|
|
72
shared/ldap_connector.py
Normal file
72
shared/ldap_connector.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
import ldap
|
||||||
|
except ImportError:
|
||||||
|
# For people that do not care about ldap.
|
||||||
|
ldap = None
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
class LdapConnector:
|
||||||
|
'''This class manages the communication with a LDAP server.'''
|
||||||
|
def __init__(self, serverUri, tentatives=5, ssl=False, timeout=5,
|
||||||
|
tool=None):
|
||||||
|
# The URI of the LDAP server, ie ldap://some.ldap.server:389.
|
||||||
|
self.serverUri = serverUri
|
||||||
|
# The object that will represent the LDAP server
|
||||||
|
self.server = None
|
||||||
|
# The number of trials the connector will at most perform to the LDAP
|
||||||
|
# server, when executing a query in it.
|
||||||
|
self.tentatives = tentatives
|
||||||
|
self.ssl = ssl
|
||||||
|
# The timeout for every query to the LDAP.
|
||||||
|
self.timeout = timeout
|
||||||
|
# A tool from a Appy application can be given and will be used, ie for
|
||||||
|
# logging purpose.
|
||||||
|
self.tool = tool
|
||||||
|
|
||||||
|
def log(self, message, type='info'):
|
||||||
|
'''Logs via a Appy tool if available.'''
|
||||||
|
if self.tool: self.tool.log(message, type=type)
|
||||||
|
|
||||||
|
def connect(self, login, password):
|
||||||
|
'''Connects to the LDAP server using p_login and p_password as
|
||||||
|
credentials. If the connection succeeds, a server object is created
|
||||||
|
in self.server and tuple (True, None) is returned. Else, tuple
|
||||||
|
(False, errorMessage) is returned.'''
|
||||||
|
try:
|
||||||
|
self.server = ldap.initialize(self.serverUri)
|
||||||
|
self.server.simple_bind_s(login, password)
|
||||||
|
return True, None
|
||||||
|
except ldap.LDAPError, le:
|
||||||
|
message = str(le)
|
||||||
|
self.log('%s: connect error with login %s (%s).' % \
|
||||||
|
(self.serverUri, login, message))
|
||||||
|
return False, message
|
||||||
|
|
||||||
|
def getFilter(self, values):
|
||||||
|
'''Builds and returns a LDAP filter based on p_values, a tuple or list
|
||||||
|
of tuples (name,value).'''
|
||||||
|
return '(&%s)' % ''.join(['(%s=%s)' % (n, v) for n, v in values])
|
||||||
|
|
||||||
|
def search(self, baseDn, scope, filter, attributes=None):
|
||||||
|
'''Performs a query in the LDAP at node p_baseDn, with the given
|
||||||
|
p_scope. p_filter is a LDAP filter that constraints the search. It
|
||||||
|
can be computed from a list of tuples (value, name) by method
|
||||||
|
m_getFilter. p_attributes is the list of attributes that we will
|
||||||
|
retrieve from the LDAP. If None, all attributes will be retrieved.'''
|
||||||
|
if self.ssl: self.server.start_tls_s()
|
||||||
|
try:
|
||||||
|
# Get the LDAP constant corresponding to p_scope.
|
||||||
|
scope = getattr(ldap, 'SCOPE_%s' % scope)
|
||||||
|
# Perform the query.
|
||||||
|
for i in range(self.tentatives):
|
||||||
|
try:
|
||||||
|
return self.server.search_st(\
|
||||||
|
baseDn, scope, filterstr=filter, attrlist=attributes,
|
||||||
|
timeout=self.timeout)
|
||||||
|
except ldap.TIMEOUT:
|
||||||
|
pass
|
||||||
|
except ldap.LDAPError, le:
|
||||||
|
self.log('LDAP query error %s: %s' % \
|
||||||
|
(le.__class__.__name__, str(le)))
|
||||||
|
# ------------------------------------------------------------------------------
|
Loading…
Reference in a new issue