--- /dev/null
+# -*- 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: GRUFUser.py 40118 2007-04-01 15:13:44Z alecm $
+__docformat__ = 'restructuredtext'
+
+from copy import copy
+
+# fakes a method from a DTML File
+from Globals import MessageDialog, DTMLFile
+
+from AccessControl import ClassSecurityInfo
+from AccessControl import Permissions
+from AccessControl import getSecurityManager
+from Globals import InitializeClass
+from Acquisition import Implicit, aq_inner, aq_parent, aq_base
+from Globals import Persistent
+from AccessControl.Role import RoleManager
+from OFS.SimpleItem import Item
+from OFS.PropertyManager import PropertyManager
+from OFS import ObjectManager, SimpleItem
+from DateTime import DateTime
+from App import ImageFile
+import AccessControl.Role, webdav.Collection
+import Products
+import os
+import string
+import shutil
+import random
+from global_symbols import *
+import AccessControl
+from Products.GroupUserFolder import postonly
+import GRUFFolder
+import GroupUserFolder
+from AccessControl.PermissionRole \
+ import _what_not_even_god_should_do, rolesForPermissionOn
+from ComputedAttribute import ComputedAttribute
+
+
+import os
+import traceback
+
+from interfaces.IUserFolder import IUser, IGroup
+
+_marker = ['INVALID_VALUE']
+
+# NOTE : _what_not_even_god_should_do is a specific permission defined by ZOPE
+# that indicates that something has not to be done within Zope.
+# This value is given to the ACCESS_NONE directive of a SecurityPolicy.
+# It's rarely used within Zope BUT as it is documented (in AccessControl)
+# and may be used by third-party products, we have to process it.
+
+
+#GROUP_PREFIX is a constant
+
+class GRUFUserAtom(AccessControl.User.BasicUser, Implicit):
+ """
+ Base class for all GRUF-catched User objects.
+ There's, alas, many copy/paste from AccessControl.BasicUser...
+ """
+ security = ClassSecurityInfo()
+
+ security.declarePrivate('_setUnderlying')
+ def _setUnderlying(self, user):
+ """
+ _setUnderlying(self, user) => Set the GRUFUser properties to
+ the underlying user's one.
+ Be careful that any change to the underlying user won't be
+ reported here. $$$ We don't know yet if User object are
+ transaction-persistant or not...
+ """
+ self._original_name = user.getUserName()
+ self._original_password = user._getPassword()
+ self._original_roles = user.getRoles()
+ self._original_domains = user.getDomains()
+ self._original_id = user.getId()
+ self.__underlying__ = user # Used for authenticate() and __getattr__
+
+
+ # ----------------------------
+ # Public User object interface
+ # ----------------------------
+
+ # Maybe allow access to unprotected attributes. Note that this is
+ # temporary to avoid exposing information but without breaking
+ # everyone's current code. In the future the security will be
+ # clamped down and permission-protected here. Because there are a
+ # fair number of user object types out there, this method denies
+ # access to names that are private parts of the standard User
+ # interface or implementation only. The other approach (only
+ # allowing access to public names in the User interface) would
+ # probably break a lot of other User implementations with extended
+ # functionality that we cant anticipate from the base scaffolding.
+
+ security.declarePrivate('__init__')
+ def __init__(self, underlying_user, GRUF, isGroup, source_id, ):
+ # When calling, set isGroup it to TRUE if this user represents a group
+ self._setUnderlying(underlying_user)
+ self._isGroup = isGroup
+ self._GRUF = GRUF
+ self._source_id = source_id
+ self.id = self._original_id
+ # Store the results of getRoles and getGroups. Initially set to None,
+ # set to a list after the methods are first called.
+ # If you are caching users you want to clear these.
+ self.clearCachedGroupsAndRoles()
+
+ security.declarePrivate('clearCachedGroupsAndRoles')
+ def clearCachedGroupsAndRoles(self, underlying_user = None):
+ self._groups = None
+ self._user_roles = None
+ self._group_roles = None
+ self._all_roles = None
+ if underlying_user:
+ self._setUnderlying(underlying_user)
+ self._original_user_roles = None
+
+ security.declarePublic('isGroup')
+ def isGroup(self,):
+ """Return 1 if this user is a group abstraction"""
+ return self._isGroup
+
+ security.declarePublic('getUserSourceId')
+ def getUserSourceId(self,):
+ """
+ getUserSourceId(self,) => string
+ Return the GRUF's GRUFUsers folder used to fetch this user.
+ """
+ return self._source_id
+
+ security.declarePrivate('getGroupNames')
+ def getGroupNames(self,):
+ """..."""
+ ret = self._getGroups(no_recurse = 1)
+ return map(lambda x: x[GROUP_PREFIX_LEN:], ret)
+
+ security.declarePrivate('getGroupIds')
+ def getGroupIds(self,):
+ """..."""
+ return list(self._getGroups(no_recurse = 1))
+
+ security.declarePrivate("getAllGroups")
+ def getAllGroups(self,):
+ """Same as getAllGroupNames()"""
+ return self.getAllGroupIds()
+
+ security.declarePrivate('getAllGroupNames')
+ def getAllGroupNames(self,):
+ """..."""
+ ret = self._getGroups()
+ return map(lambda x: x[GROUP_PREFIX_LEN:], ret)
+
+ security.declarePrivate('getAllGroupIds')
+ def getAllGroupIds(self,):
+ """..."""
+ return list(self._getGroups())
+
+ security.declarePrivate('getGroups')
+ def getGroups(self, *args, **kw):
+ """..."""
+ ret = self._getGroups(*args, **kw)
+ return list(ret)
+
+ security.declarePrivate("getImmediateGroups")
+ def getImmediateGroups(self,):
+ """
+ Return NON-TRANSITIVE groups
+ """
+ ret = self._getGroups(no_recurse = 1)
+ return list(ret)
+
+ def _getGroups(self, no_recurse = 0, already_done = None, prefix = GROUP_PREFIX):
+ """
+ getGroups(self, no_recurse = 0, already_done = None, prefix = GROUP_PREFIX) => list of strings
+
+ If this user is a user (uh, uh), get its groups.
+ THIS METHODS NOW SUPPORTS NESTED GROUPS ! :-)
+ The already_done parameter prevents infite recursions.
+ Keep it as it is, never give it a value.
+
+ If no_recurse is true, return only first level groups
+
+ This method is private and should remain so.
+ """
+ if already_done is None:
+ already_done = []
+
+ # List this user's roles. We consider that roles starting
+ # with GROUP_PREFIX are in fact groups, and thus are
+ # returned (prefixed).
+ if self._groups is not None:
+ return self._groups
+
+ # Populate cache if necessary
+ if self._original_user_roles is None:
+ self._original_user_roles = self.__underlying__.getRoles()
+
+ # Scan roles to find groups
+ ret = []
+ for role in self._original_user_roles:
+ # Inspect group-like roles
+ if role.startswith(prefix):
+
+ # Prevent infinite recursion
+ if self._isGroup and role in already_done:
+ continue
+
+ # Get the underlying group
+ grp = self.aq_parent.getUser(role)
+ if not grp:
+ continue # Invalid group
+
+ # Do not add twice the current group
+ if role in ret:
+ continue
+
+ # Append its nested groups (if recurse is asked)
+ ret.append(role)
+ if no_recurse:
+ continue
+ for extend in grp.getGroups(already_done = ret):
+ if not extend in ret:
+ ret.append(extend)
+
+ # Return the groups
+ self._groups = tuple(ret)
+ return self._groups
+
+
+ security.declarePrivate('getGroupsWithoutPrefix')
+ def getGroupsWithoutPrefix(self, **kw):
+ """
+ Same as getGroups but return them without a prefix.
+ """
+ ret = []
+ for group in self.getGroups(**kw):
+ if group.startswith(GROUP_PREFIX):
+ ret.append(group[len(GROUP_PREFIX):])
+ return ret
+
+ security.declarePublic('getUserNameWithoutGroupPrefix')
+ def getUserNameWithoutGroupPrefix(self):
+ """Return the username of a user without a group prefix"""
+ if self.isGroup() and \
+ self._original_name[:len(GROUP_PREFIX)] == GROUP_PREFIX:
+ return self._original_name[len(GROUP_PREFIX):]
+ return self._original_name
+
+ security.declarePublic('getUserId')
+ def getUserId(self):
+ """Return the user id of a user"""
+ if self.isGroup() and \
+ not self._original_name[:len(GROUP_PREFIX)] == GROUP_PREFIX:
+ return "%s%s" % (GROUP_PREFIX, self._original_name )
+ return self._original_name
+
+ security.declarePublic("getName")
+ def getName(self,):
+ """Get user's or group's name.
+ For a user, the name can be set by the underlying user folder but usually id == name.
+ For a group, the ID is prefixed, but the NAME is NOT prefixed by 'group_'.
+ """
+ return self.getUserNameWithoutGroupPrefix()
+
+ security.declarePublic("getUserName")
+ def getUserName(self,):
+ """Alias for getName()"""
+ return self.getUserNameWithoutGroupPrefix()
+
+ security.declarePublic('getId')
+ def getId(self, unprefixed = 0):
+ """Get the ID of the user. The ID can be used, at least from
+ Python, to get the user from the user's UserDatabase
+ """
+ # Return the right id
+ if self.isGroup() and not self._original_name.startswith(GROUP_PREFIX) and not unprefixed:
+ return "%s%s" % (GROUP_PREFIX, self._original_name)
+ return self._original_name
+
+ security.declarePublic('getRoles')
+ def getRoles(self):
+ """
+ Return the list (tuple) of roles assigned to a user.
+ THIS IS WHERE THE ATHENIANS REACHED !
+ """
+ if self._all_roles is not None:
+ return self._all_roles
+
+ # Return user and groups roles
+ self._all_roles = GroupUserFolder.unique(self.getUserRoles() + self.getGroupRoles())
+ return self._all_roles
+
+ security.declarePublic('getUserRoles')
+ def getUserRoles(self):
+ """
+ returns the roles defined for the user without the group roles
+ """
+ if self._user_roles is not None:
+ return self._user_roles
+ prefix = GROUP_PREFIX
+ if self._original_user_roles is None:
+ self._original_user_roles = self.__underlying__.getRoles()
+ self._user_roles = tuple([r for r in self._original_user_roles if not r.startswith(prefix)])
+ return self._user_roles
+
+ security.declarePublic("getGroupRoles")
+ def getGroupRoles(self,):
+ """
+ Return the tuple of roles belonging to this user's group(s)
+ """
+ if self._group_roles is not None:
+ return self._group_roles
+ ret = []
+ acl_users = self._GRUF.acl_users
+ groups = acl_users.getGroupIds() # XXX We can have a cache here
+
+ for group in self.getGroups():
+ if not group in groups:
+ Log("Group", group, "is invalid. Ignoring.")
+ # This may occur when groups are deleted
+ # Ignored silently
+ continue
+ ret.extend(acl_users.getGroup(group).getUserRoles())
+
+ self._group_roles = GroupUserFolder.unique(ret)
+ return self._group_roles
+
+ security.declarePublic('getRolesInContext')
+ def getRolesInContext(self, object, userid = None):
+ """
+ Return the list of roles assigned to the user,
+ including local roles assigned in context of
+ the passed in object.
+ """
+ if not userid:
+ userid=self.getId()
+
+ roles = {}
+ for role in self.getRoles():
+ roles[role] = 1
+
+ user_groups = self.getGroups()
+
+ inner_obj = getattr(object, 'aq_inner', object)
+ while 1:
+ # Usual local roles retreiving
+ local_roles = getattr(inner_obj, '__ac_local_roles__', None)
+ if local_roles:
+ if callable(local_roles):
+ local_roles = local_roles()
+ dict = local_roles or {}
+
+ for role in dict.get(userid, []):
+ roles[role] = 1
+
+ # Get roles & local roles for groups
+ # This handles nested groups as well
+ for groupid in user_groups:
+ for role in dict.get(groupid, []):
+ roles[role] = 1
+
+ # LocalRole blocking
+ obj = getattr(inner_obj, 'aq_base', inner_obj)
+ if getattr(obj, '__ac_local_roles_block__', None):
+ break
+
+ # Loop management
+ inner = getattr(inner_obj, 'aq_inner', inner_obj)
+ parent = getattr(inner, 'aq_parent', None)
+ if parent is not None:
+ inner_obj = parent
+ continue
+ if hasattr(inner_obj, 'im_self'):
+ inner_obj=inner_obj.im_self
+ inner_obj=getattr(inner_obj, 'aq_inner', inner_obj)
+ continue
+ break
+
+ return tuple(roles.keys())
+
+ security.declarePublic('getDomains')
+ def getDomains(self):
+ """Return the list of domain restrictions for a user"""
+ return self._original_domains
+
+
+ security.declarePrivate("getProperty")
+ def getProperty(self, name, default=_marker):
+ """getProperty(self, name) => return property value or raise AttributeError
+ """
+ # Try to do an attribute lookup on the underlying user object
+ v = getattr(self.__underlying__, name, default)
+ if v is _marker:
+ raise AttributeError, name
+ return v
+
+ security.declarePrivate("hasProperty")
+ def hasProperty(self, name):
+ """hasProperty"""
+ return hasattr(self.__underlying__, name)
+
+ security.declarePrivate("setProperty")
+ def setProperty(self, name, value):
+ """setProperty => Try to set the property...
+ By now, it's available only for LDAPUserFolder
+ """
+ # Get actual source
+ src = self._GRUF.getUserSource(self.getUserSourceId())
+ if not src:
+ raise RuntimeError, "Invalid or missing user source for '%s'." % (self.getId(),)
+
+ # LDAPUserFolder => specific API.
+ if hasattr(src, "manage_setUserProperty"):
+ # Unmap pty name if necessary, get it in the schema
+ ldapname = None
+ for schema in src.getSchemaConfig().values():
+ if schema["ldap_name"] == name:
+ ldapname = schema["ldap_name"]
+ if schema["public_name"] == name:
+ ldapname = schema["ldap_name"]
+ break
+
+ # If we didn't find it, we skip it
+ if ldapname is None:
+ raise KeyError, "Invalid LDAP attribute: '%s'." % (name, )
+
+ # Edit user
+ user_dn = src._find_user_dn(self.getUserName())
+ src.manage_setUserProperty(user_dn, ldapname, value)
+
+ # Expire the underlying user object
+ self.__underlying__ = src.getUser(self.getId())
+ if not self.__underlying__:
+ raise RuntimeError, "Error while setting property of '%s'." % (self.getId(),)
+
+ # Now we check if the property has been changed
+ if not self.hasProperty(name):
+ raise NotImplementedError, "Property setting is not supported for '%s'." % (name,)
+ v = self._GRUF.getUserById(self.getId()).getProperty(name)
+ if not v == value:
+ Log(LOG_DEBUG, "Property '%s' for user '%s' should be '%s' and not '%s'" % (
+ name, self.getId(), value, v,
+ ))
+ raise NotImplementedError, "Property setting is not supported for '%s'." % (name,)
+
+ # ------------------------------
+ # Internal User object interface
+ # ------------------------------
+
+ security.declarePrivate('authenticate')
+ def authenticate(self, password, request):
+ # We prevent groups from authenticating
+ if self._isGroup:
+ return None
+ return self.__underlying__.authenticate(password, request)
+
+
+ security.declarePublic('allowed')
+ def allowed(self, object, object_roles=None):
+ """Check whether the user has access to object. The user must
+ have one of the roles in object_roles to allow access."""
+
+ if object_roles is _what_not_even_god_should_do:
+ return 0
+
+ # Short-circuit the common case of anonymous access.
+ if object_roles is None or 'Anonymous' in object_roles:
+ return 1
+
+ # Provide short-cut access if object is protected by 'Authenticated'
+ # role and user is not nobody
+ if 'Authenticated' in object_roles and \
+ (self.getUserName() != 'Anonymous User'):
+ return 1
+
+ # Check for ancient role data up front, convert if found.
+ # This should almost never happen, and should probably be
+ # deprecated at some point.
+ if 'Shared' in object_roles:
+ object_roles = self._shared_roles(object)
+ if object_roles is None or 'Anonymous' in object_roles:
+ return 1
+
+
+ # Trying to make some speed improvements, changes starts here.
+ # Helge Tesdal, Plone Solutions AS, http://www.plonesolutions.com
+ # We avoid using the getRoles() and getRolesInContext() methods to be able
+ # to short circuit.
+
+ # Dict for faster lookup and avoiding duplicates
+ object_roles_dict = {}
+ for role in object_roles:
+ object_roles_dict[role] = 1
+
+ if [role for role in self.getUserRoles() if object_roles_dict.has_key(role)]:
+ if self._check_context(object):
+ return 1
+ return None
+
+ # Try the top level group roles.
+ if [role for role in self.getGroupRoles() if object_roles_dict.has_key(role)]:
+ if self._check_context(object):
+ return 1
+ return None
+
+ user_groups = self.getGroups()
+ # No luck on the top level, try local roles
+ inner_obj = getattr(object, 'aq_inner', object)
+ userid = self.getId()
+ while 1:
+ local_roles = getattr(inner_obj, '__ac_local_roles__', None)
+ if local_roles:
+ if callable(local_roles):
+ local_roles = local_roles()
+ dict = local_roles or {}
+
+ if [role for role in dict.get(userid, []) if object_roles_dict.has_key(role)]:
+ if self._check_context(object):
+ return 1
+ return None
+
+ # Get roles & local roles for groups
+ # This handles nested groups as well
+ for groupid in user_groups:
+ if [role for role in dict.get(groupid, []) if object_roles_dict.has_key(role)]:
+ if self._check_context(object):
+ return 1
+ return None
+
+ # LocalRole blocking
+ obj = getattr(inner_obj, 'aq_base', inner_obj)
+ if getattr(obj, '__ac_local_roles_block__', None):
+ break
+
+ # Loop control
+ inner = getattr(inner_obj, 'aq_inner', inner_obj)
+ parent = getattr(inner, 'aq_parent', None)
+ if parent is not None:
+ inner_obj = parent
+ continue
+ if hasattr(inner_obj, 'im_self'):
+ inner_obj=inner_obj.im_self
+ inner_obj=getattr(inner_obj, 'aq_inner', inner_obj)
+ continue
+ break
+ return None
+
+
+ security.declarePublic('hasRole')
+ def hasRole(self, *args, **kw):
+ """hasRole is an alias for 'allowed' and has been deprecated.
+
+ Code still using this method should convert to either 'has_role' or
+ 'allowed', depending on the intended behaviour.
+
+ """
+ import warnings
+ warnings.warn('BasicUser.hasRole is deprecated, please use '
+ 'BasicUser.allowed instead; hasRole was an alias for allowed, but '
+ 'you may have ment to use has_role.', DeprecationWarning)
+ return self.allowed(*args, **kw)
+
+ # #
+ # Underlying user object support #
+ # #
+
+ def __getattr__(self, name):
+ # This will call the underlying object's methods
+ # if they are not found in this user object.
+ # We will have to check Chris' http://www.plope.com/Members/chrism/plone_on_zope_head
+ # to make it work with Zope HEAD.
+ ret = getattr(self.__dict__['__underlying__'], name)
+ return ret
+
+ security.declarePublic('getUnwrappedUser')
+ def getUnwrappedUser(self,):
+ """
+ same as GRUF.getUnwrappedUser, but implicitly with this particular user
+ """
+ return self.__dict__['__underlying__']
+
+ def __getitem__(self, name):
+ # This will call the underlying object's methods
+ # if they are not found in this user object.
+ return self.__underlying__[name]
+
+ # #
+ # HTML link support #
+ # #
+
+ def asHTML(self, implicit=0):
+ """
+ asHTML(self, implicit=0) => HTML string
+ Used to generate homogeneous links for management screens
+ """
+ acl_users = self.acl_users
+ if self.isGroup():
+ color = acl_users.group_color
+ kind = "Group"
+ else:
+ color = acl_users.user_color
+ kind = "User"
+
+ ret = '''<a href="%(href)s" alt="%(alt)s"><font color="%(color)s">%(name)s</font></a>''' % {
+ "color": color,
+ "href": "%s/%s/manage_workspace?FORCE_USER=1" % (acl_users.absolute_url(), self.getId(), ),
+ "name": self.getUserNameWithoutGroupPrefix(),
+ "alt": "%s (%s)" % (self.getUserNameWithoutGroupPrefix(), kind, ),
+ }
+ if implicit:
+ return "<i>%s</i>" % ret
+ return ret
+
+
+ security.declarePrivate("isInGroup")
+ def isInGroup(self, groupid):
+ """Return true if the user is member of the specified group id
+ (including transitive groups)"""
+ return groupid in self.getAllGroupIds()
+
+ security.declarePublic("getRealId")
+ def getRealId(self,):
+ """Return id WITHOUT group prefix
+ """
+ raise NotImplementedError, "Must be derived in subclasses"
+
+
+class GRUFUser(GRUFUserAtom):
+ """
+ This is the class for actual user objects
+ """
+ __implements__ = (IUser, )
+
+ security = ClassSecurityInfo()
+
+ # #
+ # User Mutation #
+ # #
+
+ security.declarePublic('changePassword')
+ def changePassword(self, password, REQUEST=None):
+ """Set the user's password. This method performs its own security checks"""
+ # Check security
+ user = getSecurityManager().getUser()
+ if not user.has_permission(Permissions.manage_users, self._GRUF): # Is manager ?
+ if user.__class__.__name__ != "GRUFUser":
+ raise "Unauthorized", "You cannot change someone else's password."
+ if not user.getId() == self.getId(): # Is myself ?
+ raise "Unauthorized", "You cannot change someone else's password."
+
+ # Just do it
+ self.clearCachedGroupsAndRoles()
+ return self._GRUF.userSetPassword(self.getId(), password)
+ changePassword = postonly(changePassword)
+
+ security.declarePrivate("setRoles")
+ def setRoles(self, roles):
+ """Change the roles of a user atom.
+ """
+ self.clearCachedGroupsAndRoles()
+ return self._GRUF.userSetRoles(self.getId(), roles)
+
+ security.declarePrivate("addRole")
+ def addRole(self, role):
+ """Append a role for a user atom
+ """
+ self.clearCachedGroupsAndRoles()
+ return self._GRUF.userAddRole(self.getId(), role)
+
+ security.declarePrivate("removeRole")
+ def removeRole(self, role):
+ """Remove the role of a user atom
+ """
+ self.clearCachedGroupsAndRoles()
+ return self._GRUF.userRemoveRole(self.getId(), role)
+
+ security.declarePrivate("setPassword")
+ def setPassword(self, newPassword):
+ """Set the password of a user
+ """
+ self.clearCachedGroupsAndRoles()
+ return self._GRUF.userSetPassword(self.getId(), newPassword)
+
+ security.declarePrivate("setDomains")
+ def setDomains(self, domains):
+ """Set domains for a user
+ """
+ self.clearCachedGroupsAndRoles()
+ self._GRUF.userSetDomains(self.getId(), domains)
+ self._original_domains = self._GRUF.userGetDomains(self.getId())
+
+ security.declarePrivate("addDomain")
+ def addDomain(self, domain):
+ """Append a domain to a user
+ """
+ self.clearCachedGroupsAndRoles()
+ self._GRUF.userAddDomain(self.getId(), domain)
+ self._original_domains = self._GRUF.userGetDomains(self.getId())
+
+ security.declarePrivate("removeDomain")
+ def removeDomain(self, domain):
+ """Remove a domain from a user
+ """
+ self.clearCachedGroupsAndRoles()
+ self._GRUF.userRemoveDomain(self.getId(), domain)
+ self._original_domains = self._GRUF.userGetDomains(self.getId())
+
+ security.declarePrivate("setGroups")
+ def setGroups(self, groupnames):
+ """Set the groups of a user
+ """
+ self.clearCachedGroupsAndRoles()
+ return self._GRUF.userSetGroups(self.getId(), groupnames)
+
+ security.declarePrivate("addGroup")
+ def addGroup(self, groupname):
+ """add a group to a user atom
+ """
+ self.clearCachedGroupsAndRoles()
+ return self._GRUF.userAddGroup(self.getId(), groupname)
+
+ security.declarePrivate("removeGroup")
+ def removeGroup(self, groupname):
+ """remove a group from a user atom.
+ """
+ self.clearCachedGroupsAndRoles()
+ return self._GRUF.userRemoveGroup(self.getId(), groupname)
+
+ security.declarePrivate('_getPassword')
+ def _getPassword(self):
+ """Return the password of the user."""
+ return self._original_password
+
+ security.declarePublic("getRealId")
+ def getRealId(self,):
+ """Return id WITHOUT group prefix
+ """
+ return self.getId()
+
+
+class GRUFGroup(GRUFUserAtom):
+ """
+ This is the class for actual group objects
+ """
+ __implements__ = (IGroup, )
+
+ security = ClassSecurityInfo()
+
+ security.declarePublic("getRealId")
+ def getRealId(self,):
+ """Return group id WITHOUT group prefix
+ """
+ return self.getId()[len(GROUP_PREFIX):]
+
+ def _getLDAPMemberIds(self,):
+ """
+ _getLDAPMemberIds(self,) => Uses LDAPUserFolder to find
+ users in a group.
+ """
+ # Find the right source
+ gruf = self.aq_parent
+ src = None
+ for src in gruf.listUserSources():
+ if not src.meta_type == "LDAPUserFolder":
+ continue
+ if src is None:
+ Log(LOG_DEBUG, "No LDAPUserFolder source found")
+ return []
+
+ # Find the group in LDAP
+ groups = src.getGroups()
+ groupid = self.getId()
+ grp = [ group for group in groups if group[0] == self.getId() ]
+ if not grp:
+ Log(LOG_DEBUG, "No such group ('%s') found." % (groupid,))
+ return []
+
+ # Return the grup member ids
+ userids = src.getGroupedUsers(grp)
+ Log(LOG_DEBUG, "We've found %d users belonging to the group '%s'" % (len(userids), grp), )
+ return userids
+
+ def _getMemberIds(self, users = 1, groups = 1, transitive = 1, ):
+ """
+ Return the member ids (users and groups) of the atoms of this group.
+ Transitiveness attribute is ignored with LDAP (no nested groups with
+ LDAP anyway).
+ This method now uses a shortcut to fetch members of an LDAP group
+ (stored either within Zope or within your LDAP server)
+ """
+ # Initial parameters.
+ # We fetch the users/groups list depending on what we search,
+ # and carefuly avoiding to use LDAP sources.
+ gruf = self.aq_parent
+ ldap_sources = []
+ lst = []
+ if transitive:
+ method = "getAllGroupIds"
+ else:
+ method = "getGroupIds"
+ if users:
+ for src in gruf.listUserSources():
+ if src.meta_type == 'LDAPUserFolder':
+ ldap_sources.append(src)
+ continue # We'll fetch 'em later
+ lst.extend(src.getUserNames())
+ if groups:
+ lst.extend(gruf.getGroupIds())
+
+ # First extraction for regular user sources.
+ # This part is very very long, and the more users you have,
+ # the longer this method will be.
+ groupid = self.getId()
+ groups_mapping = {}
+ for u in lst:
+ usr = gruf.getUser(u)
+ if not usr:
+ groups_mapping[u] = []
+ Log(LOG_WARNING, "Invalid user retreiving:", u)
+ else:
+ groups_mapping[u] = getattr(usr, method)()
+ members = [u for u in lst if groupid in groups_mapping[u]]
+
+ # If we have LDAP sources, we fetch user-group mapping inside directly
+ groupid = self.getId()
+ for src in ldap_sources:
+ groups = src.getGroups()
+ # With LDAPUserFolder >= 2.7 we need to add GROUP_PREFIX to group_name
+ # We keep backward compatibility
+ grp = [ group for group in groups if group[0] == self.getId() or \
+ GROUP_PREFIX + group[0] == self.getId()]
+ if not grp:
+ Log(LOG_DEBUG, "No such group ('%s') found." % (groupid,))
+ continue
+
+ # Return the grup member ids
+ userids = [ str(u) for u in src.getGroupedUsers(grp) ]
+ Log(LOG_DEBUG, "We've found %d users belonging to the group '%s'" % (len(userids), grp), )
+ members.extend(userids)
+
+ # Return the members we've found
+ return members
+
+ security.declarePrivate("getMemberIds")
+ def getMemberIds(self, transitive = 1, ):
+ "Return member ids of this group, including or not transitive groups."
+ return self._getMemberIds(transitive = transitive)
+
+ security.declarePrivate("getUserMemberIds")
+ def getUserMemberIds(self, transitive = 1, ):
+ """Return the member ids (users only) of the users of this group"""
+ return self._getMemberIds(groups = 0, transitive = transitive)
+
+ security.declarePrivate("getGroupMemberIds")
+ def getGroupMemberIds(self, transitive = 1, ):
+ """Return the members ids (groups only) of the groups of this group"""
+ return self._getMemberIds(users = 0, transitive = transitive)
+
+ security.declarePrivate("hasMember")
+ def hasMember(self, id):
+ """Return true if the specified atom id is in the group.
+ This is the contrary of IUserAtom.isInGroup(groupid)"""
+ gruf = self.aq_parent
+ return id in gruf.getMemberIds(self.getId())
+
+ security.declarePrivate("addMember")
+ def addMember(self, userid):
+ """Add a user the the current group"""
+ gruf = self.aq_parent
+ groupid = self.getId()
+ usr = gruf.getUser(userid)
+ if not usr:
+ raise ValueError, "Invalid user: '%s'" % (userid, )
+ if not groupid in gruf.getGroupNames() + gruf.getGroupIds():
+ raise ValueError, "Invalid group: '%s'" % (groupid, )
+ groups = list(usr.getGroups())
+ groups.append(groupid)
+ groups = GroupUserFolder.unique(groups)
+ return gruf._updateUser(userid, groups = groups)
+
+ security.declarePrivate("removeMember")
+ def removeMember(self, userid):
+ """Remove a user from the current group"""
+ gruf = self.aq_parent
+ groupid = self.getId()
+
+ # Check the user
+ usr = gruf.getUser(userid)
+ if not usr:
+ raise ValueError, "Invalid user: '%s'" % (userid, )
+
+ # Now, remove the group
+ groups = list(usr.getImmediateGroups())
+ if groupid in groups:
+ groups.remove(groupid)
+ gruf._updateUser(userid, groups = groups)
+ else:
+ raise ValueError, "User '%s' doesn't belong to group '%s'" % (userid, groupid, )
+
+ security.declarePrivate("setMembers")
+ def setMembers(self, userids):
+ """Set the members of the group
+ """
+ member_ids = self.getMemberIds()
+ all_ids = copy(member_ids)
+ all_ids.extend(userids)
+ groupid = self.getId()
+ for id in all_ids:
+ if id in member_ids and id not in userids:
+ self.removeMember(id)
+ elif id not in member_ids and id in userids:
+ self.addMember(id)
+
+
+InitializeClass(GRUFUser)
+InitializeClass(GRUFGroup)