[gen] LDAP bugfixes.
This commit is contained in:
parent
e51308b277
commit
e344ff51e2
|
@ -1,9 +1,10 @@
|
|||
'''This script allows to check a LDAP connection.'''
|
||||
import sys, ldap
|
||||
import sys
|
||||
from appy.shared.ldap_connector import LdapConnector
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class LdapTester:
|
||||
'''Usage: python checkldap.py ldapUri login password base attrs filter
|
||||
'''Usage: python checkldap.py ldapUri login password base attrs filter scope
|
||||
|
||||
ldapUri is, for example, "ldap://127.0.0.1:389"
|
||||
login is the login user DN, ie: "cn=gdy,o=geezteem"
|
||||
|
@ -27,34 +28,21 @@ class LdapTester:
|
|||
self.attrs = self.attrs.split(',')
|
||||
self.tentatives = 5
|
||||
self.timeout = 5
|
||||
self.attrList = ['cn']
|
||||
self.attributes = ['cn']
|
||||
self.ssl = False
|
||||
|
||||
def test(self):
|
||||
# Connect the the LDAP
|
||||
print('Creating server object for server %s...' % self.uri)
|
||||
server = ldap.initialize(self.uri)
|
||||
print('Done. Login with %s...' % self.login)
|
||||
server.simple_bind(self.login, self.password)
|
||||
if self.ssl:
|
||||
server.start_tls_s()
|
||||
try:
|
||||
for i in range(self.tentatives):
|
||||
try:
|
||||
print('Done. Performing a simple query on %s...'% self.base)
|
||||
res = server.search_st(self.base,
|
||||
getattr(ldap, 'SCOPE_%s' % self.scope),
|
||||
filterstr=self.filter,
|
||||
attrlist=self.attrs, timeout=5)
|
||||
print('Got %d entries' % len(res))
|
||||
break
|
||||
except ldap.TIMEOUT:
|
||||
print('Got timeout.')
|
||||
except ldap.LDAPError, le:
|
||||
print('%s %s' % (le.__class__.__name__, str(le)))
|
||||
print('Connecting to... %s' % self.uri)
|
||||
connector = LdapConnector(self.uri)
|
||||
success, msg = connector.connect(self.login, self.password)
|
||||
if not success: return
|
||||
# Perform the query.
|
||||
print ('Querying %s...' % self.base)
|
||||
res = connector.search(self.base, self.scope, self.filter,
|
||||
self.attributes)
|
||||
print('Got %d results' % len(res))
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
LdapTester().test()
|
||||
|
||||
if __name__ == '__main__': LdapTester().test()
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -412,7 +412,7 @@ class LdapConfig:
|
|||
def getUserAttributes(self):
|
||||
'''Gets the attributes we want to get from the LDAP for characterizing
|
||||
a user.'''
|
||||
res = [self.loginAttribute]
|
||||
res = []
|
||||
for name in self.ldapAttributes.iterkeys():
|
||||
if getattr(self, name):
|
||||
res.append(getattr(self, name))
|
||||
|
@ -429,7 +429,9 @@ class LdapConfig:
|
|||
ldapName = getattr(self, name)
|
||||
if not ldapName: continue
|
||||
if ldapData.has_key(ldapName) and ldapData[ldapName]:
|
||||
res[appyName] = ldapData[ldapName]
|
||||
value = ldapData[ldapName]
|
||||
if isinstance(value, list): value = value[0]
|
||||
res[appyName] = value
|
||||
return res
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Zope product.'''
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
import os, os.path, time
|
||||
import os, os.path
|
||||
import appy
|
||||
import appy.version
|
||||
import appy.gen as gen
|
||||
|
@ -16,24 +16,6 @@ from appy.shared.data import languages
|
|||
homePage = '<tal:h define="dummy python: request.RESPONSE.redirect(' \
|
||||
'context.config.getHomePage())"/>'
|
||||
|
||||
# Stuff for tracking user activity ---------------------------------------------
|
||||
loggedUsers = {}
|
||||
originalTraverse = None
|
||||
doNotTrack = ('.jpg','.gif','.png','.js','.css')
|
||||
|
||||
def traverseWrapper(self, path, response=None, validated_hook=None):
|
||||
'''This function is called every time a users gets a URL, this is used for
|
||||
tracking user activity. self is a BaseRequest'''
|
||||
res = originalTraverse(self, path, response, validated_hook)
|
||||
if os.path.splitext(path)[-1].lower() not in doNotTrack:
|
||||
# Do nothing when the user gets non-pages.
|
||||
userId, dummy = gutils.readCookie(self)
|
||||
if userId:
|
||||
loggedUsers[userId] = time.time()
|
||||
# "Touch" the SESSION object. Else, expiration won't occur.
|
||||
session = self.SESSION
|
||||
return res
|
||||
|
||||
def onDelSession(sessionObject, container):
|
||||
'''This function is called when a session expires.'''
|
||||
rq = container.REQUEST
|
||||
|
@ -284,17 +266,6 @@ class ZopeInstaller:
|
|||
else:
|
||||
sessionData.setDelNotificationTarget(None)
|
||||
|
||||
def enableUserTracking(self):
|
||||
'''Enables the machinery allowing to know who is currently logged in.
|
||||
Information about logged users will be stored in RAM, in the variable
|
||||
named loggedUsers defined above.'''
|
||||
global originalTraverse
|
||||
if not originalTraverse:
|
||||
# User tracking is not enabled yet. Do it now.
|
||||
BaseRequest = self.config.BaseRequest
|
||||
originalTraverse = BaseRequest.traverse
|
||||
BaseRequest.traverse = traverseWrapper
|
||||
|
||||
def installZopeClasses(self):
|
||||
'''Zope-level class registration.'''
|
||||
for klass in self.classes:
|
||||
|
@ -358,7 +329,6 @@ class ZopeInstaller:
|
|||
self.installRoles()
|
||||
self.installAppyTypes()
|
||||
self.installZopeClasses()
|
||||
self.enableUserTracking()
|
||||
self.configureSessions()
|
||||
self.installBaseObjects()
|
||||
# The following line cleans and rebuilds the catalog entirely.
|
||||
|
|
|
@ -1038,17 +1038,20 @@ class ToolMixin(BaseMixin):
|
|||
if not success: return
|
||||
# The password is correct. We can create/update our local user
|
||||
# corresponding to this LDAP user.
|
||||
userParams = cfg.getUserParams(ldapData)
|
||||
userParams = cfg.getUserParams(ldapData[0][1])
|
||||
tool = self.appy()
|
||||
user = tool.search1('User', noSecurity=True, login=login)
|
||||
if user:
|
||||
# Update the user with fresh info about him from the LDAP
|
||||
for name, value in userParams.iteritems():
|
||||
setattr(user, name, value)
|
||||
# Update user password
|
||||
user.setPassword(password, log=False)
|
||||
user.reindex()
|
||||
else:
|
||||
# Create the user
|
||||
user = tool.create('users', login=login, source='ldap',**userParams)
|
||||
user = tool.create('users', noSecurity=True, login=login,
|
||||
password1=password, source='ldap', **userParams)
|
||||
return user
|
||||
|
||||
def getUser(self, authentify=False, source='zodb'):
|
||||
|
@ -1058,7 +1061,9 @@ class ToolMixin(BaseMixin):
|
|||
|
||||
If p_authentify is True and p_source is "zodb", authentication is
|
||||
performed locally. Else (p_source is "ldap"), authentication is
|
||||
performed on a LDAP (if a LDAP configuration is found).'''
|
||||
performed on a LDAP (if a LDAP configuration is found). If p_source
|
||||
is "any", authentication is performed on the local User object, be it
|
||||
really local or a copy of a LDAP user.'''
|
||||
tool = self.appy()
|
||||
req = tool.request
|
||||
# Try first to return the user that can be cached on the request. In
|
||||
|
@ -1073,9 +1078,14 @@ class ToolMixin(BaseMixin):
|
|||
if authentify and not login: return
|
||||
# Now, get the User instance.
|
||||
if source == 'zodb':
|
||||
# Get the User object, but only if it is a true local user.
|
||||
user = tool.search1('User', noSecurity=True, login=login)
|
||||
if user and (user.source != 'zodb'): user = None # Not a local one.
|
||||
elif source == 'ldap':
|
||||
user = self.getLdapUser(login, password)
|
||||
elif source == 'any':
|
||||
# Get the user object, be it really local or a copy of a LDAP user.
|
||||
user = tool.search1('User', noSecurity=True, login=login)
|
||||
if not user: return
|
||||
# Authentify the user if required.
|
||||
if authentify:
|
||||
|
@ -1131,22 +1141,35 @@ class ToolMixin(BaseMixin):
|
|||
session.invalidate()
|
||||
self.log('User "%s" has been logged out.' % userId)
|
||||
# Remove user from variable "loggedUsers"
|
||||
from appy.gen.installer import loggedUsers
|
||||
if loggedUsers.has_key(userId): del loggedUsers[userId]
|
||||
if self.loggedUsers.has_key(userId): del self.loggedUsers[userId]
|
||||
return self.goto(self.getApp().absolute_url())
|
||||
|
||||
# This dict stores, for every logged user, the date/time of its last access
|
||||
loggedUsers = {}
|
||||
forgetAccessExtensions = ('.jpg', '.gif', '.png', '.js', '.css')
|
||||
def rememberAccess(self, id, user):
|
||||
'''Every time there is a hit on the server, this method is called in
|
||||
order to update global dict loggedUsers (see above).'''
|
||||
if not id: return
|
||||
if os.path.splitext(id)[-1].lower() in self.forgetAccessExtensions:
|
||||
return
|
||||
self.loggedUsers[user.login] = time.time()
|
||||
# "Touch" the SESSION object. Else, expiration won't occur.
|
||||
session = self.REQUEST.SESSION
|
||||
|
||||
def validate(self, request, auth='', roles=_noroles):
|
||||
'''This method performs authentication and authorization. It is used as
|
||||
a replacement for Zope's AccessControl.User.BasicUserFolder.validate,
|
||||
that allows to manage cookie-based authentication.'''
|
||||
v = request['PUBLISHED'] # The published object
|
||||
tool = self.getParentNode().config
|
||||
# v is the object (value) we're validating access to
|
||||
# n is the name used to access the object
|
||||
# a is the object the object was accessed through
|
||||
# c is the physical container of the object
|
||||
a, c, n, v = self._getobcontext(v, request)
|
||||
# Identify and authentify the user
|
||||
user = self.getParentNode().config.getUser(authentify=True)
|
||||
user = tool.getUser(authentify=True, source='any')
|
||||
if not user:
|
||||
# Login and/or password incorrect. Try to authorize and return the
|
||||
# anonymous user.
|
||||
|
@ -1156,7 +1179,9 @@ class ToolMixin(BaseMixin):
|
|||
return
|
||||
else:
|
||||
# We found a user and his password was correct. Try to authorize him
|
||||
# against the published object.
|
||||
# against the published object. By the way, remember its last access
|
||||
# to this system.
|
||||
tool.rememberAccess(a.getId(), user)
|
||||
user = user.getZopeUser()
|
||||
if self.authorize(user, a, c, n, v, roles):
|
||||
return user.__of__(self)
|
||||
|
@ -1237,6 +1262,7 @@ class ToolMixin(BaseMixin):
|
|||
if ',' in contentType: return ()
|
||||
return [f.__dict__ for f in self.getAllAppyTypes(contentType) \
|
||||
if (f.type == 'Pod') and (f.show == 'result')]
|
||||
|
||||
def formatDate(self, aDate, withHour=True):
|
||||
'''Returns aDate formatted as specified by tool.dateFormat.
|
||||
If p_withHour is True, hour is appended, with a format specified
|
||||
|
|
|
@ -8,14 +8,9 @@ import wrappers
|
|||
# The following imports are here for allowing mixin classes to access those
|
||||
# elements without being statically dependent on Zope packages.
|
||||
from persistent.list import PersistentList
|
||||
from zExceptions import BadRequest
|
||||
from ZPublisher.HTTPRequest import BaseRequest
|
||||
from OFS.Image import File
|
||||
from ZPublisher.HTTPRequest import FileUpload
|
||||
from AccessControl import getSecurityManager
|
||||
from DateTime import DateTime
|
||||
from Products.ExternalMethod.ExternalMethod import ExternalMethod
|
||||
from Products.Transience.Transience import TransientObjectContainer
|
||||
import appy.gen
|
||||
import logging
|
||||
logger = logging.getLogger('<!applicationName!>')
|
||||
|
|
|
@ -4,7 +4,6 @@ import appy
|
|||
from appy.gen.mail import sendMail
|
||||
from appy.shared.utils import executeCommand
|
||||
from appy.gen.wrappers import AbstractWrapper
|
||||
from appy.gen.installer import loggedUsers
|
||||
from appy.px import Px
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -668,7 +667,7 @@ class ToolWrapper(AbstractWrapper):
|
|||
'<tr><th></th><th>%s</th></tr>' % \
|
||||
self.translate('last_user_access')
|
||||
rows = []
|
||||
for userId, lastAccess in loggedUsers.items():
|
||||
for userId, lastAccess in self.o.loggedUsers.items():
|
||||
user = self.search1('User', noSecurity=True, login=userId)
|
||||
if not user: continue # Could have been deleted in the meanwhile
|
||||
fmt = '%s (%s)' % (self.dateFormat, self.hourFormat)
|
||||
|
|
|
@ -63,7 +63,7 @@ class UserWrapper(AbstractWrapper):
|
|||
'''Returns p_clearPassword, encrypted.'''
|
||||
return self.o.getTool().acl_users._encryptPassword(clearPassword)
|
||||
|
||||
def setPassword(self, newPassword=None):
|
||||
def setPassword(self, newPassword=None, log=True):
|
||||
'''Sets a p_newPassword for self. If p_newPassword is not given, we
|
||||
generate one. This method returns the generated password (or simply
|
||||
p_newPassword if no generation occurred).'''
|
||||
|
@ -76,12 +76,13 @@ class UserWrapper(AbstractWrapper):
|
|||
zopeUser = self.getZopeUser()
|
||||
tool = self.tool.o
|
||||
zopeUser.__ = self.encryptPassword(newPassword)
|
||||
if self.user.login == login:
|
||||
if self.user and (self.user.login == login):
|
||||
# The user for which we change the password is the currently logged
|
||||
# user. So update the authentication cookie, too.
|
||||
gutils.writeCookie(login, newPassword, self.request)
|
||||
self.log('Password %s by "%s" for "%s".' % \
|
||||
(msgPart, self.user.login, login))
|
||||
if log:
|
||||
self.log('Password %s by "%s" for "%s".' % \
|
||||
(msgPart, self.user.login, login))
|
||||
return newPassword
|
||||
|
||||
def checkPassword(self, clearPassword):
|
||||
|
@ -191,10 +192,11 @@ class UserWrapper(AbstractWrapper):
|
|||
# "self" must be owned by its Zope user.
|
||||
if 'Owner' not in self.o.get_local_roles_for_userid(login):
|
||||
self.o.manage_addLocalRoles(login, ('Owner',))
|
||||
# If the user was created by an Anonymous, Anonymous can't stay Owner
|
||||
# of the object.
|
||||
# If the user was created by anon or system, remove this local role.
|
||||
if 'anon' in self.o.__ac_local_roles__:
|
||||
del self.o.__ac_local_roles__['anon']
|
||||
if 'system' in self.o.__ac_local_roles__:
|
||||
del self.o.__ac_local_roles__['system']
|
||||
return self._callCustom('onEdit', created)
|
||||
|
||||
def mayEdit(self):
|
||||
|
@ -311,7 +313,7 @@ class UserWrapper(AbstractWrapper):
|
|||
userLogins = self.getLogins()
|
||||
for login, roles in localRoles.iteritems():
|
||||
# Ignore logins not corresponding to this user.
|
||||
if login not in logins: continue
|
||||
if login not in userLogins: continue
|
||||
for role in roles:
|
||||
if role in allowedRoles: return True
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -26,7 +26,10 @@ class LdapConnector:
|
|||
|
||||
def log(self, message, type='info'):
|
||||
'''Logs via a Appy tool if available.'''
|
||||
if self.tool: self.tool.log(message, type=type)
|
||||
if self.tool:
|
||||
self.tool.log(message, type=type)
|
||||
else:
|
||||
print(message)
|
||||
|
||||
def connect(self, login, password):
|
||||
'''Connects to the LDAP server using p_login and p_password as
|
||||
|
|
Loading…
Reference in a new issue