]> CRI, Mines Paris - PSL - GroupUserFolder.git/blobdiff - Products/GroupUserFolder/LDAPGroupFolder.py
eggification
[GroupUserFolder.git] / Products / GroupUserFolder / LDAPGroupFolder.py
diff --git a/Products/GroupUserFolder/LDAPGroupFolder.py b/Products/GroupUserFolder/LDAPGroupFolder.py
new file mode 100755 (executable)
index 0000000..a269384
--- /dev/null
@@ -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):<br> %s' % '<br>'.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)