X-Git-Url: https://scm.cri.minesparis.psl.eu/git/GroupUserFolder.git/blobdiff_plain/e9d14b6b5cc9cd4775c60cb340b5c4c787536fc3..3e1ba4932c34812cf2f6f3569b0f0dbea97b7a0b:/Products/GroupUserFolder/LDAPGroupFolder.py?ds=inline diff --git a/Products/GroupUserFolder/LDAPGroupFolder.py b/Products/GroupUserFolder/LDAPGroupFolder.py new file mode 100755 index 0000000..a269384 --- /dev/null +++ b/Products/GroupUserFolder/LDAPGroupFolder.py @@ -0,0 +1,393 @@ +# -*- coding: utf-8 -*- +## GroupUserFolder +## Copyright (C)2006 Ingeniweb + +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; see the file COPYING. If not, write to the +## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +""" + +""" +__version__ = "$Revision: $" +# $Source: $ +# $Id: LDAPGroupFolder.py 587 2008-07-31 09:20:06Z pin $ +__docformat__ = 'restructuredtext' + +import time, traceback + +# Zope imports +from Globals import DTMLFile, InitializeClass +from Acquisition import aq_base +from AccessControl import ClassSecurityInfo +from AccessControl.User import SimpleUser +from AccessControl.Permissions import view_management_screens, manage_users +from OFS.SimpleItem import SimpleItem +from DateTime import DateTime + +from Products.GroupUserFolder import postonly +import GroupUserFolder + +from global_symbols import * + +# LDAPUserFolder package imports +from Products.LDAPUserFolder.SimpleCache import SimpleCache + +addLDAPGroupFolderForm = DTMLFile('dtml/addLDAPGroupFolder', globals()) + + +class LDAPGroupFolder(SimpleItem): + """ """ + security = ClassSecurityInfo() + + meta_type = 'LDAPGroupFolder' + id = 'acl_users' + + isPrincipiaFolderish=1 + isAUserFolder=1 + + manage_options=( + ({'label' : 'Groups', 'action' : 'manage_main',},) + + SimpleItem.manage_options + ) + + security.declareProtected(view_management_screens, 'manage_main') + manage_main = DTMLFile('dtml/groups', globals()) + + + def __setstate__(self, v): + """ """ + LDAPGroupFolder.inheritedAttribute('__setstate__')(self, v) + self._cache = SimpleCache() + self._cache.setTimeout(600) + self._cache.clear() + + def __init__( self, title, luf=''): + """ """ + self._luf = luf + self._cache = SimpleCache() + self._cache.setTimeout(600) + self._cache.clear() + + security.declarePrivate(manage_users, 'getGRUF') + def getGRUF(self): + """ """ + return self.aq_parent.aq_parent + + + security.declareProtected(manage_users, 'getLUF') + def getLUF(self): + """ """ + s = self.getGRUF().getUserSource(self._luf) + if getattr(s, 'meta_type', None) != "LDAPUserFolder": + # whoops, we moved LDAPUF... let's try to find it back + Log(LOG_WARNING, "LDAPUserFolder moved. Trying to find it back.") + s = None + for src in self.getGRUF().listUserSources(): + if src.meta_type == "LDAPUserFolder": + self._luf = src.getPhysicalPath()[-2] + s = src + break + if not s: + raise RuntimeError, "You must change your groups source in GRUF if you do not have a LDAPUserFolder as a users source." + return s + + + security.declareProtected(manage_users, 'getGroups') + def getGroups(self, dn='*', attr=None, pwd=''): + """ """ + return self.getLUF().getGroups(dn, attr, pwd) + + + security.declareProtected(manage_users, 'getGroupType') + def getGroupType(self, group_dn): + """ """ + return self.getLUF().getGroupType(group_dn) + + security.declareProtected(manage_users, 'getGroupMappings') + def getGroupMappings(self): + """ """ + return self.getLUF().getGroupMappings() + + security.declareProtected(manage_users, 'manage_addGroupMapping') + def manage_addGroupMapping(self, group_name, role_name, REQUEST=None): + """ """ + self._cache.remove(group_name) + self.getLUF().manage_addGroupMapping(group_name, role_name, None) + + if REQUEST: + msg = 'Added LDAP group to Zope role mapping: %s -> %s' % ( + group_name, role_name) + return self.manage_main(manage_tabs_message=msg) + manage_addGroupMapping = postonly(manage_addGroupMapping) + + security.declareProtected(manage_users, 'manage_deleteGroupMappings') + def manage_deleteGroupMappings(self, group_names, REQUEST=None): + """ Delete mappings from LDAP group to Zope role """ + self._cache.clear() + self.getLUF().manage_deleteGroupMappings(group_names, None) + if REQUEST: + msg = 'Deleted LDAP group to Zope role mapping for: %s' % ( + ', '.join(group_names)) + return self.manage_main(manage_tabs_message=msg) + manage_deleteGroupMappings = postonly(manage_deleteGroupMappings) + + security.declareProtected(manage_users, 'manage_addGroup') + def manage_addGroup( self + , newgroup_name + , newgroup_type='groupOfUniqueNames' + , REQUEST=None + ): + """Add a new group in groups_base. + """ + self.getLUF().manage_addGroup(newgroup_name, newgroup_type, None) + + if REQUEST: + msg = 'Added new group %s' % (newgroup_name) + return self.manage_main(manage_tabs_message=msg) + manage_addGroup = postonly(manage_addGroup) + + security.declareProtected(manage_users, 'manage_deleteGroups') + def manage_deleteGroups(self, dns=[], REQUEST=None): + """ Delete groups from groups_base """ + self.getLUF().manage_deleteGroups(dns, None) + self._cache.clear() + + if REQUEST: + msg = 'Deleted group(s):
%s' % '
'.join(dns) + return self.manage_main(manage_tabs_message=msg) + manage_deleteGroups = postonly(manage_deleteGroups) + + security.declareProtected(manage_users, 'getUser') + def getUser(self, name): + """ """ + # Prevent locally stored groups + luf = self.getLUF() + if luf._local_groups: + return [] + + # Get the group from the cache + user = self._cache.get(name, '') + if user: + return user + + # Scan groups to find the proper user. + # THIS MAY BE EXPENSIVE AND HAS TO BE OPTIMIZED... + grps = self.getLUF().getGroups() + valid_roles = self.userFolderGetRoles() + dn = None + for n, g_dn in grps: + if n == name: + dn = g_dn + break + if not dn: + return None + + # Current mapping + roles = self.getLUF()._mapRoles([name]) + + # Nested groups + groups = list(self.getLUF().getGroups(dn=dn, attr='cn', )) + roles.extend(self.getLUF()._mapRoles(groups)) + + # !!! start test + Log(LOG_DEBUG, name, "roles", groups, roles) + Log(LOG_DEBUG, name, "mapping", getattr(self.getLUF(), '_groups_mappings', {})) + # !!! end test + + actual_roles = [] + for r in roles: + if r in valid_roles: + actual_roles.append(r) + elif "%s%s" % (GROUP_PREFIX, r) in valid_roles: + actual_roles.append("%s%s" % (GROUP_PREFIX, r)) + Log(LOG_DEBUG, name, "actual roles", actual_roles) + user = GroupUser(n, '', actual_roles, []) + self._cache.set(name, user) + return user + + security.declareProtected(manage_users, 'getUserNames') + def getUserNames(self): + """ """ + Log(LOG_DEBUG, "getUserNames", ) + LogCallStack(LOG_DEBUG) + # Prevent locally stored groups + luf = self.getLUF() + if luf._local_groups: + return [] + return [g[0] for g in luf.getGroups()] + + security.declareProtected(manage_users, 'getUsers') + def getUsers(self, authenticated=1): + """ """ + # Prevent locally stored groups + luf = self.getLUF() + if luf._local_groups: + return [] + + data = [] + + grps = self.getLUF().getGroups() + valid_roles = self.userFolderGetRoles() + for n, dn in grps: + # Group mapping + roles = self.getLUF()._mapRoles([n]) + + # computation + actual_roles = [] + for r in roles: + if r in valid_roles: + actual_roles.append(r) + elif "%s%s" % (GROUP_PREFIX, r) in valid_roles: + actual_roles.append("%s%s" % (GROUP_PREFIX, r)) + user = GroupUser(n, '', actual_roles, []) + data.append(user) + + return data + + security.declarePrivate('_doAddUser') + def _doAddUser(self, name, password, roles, domains, **kw): + """WARNING: If a role with exists with the same name as the group, we do not add + the group mapping for it, but we create it as if it were a Zope ROLE. + Ie. it's not possible to have a GRUF Group name = a Zope role name, BUT, + with this system, it's possible to differenciate between LDAP groups and LDAP roles. + """ + self.getLUF().manage_addGroup(name) + self.manage_addGroupMapping(name, "group_" + name, None, ) + self._doChangeUser(name, password, roles, domains, **kw) + + security.declarePrivate('_doDelUsers') + def _doDelUsers(self, names): + dns = [] + luf = self.getLUF() + for g_name, dn in luf.getGroups(): + if g_name in names: + dns.append(dn) + self._cache.clear() + return luf.manage_deleteGroups(dns) + + security.declarePrivate('_doChangeUser') + def _doChangeUser(self, name, password, roles, domains, **kw): + """ + This is used to change the groups (especially their roles). + + [ THIS TEXT IS OUTDATED : + WARNING: If a ZOPE role with the same name as the GRUF group exists, + we do not add the group mapping for it, but we create it as if it were a Zope ROLE. + Ie. it's not possible to have a GRUF Group name = a Zope role name, BUT, + with this system, it's possible to differenciate between LDAP groups and LDAP roles. + ] + """ + luf = self.getLUF() + self._cache.remove(name) + + # Get group DN + dn = None + for g_name, g_dn in luf.getGroups(): + if g_name == name: + dn = g_dn + break + if not dn: + raise ValueError, "Invalid LDAP group: '%s'" % (name, ) + + # Edit group mappings +## if name in self.aq_parent.valid_roles(): +## # This is, in fact, a role +## self.getLUF().manage_addGroupMapping(name, name) +## else: +## # This is a group -> we set it as a group +## self.getLUF().manage_addGroupMapping(name, self.getGroupPrefix() + name) + + # Change roles + if luf._local_groups: + luf.manage_editUserRoles(dn, roles) + else: + # We have to transform roles into group dns: transform them as a dict + role_dns = [] + all_groups = luf.getGroups() + all_roles = luf.valid_roles() + groups = {} + for g in all_groups: + groups[g[0]] = g[1] + + # LDAPUF < 2.4Beta3 adds possibly invalid roles to the user roles + # (for example, adding the cn of a group additionnaly to the mapped zope role). + # So we must remove from our 'roles' list all roles which are prefixed by group prefix + # but are not actually groups. + # If a group has the same name as a role, we assume that it should be a _role_. + # We should check against group/role mapping here, but... well... XXX TODO ! + # See "HERE IT IS" comment below. + + # Scan roles we are asking for to manage groups correctly + for role in roles: + if not role in all_roles: + continue # Do not allow propagation of invalid roles + if role.startswith(GROUP_PREFIX): + role = role[GROUP_PREFIX_LEN:] # Remove group prefix : groups are stored WITHOUT prefix in LDAP + if role in all_roles: + continue # HERE IT IS + r = groups.get(role, None) + if not r: + Log(LOG_WARNING, "LDAP Server doesn't provide a '%s' group (asked for user '%s')." % (role, name, )) + continue + role_dns.append(r) + + # Perform the change + luf.manage_editGroupRoles(dn, role_dns) + + + +def manage_addLDAPGroupFolder( self, title = '', luf='', REQUEST=None): + """ """ + this_folder = self.this() + + if hasattr(aq_base(this_folder), 'acl_users') and REQUEST is not None: + msg = 'This+object+already+contains+a+User+Folder' + + else: + # Try to guess where is LUF + if not luf: + for src in this_folder.listUserSources(): + if src.meta_type == "LDAPUserFolder": + luf = src.aq_parent.getId() + + # No LUF found : error + if not luf: + raise KeyError, "You must be within GRUF with a LDAPUserFolder as one of your user sources." + + n = LDAPGroupFolder( title, luf ) + + this_folder._setObject('acl_users', n) + this_folder.__allow_groups__ = self.acl_users + + msg = 'Added+LDAPGroupFolder' + + # return to the parent object's manage_main + if REQUEST: + url = REQUEST['URL1'] + qs = 'manage_tabs_message=%s' % msg + REQUEST.RESPONSE.redirect('%s/manage_main?%s' % (url, qs)) + + +InitializeClass(LDAPGroupFolder) + + +class GroupUser(SimpleUser): + """ """ + + def __init__(self, name, password, roles, domains): + SimpleUser.__init__(self, name, password, roles, domains) + self._created = time.time() + + def getCreationTime(self): + """ """ + return DateTime(self._created)