1 # -*- coding: utf-8 -*-
3 ## Copyright (C)2006 Ingeniweb
5 ## This program is free software; you can redistribute it and/or modify
6 ## it under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 2 of the License, or
8 ## (at your option) any later version.
10 ## This program is distributed in the hope that it will be useful,
11 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ## GNU General Public License for more details.
15 ## You should have received a copy of the GNU General Public License
16 ## along with this program; see the file COPYING. If not, write to the
17 ## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 __version__
= "$Revision: $"
23 # $Id: GRUFUser.py 40118 2007-04-01 15:13:44Z alecm $
24 __docformat__
= 'restructuredtext'
28 # fakes a method from a DTML File
29 from Globals
import MessageDialog
, DTMLFile
31 from AccessControl
import ClassSecurityInfo
32 from AccessControl
import Permissions
33 from AccessControl
import getSecurityManager
34 from Globals
import InitializeClass
35 from Acquisition
import Implicit
, aq_inner
, aq_parent
, aq_base
36 from Globals
import Persistent
37 from AccessControl
.Role
import RoleManager
38 from OFS
.SimpleItem
import Item
39 from OFS
.PropertyManager
import PropertyManager
40 from OFS
import ObjectManager
, SimpleItem
41 from DateTime
import DateTime
42 from App
import ImageFile
43 import AccessControl
.Role
, webdav
.Collection
49 from global_symbols
import *
51 from Products
.GroupUserFolder
import postonly
53 import GroupUserFolder
54 from AccessControl
.PermissionRole \
55 import _what_not_even_god_should_do
, rolesForPermissionOn
56 from ComputedAttribute
import ComputedAttribute
62 from interfaces
.IUserFolder
import IUser
, IGroup
64 _marker
= ['INVALID_VALUE']
66 # NOTE : _what_not_even_god_should_do is a specific permission defined by ZOPE
67 # that indicates that something has not to be done within Zope.
68 # This value is given to the ACCESS_NONE directive of a SecurityPolicy.
69 # It's rarely used within Zope BUT as it is documented (in AccessControl)
70 # and may be used by third-party products, we have to process it.
73 #GROUP_PREFIX is a constant
75 class GRUFUserAtom(AccessControl
.User
.BasicUser
, Implicit
):
77 Base class for all GRUF-catched User objects.
78 There's, alas, many copy/paste from AccessControl.BasicUser...
80 security
= ClassSecurityInfo()
82 security
.declarePrivate('_setUnderlying')
83 def _setUnderlying(self
, user
):
85 _setUnderlying(self, user) => Set the GRUFUser properties to
86 the underlying user's one.
87 Be careful that any change to the underlying user won't be
88 reported here. $$$ We don't know yet if User object are
89 transaction-persistant or not...
91 self
._original
_name
= user
.getUserName()
92 self
._original
_password
= user
._getPassword
()
93 self
._original
_roles
= user
.getRoles()
94 self
._original
_domains
= user
.getDomains()
95 self
._original
_id
= user
.getId()
96 self
.__underlying
__ = user
# Used for authenticate() and __getattr__
99 # ----------------------------
100 # Public User object interface
101 # ----------------------------
103 # Maybe allow access to unprotected attributes. Note that this is
104 # temporary to avoid exposing information but without breaking
105 # everyone's current code. In the future the security will be
106 # clamped down and permission-protected here. Because there are a
107 # fair number of user object types out there, this method denies
108 # access to names that are private parts of the standard User
109 # interface or implementation only. The other approach (only
110 # allowing access to public names in the User interface) would
111 # probably break a lot of other User implementations with extended
112 # functionality that we cant anticipate from the base scaffolding.
114 security
.declarePrivate('__init__')
115 def __init__(self
, underlying_user
, GRUF
, isGroup
, source_id
, ):
116 # When calling, set isGroup it to TRUE if this user represents a group
117 self
._setUnderlying
(underlying_user
)
118 self
._isGroup
= isGroup
120 self
._source
_id
= source_id
121 self
.id = self
._original
_id
122 # Store the results of getRoles and getGroups. Initially set to None,
123 # set to a list after the methods are first called.
124 # If you are caching users you want to clear these.
125 self
.clearCachedGroupsAndRoles()
127 security
.declarePrivate('clearCachedGroupsAndRoles')
128 def clearCachedGroupsAndRoles(self
, underlying_user
= None):
130 self
._user
_roles
= None
131 self
._group
_roles
= None
132 self
._all
_roles
= None
134 self
._setUnderlying
(underlying_user
)
135 self
._original
_user
_roles
= None
137 security
.declarePublic('isGroup')
139 """Return 1 if this user is a group abstraction"""
142 security
.declarePublic('getUserSourceId')
143 def getUserSourceId(self
,):
145 getUserSourceId(self,) => string
146 Return the GRUF's GRUFUsers folder used to fetch this user.
148 return self
._source
_id
150 security
.declarePrivate('getGroupNames')
151 def getGroupNames(self
,):
153 ret
= self
._getGroups
(no_recurse
= 1)
154 return map(lambda x
: x
[GROUP_PREFIX_LEN
:], ret
)
156 security
.declarePrivate('getGroupIds')
157 def getGroupIds(self
,):
159 return list(self
._getGroups
(no_recurse
= 1))
161 security
.declarePrivate("getAllGroups")
162 def getAllGroups(self
,):
163 """Same as getAllGroupNames()"""
164 return self
.getAllGroupIds()
166 security
.declarePrivate('getAllGroupNames')
167 def getAllGroupNames(self
,):
169 ret
= self
._getGroups
()
170 return map(lambda x
: x
[GROUP_PREFIX_LEN
:], ret
)
172 security
.declarePrivate('getAllGroupIds')
173 def getAllGroupIds(self
,):
175 return list(self
._getGroups
())
177 security
.declarePrivate('getGroups')
178 def getGroups(self
, *args
, **kw
):
180 ret
= self
._getGroups
(*args
, **kw
)
183 security
.declarePrivate("getImmediateGroups")
184 def getImmediateGroups(self
,):
186 Return NON-TRANSITIVE groups
188 ret
= self
._getGroups
(no_recurse
= 1)
191 def _getGroups(self
, no_recurse
= 0, already_done
= None, prefix
= GROUP_PREFIX
):
193 getGroups(self, no_recurse = 0, already_done = None, prefix = GROUP_PREFIX) => list of strings
195 If this user is a user (uh, uh), get its groups.
196 THIS METHODS NOW SUPPORTS NESTED GROUPS ! :-)
197 The already_done parameter prevents infite recursions.
198 Keep it as it is, never give it a value.
200 If no_recurse is true, return only first level groups
202 This method is private and should remain so.
204 if already_done
is None:
207 # List this user's roles. We consider that roles starting
208 # with GROUP_PREFIX are in fact groups, and thus are
209 # returned (prefixed).
210 if self
._groups
is not None:
213 # Populate cache if necessary
214 if self
._original
_user
_roles
is None:
215 self
._original
_user
_roles
= self
.__underlying
__.getRoles()
217 # Scan roles to find groups
219 for role
in self
._original
_user
_roles
:
220 # Inspect group-like roles
221 if role
.startswith(prefix
):
223 # Prevent infinite recursion
224 if self
._isGroup
and role
in already_done
:
227 # Get the underlying group
228 grp
= self
.aq_parent
.getUser(role
)
230 continue # Invalid group
232 # Do not add twice the current group
236 # Append its nested groups (if recurse is asked)
240 for extend
in grp
.getGroups(already_done
= ret
):
241 if not extend
in ret
:
245 self
._groups
= tuple(ret
)
249 security
.declarePrivate('getGroupsWithoutPrefix')
250 def getGroupsWithoutPrefix(self
, **kw
):
252 Same as getGroups but return them without a prefix.
255 for group
in self
.getGroups(**kw
):
256 if group
.startswith(GROUP_PREFIX
):
257 ret
.append(group
[len(GROUP_PREFIX
):])
260 security
.declarePublic('getUserNameWithoutGroupPrefix')
261 def getUserNameWithoutGroupPrefix(self
):
262 """Return the username of a user without a group prefix"""
263 if self
.isGroup() and \
264 self
._original
_name
[:len(GROUP_PREFIX
)] == GROUP_PREFIX
:
265 return self
._original
_name
[len(GROUP_PREFIX
):]
266 return self
._original
_name
268 security
.declarePublic('getUserId')
270 """Return the user id of a user"""
271 if self
.isGroup() and \
272 not self
._original
_name
[:len(GROUP_PREFIX
)] == GROUP_PREFIX
:
273 return "%s%s" % (GROUP_PREFIX
, self
._original
_name
)
274 return self
._original
_name
276 security
.declarePublic("getName")
278 """Get user's or group's name.
279 For a user, the name can be set by the underlying user folder but usually id == name.
280 For a group, the ID is prefixed, but the NAME is NOT prefixed by 'group_'.
282 return self
.getUserNameWithoutGroupPrefix()
284 security
.declarePublic("getUserName")
285 def getUserName(self
,):
286 """Alias for getName()"""
287 return self
.getUserNameWithoutGroupPrefix()
289 security
.declarePublic('getId')
290 def getId(self
, unprefixed
= 0):
291 """Get the ID of the user. The ID can be used, at least from
292 Python, to get the user from the user's UserDatabase
294 # Return the right id
295 if self
.isGroup() and not self
._original
_name
.startswith(GROUP_PREFIX
) and not unprefixed
:
296 return "%s%s" % (GROUP_PREFIX
, self
._original
_name
)
297 return self
._original
_name
299 security
.declarePublic('getRoles')
302 Return the list (tuple) of roles assigned to a user.
303 THIS IS WHERE THE ATHENIANS REACHED !
305 if self
._all
_roles
is not None:
306 return self
._all
_roles
308 # Return user and groups roles
309 self
._all
_roles
= GroupUserFolder
.unique(self
.getUserRoles() + self
.getGroupRoles())
310 return self
._all
_roles
312 security
.declarePublic('getUserRoles')
313 def getUserRoles(self
):
315 returns the roles defined for the user without the group roles
317 if self
._user
_roles
is not None:
318 return self
._user
_roles
319 prefix
= GROUP_PREFIX
320 if self
._original
_user
_roles
is None:
321 self
._original
_user
_roles
= self
.__underlying
__.getRoles()
322 self
._user
_roles
= tuple([r
for r
in self
._original
_user
_roles
if not r
.startswith(prefix
)])
323 return self
._user
_roles
325 security
.declarePublic("getGroupRoles")
326 def getGroupRoles(self
,):
328 Return the tuple of roles belonging to this user's group(s)
330 if self
._group
_roles
is not None:
331 return self
._group
_roles
333 acl_users
= self
._GRUF
.acl_users
334 groups
= acl_users
.getGroupIds() # XXX We can have a cache here
336 for group
in self
.getGroups():
337 if not group
in groups
:
338 Log("Group", group
, "is invalid. Ignoring.")
339 # This may occur when groups are deleted
342 ret
.extend(acl_users
.getGroup(group
).getUserRoles())
344 self
._group
_roles
= GroupUserFolder
.unique(ret
)
345 return self
._group
_roles
347 security
.declarePublic('getRolesInContext')
348 def getRolesInContext(self
, object, userid
= None):
350 Return the list of roles assigned to the user,
351 including local roles assigned in context of
352 the passed in object.
358 for role
in self
.getRoles():
361 user_groups
= self
.getGroups()
363 inner_obj
= getattr(object, 'aq_inner', object)
365 # Usual local roles retreiving
366 local_roles
= getattr(inner_obj
, '__ac_local_roles__', None)
368 if callable(local_roles
):
369 local_roles
= local_roles()
370 dict = local_roles
or {}
372 for role
in dict.get(userid
, []):
375 # Get roles & local roles for groups
376 # This handles nested groups as well
377 for groupid
in user_groups
:
378 for role
in dict.get(groupid
, []):
382 obj
= getattr(inner_obj
, 'aq_base', inner_obj
)
383 if getattr(obj
, '__ac_local_roles_block__', None):
387 inner
= getattr(inner_obj
, 'aq_inner', inner_obj
)
388 parent
= getattr(inner
, 'aq_parent', None)
389 if parent
is not None:
392 if hasattr(inner_obj
, 'im_self'):
393 inner_obj
=inner_obj
.im_self
394 inner_obj
=getattr(inner_obj
, 'aq_inner', inner_obj
)
398 return tuple(roles
.keys())
400 security
.declarePublic('getDomains')
401 def getDomains(self
):
402 """Return the list of domain restrictions for a user"""
403 return self
._original
_domains
406 security
.declarePrivate("getProperty")
407 def getProperty(self
, name
, default
=_marker
):
408 """getProperty(self, name) => return property value or raise AttributeError
410 # Try to do an attribute lookup on the underlying user object
411 v
= getattr(self
.__underlying
__, name
, default
)
413 raise AttributeError, name
416 security
.declarePrivate("hasProperty")
417 def hasProperty(self
, name
):
419 return hasattr(self
.__underlying
__, name
)
421 security
.declarePrivate("setProperty")
422 def setProperty(self
, name
, value
):
423 """setProperty => Try to set the property...
424 By now, it's available only for LDAPUserFolder
427 src
= self
._GRUF
.getUserSource(self
.getUserSourceId())
429 raise RuntimeError, "Invalid or missing user source for '%s'." % (self
.getId(),)
431 # LDAPUserFolder => specific API.
432 if hasattr(src
, "manage_setUserProperty"):
433 # Unmap pty name if necessary, get it in the schema
435 for schema
in src
.getSchemaConfig().values():
436 if schema
["ldap_name"] == name
:
437 ldapname
= schema
["ldap_name"]
438 if schema
["public_name"] == name
:
439 ldapname
= schema
["ldap_name"]
442 # If we didn't find it, we skip it
444 raise KeyError, "Invalid LDAP attribute: '%s'." % (name
, )
447 user_dn
= src
._find
_user
_dn
(self
.getUserName())
448 src
.manage_setUserProperty(user_dn
, ldapname
, value
)
450 # Expire the underlying user object
451 self
.__underlying
__ = src
.getUser(self
.getId())
452 if not self
.__underlying
__:
453 raise RuntimeError, "Error while setting property of '%s'." % (self
.getId(),)
455 # Now we check if the property has been changed
456 if not self
.hasProperty(name
):
457 raise NotImplementedError, "Property setting is not supported for '%s'." % (name
,)
458 v
= self
._GRUF
.getUserById(self
.getId()).getProperty(name
)
460 Log(LOG_DEBUG
, "Property '%s' for user '%s' should be '%s' and not '%s'" % (
461 name
, self
.getId(), value
, v
,
463 raise NotImplementedError, "Property setting is not supported for '%s'." % (name
,)
465 # ------------------------------
466 # Internal User object interface
467 # ------------------------------
469 security
.declarePrivate('authenticate')
470 def authenticate(self
, password
, request
):
471 # We prevent groups from authenticating
474 return self
.__underlying
__.authenticate(password
, request
)
477 security
.declarePublic('allowed')
478 def allowed(self
, object, object_roles
=None):
479 """Check whether the user has access to object. The user must
480 have one of the roles in object_roles to allow access."""
482 if object_roles
is _what_not_even_god_should_do
:
485 # Short-circuit the common case of anonymous access.
486 if object_roles
is None or 'Anonymous' in object_roles
:
489 # Provide short-cut access if object is protected by 'Authenticated'
490 # role and user is not nobody
491 if 'Authenticated' in object_roles
and \
492 (self
.getUserName() != 'Anonymous User'):
495 # Check for ancient role data up front, convert if found.
496 # This should almost never happen, and should probably be
497 # deprecated at some point.
498 if 'Shared' in object_roles
:
499 object_roles
= self
._shared
_roles
(object)
500 if object_roles
is None or 'Anonymous' in object_roles
:
504 # Trying to make some speed improvements, changes starts here.
505 # Helge Tesdal, Plone Solutions AS, http://www.plonesolutions.com
506 # We avoid using the getRoles() and getRolesInContext() methods to be able
509 # Dict for faster lookup and avoiding duplicates
510 object_roles_dict
= {}
511 for role
in object_roles
:
512 object_roles_dict
[role
] = 1
514 if [role
for role
in self
.getUserRoles() if object_roles_dict
.has_key(role
)]:
515 if self
._check
_context
(object):
519 # Try the top level group roles.
520 if [role
for role
in self
.getGroupRoles() if object_roles_dict
.has_key(role
)]:
521 if self
._check
_context
(object):
525 user_groups
= self
.getGroups()
526 # No luck on the top level, try local roles
527 inner_obj
= getattr(object, 'aq_inner', object)
528 userid
= self
.getId()
530 local_roles
= getattr(inner_obj
, '__ac_local_roles__', None)
532 if callable(local_roles
):
533 local_roles
= local_roles()
534 dict = local_roles
or {}
536 if [role
for role
in dict.get(userid
, []) if object_roles_dict
.has_key(role
)]:
537 if self
._check
_context
(object):
541 # Get roles & local roles for groups
542 # This handles nested groups as well
543 for groupid
in user_groups
:
544 if [role
for role
in dict.get(groupid
, []) if object_roles_dict
.has_key(role
)]:
545 if self
._check
_context
(object):
550 obj
= getattr(inner_obj
, 'aq_base', inner_obj
)
551 if getattr(obj
, '__ac_local_roles_block__', None):
555 inner
= getattr(inner_obj
, 'aq_inner', inner_obj
)
556 parent
= getattr(inner
, 'aq_parent', None)
557 if parent
is not None:
560 if hasattr(inner_obj
, 'im_self'):
561 inner_obj
=inner_obj
.im_self
562 inner_obj
=getattr(inner_obj
, 'aq_inner', inner_obj
)
568 security
.declarePublic('hasRole')
569 def hasRole(self
, *args
, **kw
):
570 """hasRole is an alias for 'allowed' and has been deprecated.
572 Code still using this method should convert to either 'has_role' or
573 'allowed', depending on the intended behaviour.
577 warnings
.warn('BasicUser.hasRole is deprecated, please use '
578 'BasicUser.allowed instead; hasRole was an alias for allowed, but '
579 'you may have ment to use has_role.', DeprecationWarning)
580 return self
.allowed(*args
, **kw
)
583 # Underlying user object support #
586 def __getattr__(self
, name
):
587 # This will call the underlying object's methods
588 # if they are not found in this user object.
589 # We will have to check Chris' http://www.plope.com/Members/chrism/plone_on_zope_head
590 # to make it work with Zope HEAD.
591 ret
= getattr(self
.__dict
__['__underlying__'], name
)
594 security
.declarePublic('getUnwrappedUser')
595 def getUnwrappedUser(self
,):
597 same as GRUF.getUnwrappedUser, but implicitly with this particular user
599 return self
.__dict
__['__underlying__']
601 def __getitem__(self
, name
):
602 # This will call the underlying object's methods
603 # if they are not found in this user object.
604 return self
.__underlying
__[name
]
607 # HTML link support #
610 def asHTML(self
, implicit
=0):
612 asHTML(self, implicit=0) => HTML string
613 Used to generate homogeneous links for management screens
615 acl_users
= self
.acl_users
617 color
= acl_users
.group_color
620 color
= acl_users
.user_color
623 ret
= '''<a href="%(href)s" alt="%(alt)s"><font color="%(color)s">%(name)s</font></a>''' % {
625 "href": "%s/%s/manage_workspace?FORCE_USER=1" % (acl_users
.absolute_url(), self
.getId(), ),
626 "name": self
.getUserNameWithoutGroupPrefix(),
627 "alt": "%s (%s)" % (self
.getUserNameWithoutGroupPrefix(), kind
, ),
630 return "<i>%s</i>" % ret
634 security
.declarePrivate("isInGroup")
635 def isInGroup(self
, groupid
):
636 """Return true if the user is member of the specified group id
637 (including transitive groups)"""
638 return groupid
in self
.getAllGroupIds()
640 security
.declarePublic("getRealId")
641 def getRealId(self
,):
642 """Return id WITHOUT group prefix
644 raise NotImplementedError, "Must be derived in subclasses"
647 class GRUFUser(GRUFUserAtom
):
649 This is the class for actual user objects
651 __implements__
= (IUser
, )
653 security
= ClassSecurityInfo()
659 security
.declarePublic('changePassword')
660 def changePassword(self
, password
, REQUEST
=None):
661 """Set the user's password. This method performs its own security checks"""
663 user
= getSecurityManager().getUser()
664 if not user
.has_permission(Permissions
.manage_users
, self
._GRUF
): # Is manager ?
665 if user
.__class
__.__name
__ != "GRUFUser":
666 raise "Unauthorized", "You cannot change someone else's password."
667 if not user
.getId() == self
.getId(): # Is myself ?
668 raise "Unauthorized", "You cannot change someone else's password."
671 self
.clearCachedGroupsAndRoles()
672 return self
._GRUF
.userSetPassword(self
.getId(), password
)
673 changePassword
= postonly(changePassword
)
675 security
.declarePrivate("setRoles")
676 def setRoles(self
, roles
):
677 """Change the roles of a user atom.
679 self
.clearCachedGroupsAndRoles()
680 return self
._GRUF
.userSetRoles(self
.getId(), roles
)
682 security
.declarePrivate("addRole")
683 def addRole(self
, role
):
684 """Append a role for a user atom
686 self
.clearCachedGroupsAndRoles()
687 return self
._GRUF
.userAddRole(self
.getId(), role
)
689 security
.declarePrivate("removeRole")
690 def removeRole(self
, role
):
691 """Remove the role of a user atom
693 self
.clearCachedGroupsAndRoles()
694 return self
._GRUF
.userRemoveRole(self
.getId(), role
)
696 security
.declarePrivate("setPassword")
697 def setPassword(self
, newPassword
):
698 """Set the password of a user
700 self
.clearCachedGroupsAndRoles()
701 return self
._GRUF
.userSetPassword(self
.getId(), newPassword
)
703 security
.declarePrivate("setDomains")
704 def setDomains(self
, domains
):
705 """Set domains for a user
707 self
.clearCachedGroupsAndRoles()
708 self
._GRUF
.userSetDomains(self
.getId(), domains
)
709 self
._original
_domains
= self
._GRUF
.userGetDomains(self
.getId())
711 security
.declarePrivate("addDomain")
712 def addDomain(self
, domain
):
713 """Append a domain to a user
715 self
.clearCachedGroupsAndRoles()
716 self
._GRUF
.userAddDomain(self
.getId(), domain
)
717 self
._original
_domains
= self
._GRUF
.userGetDomains(self
.getId())
719 security
.declarePrivate("removeDomain")
720 def removeDomain(self
, domain
):
721 """Remove a domain from a user
723 self
.clearCachedGroupsAndRoles()
724 self
._GRUF
.userRemoveDomain(self
.getId(), domain
)
725 self
._original
_domains
= self
._GRUF
.userGetDomains(self
.getId())
727 security
.declarePrivate("setGroups")
728 def setGroups(self
, groupnames
):
729 """Set the groups of a user
731 self
.clearCachedGroupsAndRoles()
732 return self
._GRUF
.userSetGroups(self
.getId(), groupnames
)
734 security
.declarePrivate("addGroup")
735 def addGroup(self
, groupname
):
736 """add a group to a user atom
738 self
.clearCachedGroupsAndRoles()
739 return self
._GRUF
.userAddGroup(self
.getId(), groupname
)
741 security
.declarePrivate("removeGroup")
742 def removeGroup(self
, groupname
):
743 """remove a group from a user atom.
745 self
.clearCachedGroupsAndRoles()
746 return self
._GRUF
.userRemoveGroup(self
.getId(), groupname
)
748 security
.declarePrivate('_getPassword')
749 def _getPassword(self
):
750 """Return the password of the user."""
751 return self
._original
_password
753 security
.declarePublic("getRealId")
754 def getRealId(self
,):
755 """Return id WITHOUT group prefix
760 class GRUFGroup(GRUFUserAtom
):
762 This is the class for actual group objects
764 __implements__
= (IGroup
, )
766 security
= ClassSecurityInfo()
768 security
.declarePublic("getRealId")
769 def getRealId(self
,):
770 """Return group id WITHOUT group prefix
772 return self
.getId()[len(GROUP_PREFIX
):]
774 def _getLDAPMemberIds(self
,):
776 _getLDAPMemberIds(self,) => Uses LDAPUserFolder to find
779 # Find the right source
780 gruf
= self
.aq_parent
782 for src
in gruf
.listUserSources():
783 if not src
.meta_type
== "LDAPUserFolder":
786 Log(LOG_DEBUG
, "No LDAPUserFolder source found")
789 # Find the group in LDAP
790 groups
= src
.getGroups()
791 groupid
= self
.getId()
792 grp
= [ group
for group
in groups
if group
[0] == self
.getId() ]
794 Log(LOG_DEBUG
, "No such group ('%s') found." % (groupid
,))
797 # Return the grup member ids
798 userids
= src
.getGroupedUsers(grp
)
799 Log(LOG_DEBUG
, "We've found %d users belonging to the group '%s'" % (len(userids
), grp
), )
802 def _getMemberIds(self
, users
= 1, groups
= 1, transitive
= 1, ):
804 Return the member ids (users and groups) of the atoms of this group.
805 Transitiveness attribute is ignored with LDAP (no nested groups with
807 This method now uses a shortcut to fetch members of an LDAP group
808 (stored either within Zope or within your LDAP server)
810 # Initial parameters.
811 # We fetch the users/groups list depending on what we search,
812 # and carefuly avoiding to use LDAP sources.
813 gruf
= self
.aq_parent
817 method
= "getAllGroupIds"
819 method
= "getGroupIds"
821 for src
in gruf
.listUserSources():
822 if src
.meta_type
== 'LDAPUserFolder':
823 ldap_sources
.append(src
)
824 continue # We'll fetch 'em later
825 lst
.extend(src
.getUserNames())
827 lst
.extend(gruf
.getGroupIds())
829 # First extraction for regular user sources.
830 # This part is very very long, and the more users you have,
831 # the longer this method will be.
832 groupid
= self
.getId()
835 usr
= gruf
.getUser(u
)
837 groups_mapping
[u
] = []
838 Log(LOG_WARNING
, "Invalid user retreiving:", u
)
840 groups_mapping
[u
] = getattr(usr
, method
)()
841 members
= [u
for u
in lst
if groupid
in groups_mapping
[u
]]
843 # If we have LDAP sources, we fetch user-group mapping inside directly
844 groupid
= self
.getId()
845 for src
in ldap_sources
:
846 groups
= src
.getGroups()
847 # With LDAPUserFolder >= 2.7 we need to add GROUP_PREFIX to group_name
848 # We keep backward compatibility
849 grp
= [ group
for group
in groups
if group
[0] == self
.getId() or \
850 GROUP_PREFIX
+ group
[0] == self
.getId()]
852 Log(LOG_DEBUG
, "No such group ('%s') found." % (groupid
,))
855 # Return the grup member ids
856 userids
= [ str(u
) for u
in src
.getGroupedUsers(grp
) ]
857 Log(LOG_DEBUG
, "We've found %d users belonging to the group '%s'" % (len(userids
), grp
), )
858 members
.extend(userids
)
860 # Return the members we've found
863 security
.declarePrivate("getMemberIds")
864 def getMemberIds(self
, transitive
= 1, ):
865 "Return member ids of this group, including or not transitive groups."
866 return self
._getMemberIds
(transitive
= transitive
)
868 security
.declarePrivate("getUserMemberIds")
869 def getUserMemberIds(self
, transitive
= 1, ):
870 """Return the member ids (users only) of the users of this group"""
871 return self
._getMemberIds
(groups
= 0, transitive
= transitive
)
873 security
.declarePrivate("getGroupMemberIds")
874 def getGroupMemberIds(self
, transitive
= 1, ):
875 """Return the members ids (groups only) of the groups of this group"""
876 return self
._getMemberIds
(users
= 0, transitive
= transitive
)
878 security
.declarePrivate("hasMember")
879 def hasMember(self
, id):
880 """Return true if the specified atom id is in the group.
881 This is the contrary of IUserAtom.isInGroup(groupid)"""
882 gruf
= self
.aq_parent
883 return id in gruf
.getMemberIds(self
.getId())
885 security
.declarePrivate("addMember")
886 def addMember(self
, userid
):
887 """Add a user the the current group"""
888 gruf
= self
.aq_parent
889 groupid
= self
.getId()
890 usr
= gruf
.getUser(userid
)
892 raise ValueError, "Invalid user: '%s'" % (userid
, )
893 if not groupid
in gruf
.getGroupNames() + gruf
.getGroupIds():
894 raise ValueError, "Invalid group: '%s'" % (groupid
, )
895 groups
= list(usr
.getGroups())
896 groups
.append(groupid
)
897 groups
= GroupUserFolder
.unique(groups
)
898 return gruf
._updateUser
(userid
, groups
= groups
)
900 security
.declarePrivate("removeMember")
901 def removeMember(self
, userid
):
902 """Remove a user from the current group"""
903 gruf
= self
.aq_parent
904 groupid
= self
.getId()
907 usr
= gruf
.getUser(userid
)
909 raise ValueError, "Invalid user: '%s'" % (userid
, )
911 # Now, remove the group
912 groups
= list(usr
.getImmediateGroups())
913 if groupid
in groups
:
914 groups
.remove(groupid
)
915 gruf
._updateUser
(userid
, groups
= groups
)
917 raise ValueError, "User '%s' doesn't belong to group '%s'" % (userid
, groupid
, )
919 security
.declarePrivate("setMembers")
920 def setMembers(self
, userids
):
921 """Set the members of the group
923 member_ids
= self
.getMemberIds()
924 all_ids
= copy(member_ids
)
925 all_ids
.extend(userids
)
926 groupid
= self
.getId()
928 if id in member_ids
and id not in userids
:
929 self
.removeMember(id)
930 elif id not in member_ids
and id in userids
:
934 InitializeClass(GRUFUser
)
935 InitializeClass(GRUFGroup
)