Back to index

plone3  3.1.7
LDAPGroupFolder.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 ## GroupUserFolder
00003 ## Copyright (C)2006 Ingeniweb
00004 
00005 ## This program is free software; you can redistribute it and/or modify
00006 ## it under the terms of the GNU General Public License as published by
00007 ## the Free Software Foundation; either version 2 of the License, or
00008 ## (at your option) any later version.
00009 
00010 ## This program is distributed in the hope that it will be useful,
00011 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 ## GNU General Public License for more details.
00014 
00015 ## You should have received a copy of the GNU General Public License
00016 ## along with this program; see the file COPYING. If not, write to the
00017 ## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018 """
00019 
00020 """
00021 __version__ = "$Revision:  $"
00022 # $Source:  $
00023 # $Id: LDAPGroupFolder.py 40168 2007-04-02 12:12:41Z maurits $
00024 __docformat__ = 'restructuredtext'
00025 
00026 import time, traceback
00027 
00028 # Zope imports
00029 from Globals import DTMLFile, InitializeClass
00030 from Acquisition import aq_base
00031 from AccessControl import ClassSecurityInfo
00032 from AccessControl.User import SimpleUser
00033 from AccessControl.Permissions import view_management_screens, manage_users
00034 from OFS.SimpleItem import SimpleItem
00035 from DateTime import DateTime
00036 
00037 from Products.GroupUserFolder import postonly
00038 import GroupUserFolder
00039 
00040 from global_symbols import *
00041 
00042 # LDAPUserFolder package imports
00043 from Products.LDAPUserFolder.SimpleCache import SimpleCache
00044 
00045 addLDAPGroupFolderForm = DTMLFile('dtml/addLDAPGroupFolder', globals())
00046 
00047 
00048 class LDAPGroupFolder(SimpleItem):
00049     """ """
00050     security = ClassSecurityInfo()
00051 
00052     meta_type = 'LDAPGroupFolder'
00053     id = 'acl_users'
00054 
00055     isPrincipiaFolderish=1
00056     isAUserFolder=1
00057 
00058     manage_options=(
00059         ({'label' : 'Groups', 'action' : 'manage_main',},)
00060         + SimpleItem.manage_options
00061         )
00062 
00063     security.declareProtected(view_management_screens, 'manage_main')
00064     manage_main = DTMLFile('dtml/groups', globals())
00065     
00066     
00067     def __setstate__(self, v):
00068         """ """
00069         LDAPGroupFolder.inheritedAttribute('__setstate__')(self, v)
00070         self._cache = SimpleCache()
00071         self._cache.setTimeout(600)
00072         self._cache.clear()
00073 
00074     def __init__( self, title, luf=''):
00075         """ """
00076         self._luf = luf
00077         self._cache = SimpleCache()
00078         self._cache.setTimeout(600)
00079         self._cache.clear()
00080 
00081     security.declarePrivate(manage_users, 'getGRUF')
00082     def getGRUF(self):
00083         """ """
00084         return self.aq_parent.aq_parent
00085 
00086 
00087     security.declareProtected(manage_users, 'getLUF')
00088     def getLUF(self):
00089         """ """
00090         s = self.getGRUF().getUserSource(self._luf)
00091         if getattr(s, 'meta_type', None) != "LDAPUserFolder":
00092             # whoops, we moved LDAPUF... let's try to find it back
00093             Log(LOG_WARNING, "LDAPUserFolder moved. Trying to find it back.")
00094             s = None
00095             for src in self.getGRUF().listUserSources():
00096                 if src.meta_type == "LDAPUserFolder":
00097                     self._luf = src.getPhysicalPath()[-2]
00098                     s = src
00099                     break
00100             if not s:
00101                 raise RuntimeError, "You must change your groups source in GRUF if you do not have a LDAPUserFolder as a users source."
00102         return s
00103 
00104 
00105     security.declareProtected(manage_users, 'getGroups')
00106     def getGroups(self, dn='*', attr=None, pwd=''):
00107         """ """
00108         return self.getLUF().getGroups(dn, attr, pwd)
00109 
00110 
00111     security.declareProtected(manage_users, 'getGroupType')
00112     def getGroupType(self, group_dn):
00113         """ """
00114         return self.getLUF().getGroupType(group_dn)
00115 
00116     security.declareProtected(manage_users, 'getGroupMappings')
00117     def getGroupMappings(self):
00118         """ """
00119         return self.getLUF().getGroupMappings()
00120 
00121     security.declareProtected(manage_users, 'manage_addGroupMapping')
00122     def manage_addGroupMapping(self, group_name, role_name, REQUEST=None):
00123         """ """
00124         self._cache.remove(group_name)
00125         self.getLUF().manage_addGroupMapping(group_name, role_name, None)
00126 
00127         if REQUEST:
00128             msg = 'Added LDAP group to Zope role mapping: %s -> %s' % (
00129                 group_name, role_name)
00130             return self.manage_main(manage_tabs_message=msg)
00131     manage_addGroupMapping = postonly(manage_addGroupMapping)
00132 
00133     security.declareProtected(manage_users, 'manage_deleteGroupMappings')
00134     def manage_deleteGroupMappings(self, group_names, REQUEST=None):
00135         """ Delete mappings from LDAP group to Zope role """
00136         self._cache.clear()
00137         self.getLUF().manage_deleteGroupMappings(group_names, None)
00138         if REQUEST:
00139             msg = 'Deleted LDAP group to Zope role mapping for: %s' % (
00140                 ', '.join(group_names))
00141             return self.manage_main(manage_tabs_message=msg)
00142     manage_deleteGroupMappings = postonly(manage_deleteGroupMappings)
00143 
00144     security.declareProtected(manage_users, 'manage_addGroup')
00145     def manage_addGroup( self
00146                        , newgroup_name
00147                        , newgroup_type='groupOfUniqueNames'
00148                        , REQUEST=None
00149                        ):
00150         """Add a new group in groups_base.
00151         """
00152         self.getLUF().manage_addGroup(newgroup_name, newgroup_type, None)
00153         
00154         if REQUEST:
00155             msg = 'Added new group %s' % (newgroup_name)
00156             return self.manage_main(manage_tabs_message=msg)
00157     manage_addGroup = postonly(manage_addGroup)
00158 
00159     security.declareProtected(manage_users, 'manage_deleteGroups')
00160     def manage_deleteGroups(self, dns=[], REQUEST=None):
00161         """ Delete groups from groups_base """
00162         self.getLUF().manage_deleteGroups(dns, None)
00163         self._cache.clear()
00164  
00165         if REQUEST:
00166             msg = 'Deleted group(s):<br> %s' % '<br>'.join(dns)
00167             return self.manage_main(manage_tabs_message=msg)
00168     manage_deleteGroups = postonly(manage_deleteGroups)
00169 
00170     security.declareProtected(manage_users, 'getUser')
00171     def getUser(self, name):
00172         """ """
00173         # Prevent locally stored groups
00174         luf = self.getLUF()
00175         if luf._local_groups:
00176             return []
00177 
00178         # Get the group from the cache
00179         user = self._cache.get(name, '')
00180         if user:
00181             return user
00182 
00183         # Scan groups to find the proper user.
00184         # THIS MAY BE EXPENSIVE AND HAS TO BE OPTIMIZED...
00185         grps = self.getLUF().getGroups()
00186         valid_roles = self.userFolderGetRoles()
00187         dn = None
00188         for n, g_dn in grps:
00189             if n == name:
00190                 dn = g_dn
00191                 break
00192         if not dn:
00193             return None
00194 
00195         # Current mapping
00196         roles = self.getLUF()._mapRoles([name])
00197 
00198         # Nested groups
00199         groups = list(self.getLUF().getGroups(dn=dn, attr='cn', ))
00200         roles.extend(self.getLUF()._mapRoles(groups))
00201 
00202         # !!! start test
00203         Log(LOG_DEBUG, name, "roles", groups, roles)
00204         Log(LOG_DEBUG, name, "mapping", getattr(self.getLUF(), '_groups_mappings', {}))
00205         # !!! end test
00206 
00207         actual_roles = []
00208         for r in roles:
00209             if r in valid_roles:
00210                 actual_roles.append(r)
00211             elif "%s%s" % (GROUP_PREFIX, r) in valid_roles:
00212                 actual_roles.append("%s%s" % (GROUP_PREFIX, r))
00213         Log(LOG_DEBUG, name, "actual roles", actual_roles)
00214         user = GroupUser(n, '', actual_roles, [])
00215         self._cache.set(name, user)
00216         return user
00217         
00218     security.declareProtected(manage_users, 'getUserNames')
00219     def getUserNames(self):
00220         """ """
00221         Log(LOG_DEBUG, "getUserNames", )
00222         LogCallStack(LOG_DEBUG)
00223         # Prevent locally stored groups
00224         luf = self.getLUF()
00225         if luf._local_groups:
00226             return []
00227         return [g[0] for g in luf.getGroups()]
00228 
00229     security.declareProtected(manage_users, 'getUsers')
00230     def getUsers(self, authenticated=1):
00231         """ """
00232         # Prevent locally stored groups
00233         luf = self.getLUF()
00234         if luf._local_groups:
00235             return []
00236 
00237         data = []
00238         
00239         grps = self.getLUF().getGroups()
00240         valid_roles = self.userFolderGetRoles()
00241         for n, dn in grps:
00242             # Group mapping
00243             roles = self.getLUF()._mapRoles([n])
00244             
00245             # computation
00246             actual_roles = []
00247             for r in roles:
00248                 if r in valid_roles:
00249                     actual_roles.append(r)
00250                 elif "%s%s" % (GROUP_PREFIX, r) in valid_roles:
00251                     actual_roles.append("%s%s" % (GROUP_PREFIX, r))
00252             user = GroupUser(n, '', actual_roles, [])
00253             data.append(user)
00254 
00255         return data
00256 
00257     security.declarePrivate('_doAddUser')
00258     def _doAddUser(self, name, password, roles, domains, **kw):
00259         """WARNING: If a role with exists with the same name as the group, we do not add
00260         the group mapping for it, but we create it as if it were a Zope ROLE.
00261         Ie. it's not possible to have a GRUF Group name = a Zope role name, BUT,
00262         with this system, it's possible to differenciate between LDAP groups and LDAP roles.
00263         """
00264         self.getLUF().manage_addGroup(name)
00265         self.manage_addGroupMapping(name, "group_" + name, None, )
00266         self._doChangeUser(name, password, roles, domains, **kw)
00267 
00268     security.declarePrivate('_doDelUsers')
00269     def _doDelUsers(self, names):
00270         dns = []
00271         luf = self.getLUF()
00272         for g_name, dn in luf.getGroups():
00273             if g_name in names:
00274                 dns.append(dn)
00275         self._cache.clear()
00276         return luf.manage_deleteGroups(dns)
00277 
00278     security.declarePrivate('_doChangeUser')
00279     def _doChangeUser(self, name, password, roles, domains, **kw):
00280         """
00281         This is used to change the groups (especially their roles).
00282 
00283         [ THIS TEXT IS OUTDATED :
00284           WARNING: If a ZOPE role with the same name as the GRUF group exists,
00285           we do not add the group mapping for it, but we create it as if it were a Zope ROLE.
00286           Ie. it's not possible to have a GRUF Group name = a Zope role name, BUT,
00287           with this system, it's possible to differenciate between LDAP groups and LDAP roles.
00288         ]
00289         """
00290         luf = self.getLUF()
00291         self._cache.remove(name)
00292 
00293         # Get group DN
00294         dn = None
00295         for g_name, g_dn in luf.getGroups():
00296             if g_name == name:
00297                 dn = g_dn
00298                 break
00299         if not dn:
00300             raise ValueError, "Invalid LDAP group: '%s'" % (name, )
00301                 
00302         # Edit group mappings
00303 ##        if name in self.aq_parent.valid_roles():
00304 ##            # This is, in fact, a role
00305 ##            self.getLUF().manage_addGroupMapping(name, name)
00306 ##        else:
00307 ##            # This is a group -> we set it as a group
00308 ##            self.getLUF().manage_addGroupMapping(name, self.getGroupPrefix() + name)
00309 
00310         # Change roles
00311         if luf._local_groups:
00312             luf.manage_editUserRoles(dn, roles)
00313         else:
00314             # We have to transform roles into group dns: transform them as a dict
00315             role_dns = []
00316             all_groups = luf.getGroups()
00317             all_roles = luf.valid_roles()
00318             groups = {}
00319             for g in all_groups:
00320                 groups[g[0]] = g[1]
00321 
00322             # LDAPUF < 2.4Beta3 adds possibly invalid roles to the user roles
00323             # (for example, adding the cn of a group additionnaly to the mapped zope role).
00324             # So we must remove from our 'roles' list all roles which are prefixed by group prefix
00325             # but are not actually groups.
00326             # If a group has the same name as a role, we assume that it should be a _role_.
00327             # We should check against group/role mapping here, but... well... XXX TODO !
00328             # See "HERE IT IS" comment below.
00329 
00330             # Scan roles we are asking for to manage groups correctly
00331             for role in roles:
00332                 if not role in all_roles:
00333                     continue                        # Do not allow propagation of invalid roles
00334                 if role.startswith(GROUP_PREFIX):
00335                     role = role[GROUP_PREFIX_LEN:]            # Remove group prefix : groups are stored WITHOUT prefix in LDAP
00336                     if role in all_roles:
00337                         continue                            # HERE IT IS
00338                 r = groups.get(role, None)
00339                 if not r:
00340                     Log(LOG_WARNING, "LDAP Server doesn't provide a '%s' group (asked for user '%s')." % (role, name, ))
00341                     continue
00342                 role_dns.append(r)
00343 
00344             # Perform the change
00345             luf.manage_editGroupRoles(dn, role_dns)
00346 
00347 
00348 
00349 def manage_addLDAPGroupFolder( self, title = '', luf='', REQUEST=None):
00350     """ """
00351     this_folder = self.this()
00352 
00353     if hasattr(aq_base(this_folder), 'acl_users') and REQUEST is not None:
00354         msg = 'This+object+already+contains+a+User+Folder'
00355 
00356     else:
00357         # Try to guess where is LUF
00358         if not luf:
00359             for src in this_folder.listUserSources():
00360                 if src.meta_type == "LDAPUserFolder":
00361                     luf = src.aq_parent.getId()
00362 
00363         # No LUF found : error
00364         if not luf:
00365             raise KeyError, "You must be within GRUF with a LDAPUserFolder as one of your user sources."
00366             
00367         n = LDAPGroupFolder( title, luf )
00368 
00369         this_folder._setObject('acl_users', n)
00370         this_folder.__allow_groups__ = self.acl_users
00371         
00372         msg = 'Added+LDAPGroupFolder'
00373  
00374     # return to the parent object's manage_main
00375     if REQUEST:
00376         url = REQUEST['URL1']
00377         qs = 'manage_tabs_message=%s' % msg
00378         REQUEST.RESPONSE.redirect('%s/manage_main?%s' % (url, qs))
00379 
00380 
00381 InitializeClass(LDAPGroupFolder)
00382 
00383 
00384 class GroupUser(SimpleUser):
00385     """ """
00386 
00387     def __init__(self, name, password, roles, domains):
00388         SimpleUser.__init__(self, name, password, roles, domains)
00389         self._created = time.time()
00390 
00391     def getCreationTime(self):
00392         """ """
00393         return DateTime(self._created)