vendor/CMF/1.6.3/CMFCore

view MembershipTool.py @ 2:4c712d7bd1d7

Added tag 1.6.3 for changeset 1babb9d61518
author Georges Racinet on purity.racinet.fr <georges@racinet.fr>
date Fri, 09 Sep 2011 12:44:00 +0200
parents
children
line source
1 ##############################################################################
2 #
3 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
4 #
5 # This software is subject to the provisions of the Zope Public License,
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 # FOR A PARTICULAR PURPOSE.
11 #
12 ##############################################################################
13 """ Basic membership tool.
15 $Id$
16 """
18 import logging
19 from AccessControl import ClassSecurityInfo
20 from AccessControl.User import nobody
21 from Acquisition import aq_base
22 from Acquisition import aq_inner
23 from Acquisition import aq_parent
24 from Globals import DTMLFile
25 from Globals import InitializeClass
26 from Globals import MessageDialog
27 from Globals import PersistentMapping
28 from OFS.Folder import Folder
29 from ZODB.POSException import ConflictError
31 from ActionProviderBase import ActionProviderBase
32 from exceptions import AccessControl_Unauthorized
33 from exceptions import BadRequest
34 from interfaces.portal_membership \
35 import portal_membership as IMembershipTool
36 from permissions import AccessContentsInformation
37 from permissions import ChangeLocalRoles
38 from permissions import ListPortalMembers
39 from permissions import ManagePortal
40 from permissions import ManageUsers
41 from permissions import SetOwnPassword
42 from permissions import View
43 from utils import _checkPermission
44 from utils import _dtmldir
45 from utils import _getAuthenticatedUser
46 from utils import getToolByName
47 from utils import UniqueObject
50 logger = logging.getLogger('CMFCore.MembershipTool')
53 class MembershipTool(UniqueObject, Folder, ActionProviderBase):
54 """ This tool accesses member data through an acl_users object.
56 It can be replaced with something that accesses member data in a
57 different way.
58 """
60 __implements__ = (IMembershipTool, ActionProviderBase.__implements__)
62 id = 'portal_membership'
63 meta_type = 'CMF Membership Tool'
64 _actions = ()
66 memberareaCreationFlag = 1
68 security = ClassSecurityInfo()
70 manage_options=( ({ 'label' : 'Configuration'
71 , 'action' : 'manage_mapRoles'
72 },) +
73 ActionProviderBase.manage_options +
74 ( { 'label' : 'Overview'
75 , 'action' : 'manage_overview'
76 },
77 ) + Folder.manage_options)
79 #
80 # ZMI methods
81 #
82 security.declareProtected(ManagePortal, 'manage_overview')
83 manage_overview = DTMLFile( 'explainMembershipTool', _dtmldir )
85 #
86 # 'portal_membership' interface methods
87 #
88 security.declareProtected(ManagePortal, 'manage_mapRoles')
89 manage_mapRoles = DTMLFile('membershipRolemapping', _dtmldir )
91 security.declareProtected(SetOwnPassword, 'setPassword')
92 def setPassword(self, password, domains=None):
93 '''Allows the authenticated member to set his/her own password.
94 '''
95 registration = getToolByName(self, 'portal_registration', None)
96 if not self.isAnonymousUser():
97 member = self.getAuthenticatedMember()
98 if registration:
99 failMessage = registration.testPasswordValidity(password)
100 if failMessage is not None:
101 raise BadRequest(failMessage)
102 member.setSecurityProfile(password=password, domains=domains)
103 else:
104 raise BadRequest('Not logged in.')
106 security.declarePublic('getAuthenticatedMember')
107 def getAuthenticatedMember(self):
108 '''
109 Returns the currently authenticated member object
110 or the Anonymous User. Never returns None.
111 '''
112 u = _getAuthenticatedUser(self)
113 if u is None:
114 u = nobody
115 return self.wrapUser(u)
117 security.declarePrivate('wrapUser')
118 def wrapUser(self, u, wrap_anon=0):
119 """ Set up the correct acquisition wrappers for a user object.
121 Provides an opportunity for a portal_memberdata tool to retrieve and
122 store member data independently of the user object.
123 """
124 b = getattr(u, 'aq_base', None)
125 if b is None:
126 # u isn't wrapped at all. Wrap it in self.acl_users.
127 b = u
128 u = u.__of__(self.acl_users)
129 if (b is nobody and not wrap_anon) or hasattr(b, 'getMemberId'):
130 # This user is either not recognized by acl_users or it is
131 # already registered with something that implements the
132 # member data tool at least partially.
133 return u
135 # Apply any role mapping if we have it
136 if hasattr(self, 'role_map'):
137 for portal_role in self.role_map.keys():
138 if (self.role_map.get(portal_role) in u.roles and
139 portal_role not in u.roles):
140 u.roles.append(portal_role)
142 mdtool = getToolByName(self, 'portal_memberdata', None)
143 if mdtool is not None:
144 try:
145 u = mdtool.wrapUser(u)
146 except ConflictError:
147 raise
148 except:
149 logger.error("Error during wrapUser", exc_info=True)
150 return u
152 security.declareProtected(ManagePortal, 'getPortalRoles')
153 def getPortalRoles(self):
154 """
155 Return all local roles defined by the portal itself,
156 which means roles that are useful and understood
157 by the portal object
158 """
159 parent = self.aq_inner.aq_parent
160 roles = list( parent.userdefined_roles() )
162 # This is *not* a local role in the portal but used by it
163 roles.append('Manager')
164 roles.append('Owner')
166 return roles
168 security.declareProtected(ManagePortal, 'setRoleMapping')
169 def setRoleMapping(self, portal_role, userfolder_role):
170 """
171 set the mapping of roles between roles understood by
172 the portal and roles coming from outside user sources
173 """
174 if not hasattr(self, 'role_map'): self.role_map = PersistentMapping()
176 if len(userfolder_role) < 1:
177 del self.role_map[portal_role]
178 else:
179 self.role_map[portal_role] = userfolder_role
181 return MessageDialog(
182 title ='Mapping updated',
183 message='The Role mappings have been updated',
184 action ='manage_mapRoles')
186 security.declareProtected(ManagePortal, 'getMappedRole')
187 def getMappedRole(self, portal_role):
188 """
189 returns a role name if the portal role is mapped to
190 something else or an empty string if it is not
191 """
192 if hasattr(self, 'role_map'):
193 return self.role_map.get(portal_role, '')
194 else:
195 return ''
197 security.declarePublic('getMembersFolder')
198 def getMembersFolder(self):
199 """ Get the members folder object.
200 """
201 parent = aq_parent( aq_inner(self) )
202 members = getattr(parent, 'Members', None)
203 return members
205 security.declareProtected(ManagePortal, 'getMemberareaCreationFlag')
206 def getMemberareaCreationFlag(self):
207 """
208 Returns the flag indicating whether the membership tool
209 will create a member area if an authenticated user from
210 an underlying user folder logs in first without going
211 through the join process
212 """
213 return self.memberareaCreationFlag
215 security.declareProtected(ManagePortal, 'setMemberareaCreationFlag')
216 def setMemberareaCreationFlag(self):
217 """
218 sets the flag indicating whether the membership tool
219 will create a member area if an authenticated user from
220 an underlying user folder logs in first without going
221 through the join process
222 """
223 if not hasattr(self, 'memberareaCreationFlag'):
224 self.memberareaCreationFlag = 0
226 if self.memberareaCreationFlag == 0:
227 self.memberareaCreationFlag = 1
228 else:
229 self.memberareaCreationFlag = 0
231 return MessageDialog(
232 title ='Member area creation flag changed',
233 message='Member area creation flag has been updated',
234 action ='manage_mapRoles')
236 security.declarePublic('createMemberArea')
237 def createMemberArea(self, member_id=''):
238 """ Create a member area for 'member_id' or authenticated user.
239 """
240 if not self.getMemberareaCreationFlag():
241 return None
242 members = self.getMembersFolder()
243 if not members:
244 return None
245 if self.isAnonymousUser():
246 return None
247 # Note: We can't use getAuthenticatedMember() and getMemberById()
248 # because they might be wrapped by MemberDataTool.
249 user = _getAuthenticatedUser(self)
250 user_id = user.getId()
251 if member_id in ('', user_id):
252 member = user
253 member_id = user_id
254 else:
255 if _checkPermission(ManageUsers, self):
256 member = self.acl_users.getUserById(member_id, None)
257 if member:
258 member = member.__of__(self.acl_users)
259 else:
260 raise ValueError('Member %s does not exist' % member_id)
261 else:
262 return None
263 if hasattr( aq_base(members), member_id ):
264 return None
265 else:
266 f_title = "%s's Home" % member_id
267 members.manage_addPortalFolder( id=member_id, title=f_title )
268 f=getattr(members, member_id)
270 f.manage_permission(View,
271 ['Owner','Manager','Reviewer'], 0)
272 f.manage_permission(AccessContentsInformation,
273 ['Owner','Manager','Reviewer'], 0)
275 # Grant Ownership and Owner role to Member
276 f.changeOwnership(member)
277 f.__ac_local_roles__ = None
278 f.manage_setLocalRoles(member_id, ['Owner'])
279 return f
281 security.declarePublic('createMemberarea')
282 createMemberarea = createMemberArea
284 security.declareProtected(ManageUsers, 'deleteMemberArea')
285 def deleteMemberArea(self, member_id):
286 """ Delete member area of member specified by member_id.
287 """
288 members = self.getMembersFolder()
289 if not members:
290 return 0
291 if hasattr( aq_base(members), member_id ):
292 members.manage_delObjects(member_id)
293 return 1
294 else:
295 return 0
297 security.declarePublic('isAnonymousUser')
298 def isAnonymousUser(self):
299 '''
300 Returns 1 if the user is not logged in.
301 '''
302 u = _getAuthenticatedUser(self)
303 if u is None or u.getUserName() == 'Anonymous User':
304 return 1
305 return 0
307 security.declarePublic('checkPermission')
308 def checkPermission(self, permissionName, object, subobjectName=None):
309 '''
310 Checks whether the current user has the given permission on
311 the given object or subobject.
312 '''
313 if subobjectName is not None:
314 object = getattr(object, subobjectName)
315 return _checkPermission(permissionName, object)
317 security.declarePublic('credentialsChanged')
318 def credentialsChanged(self, password):
319 '''
320 Notifies the authentication mechanism that this user has changed
321 passwords. This can be used to update the authentication cookie.
322 Note that this call should *not* cause any change at all to user
323 databases.
324 '''
325 if not self.isAnonymousUser():
326 acl_users = self.acl_users
327 user = _getAuthenticatedUser(self)
328 name = user.getUserName()
329 # this really does need to be the user name, and not the user id,
330 # because we're dealing with authentication credentials
331 if hasattr(acl_users.aq_base, 'credentialsChanged'):
332 # Use an interface provided by LoginManager.
333 acl_users.credentialsChanged(user, name, password)
334 else:
335 req = self.REQUEST
336 p = getattr(req, '_credentials_changed_path', None)
337 if p is not None:
338 # Use an interface provided by CookieCrumbler.
339 change = self.restrictedTraverse(p)
340 change(user, name, password)
342 security.declareProtected(ManageUsers, 'getMemberById')
343 def getMemberById(self, id):
344 '''
345 Returns the given member.
346 '''
347 user = self._huntUser(id, self)
348 if user is not None:
349 user = self.wrapUser(user)
350 return user
352 def _huntUser(self, username, context):
353 """Find user in the hierarchy starting from bottom level 'start'.
354 """
355 uf = context.acl_users
356 while uf is not None:
357 user = uf.getUserById(username)
358 if user is not None:
359 return user
360 container = aq_parent(aq_inner(uf))
361 parent = aq_parent(aq_inner(container))
362 uf = getattr(parent, 'acl_users', None)
363 return None
365 def __getPUS(self):
366 # Gets something we can call getUsers() and getUserNames() on.
367 acl_users = self.acl_users
368 if hasattr(acl_users, 'getUsers'):
369 return acl_users
370 else:
371 # This hack works around the absence of getUsers() in LoginManager.
372 # Gets the PersistentUserSource object that stores our users
373 for us in acl_users.UserSourcesGroup.objectValues():
374 if us.meta_type == 'Persistent User Source':
375 return us.__of__(acl_users)
377 security.declareProtected(ManageUsers, 'listMemberIds')
378 def listMemberIds(self):
379 '''Lists the ids of all members. This may eventually be
380 replaced with a set of methods for querying pieces of the
381 list rather than the entire list at once.
382 '''
383 user_folder = self.__getPUS()
384 return [ x.getId() for x in user_folder.getUsers() ]
386 security.declareProtected(ManageUsers, 'listMembers')
387 def listMembers(self):
388 '''Gets the list of all members.
389 '''
390 return map(self.wrapUser, self.__getPUS().getUsers())
392 security.declareProtected(ListPortalMembers, 'searchMembers')
393 def searchMembers( self, search_param, search_term ):
394 """ Search the membership """
395 md = getToolByName( self, 'portal_memberdata' )
397 return md.searchMemberData( search_param, search_term )
399 security.declareProtected(View, 'getCandidateLocalRoles')
400 def getCandidateLocalRoles(self, obj):
401 """ What local roles can I assign?
402 """
403 member = self.getAuthenticatedMember()
404 member_roles = member.getRolesInContext(obj)
405 if _checkPermission(ManageUsers, obj):
406 local_roles = self.getPortalRoles()
407 if 'Manager' not in member_roles:
408 local_roles.remove('Manager')
409 else:
410 local_roles = [ role for role in member_roles
411 if role not in ('Member', 'Authenticated') ]
412 local_roles.sort()
413 return tuple(local_roles)
415 security.declareProtected(View, 'setLocalRoles')
416 def setLocalRoles(self, obj, member_ids, member_role, reindex=1):
417 """ Add local roles on an item.
418 """
419 if ( _checkPermission(ChangeLocalRoles, obj)
420 and member_role in self.getCandidateLocalRoles(obj) ):
421 for member_id in member_ids:
422 roles = list(obj.get_local_roles_for_userid( userid=member_id ))
424 if member_role not in roles:
425 roles.append( member_role )
426 obj.manage_setLocalRoles( member_id, roles )
428 if reindex:
429 # It is assumed that all objects have the method
430 # reindexObjectSecurity, which is in CMFCatalogAware and
431 # thus PortalContent and PortalFolder.
432 obj.reindexObjectSecurity()
434 security.declareProtected(View, 'deleteLocalRoles')
435 def deleteLocalRoles(self, obj, member_ids, reindex=1, recursive=0):
436 """ Delete local roles of specified members.
437 """
438 if _checkPermission(ChangeLocalRoles, obj):
439 for member_id in member_ids:
440 if obj.get_local_roles_for_userid(userid=member_id):
441 obj.manage_delLocalRoles(userids=member_ids)
442 break
444 if recursive and hasattr( aq_base(obj), 'contentValues' ):
445 for subobj in obj.contentValues():
446 self.deleteLocalRoles(subobj, member_ids, 0, 1)
448 if reindex:
449 # reindexObjectSecurity is always recursive
450 obj.reindexObjectSecurity()
452 security.declarePrivate('addMember')
453 def addMember(self, id, password, roles, domains, properties=None):
454 '''Adds a new member to the user folder. Security checks will have
455 already been performed. Called by portal_registration.
456 '''
457 acl_users = self.acl_users
458 if hasattr(acl_users, '_doAddUser'):
459 acl_users._doAddUser(id, password, roles, domains)
460 else:
461 # The acl_users folder is a LoginManager. Search for a UserSource
462 # with the needed support.
463 for source in acl_users.UserSourcesGroup.objectValues():
464 if hasattr(source, 'addUser'):
465 source.__of__(self).addUser(id, password, roles, domains)
466 raise "Can't add Member", "No supported UserSources"
468 if properties is not None:
469 member = self.getMemberById(id)
470 member.setMemberProperties(properties)
472 security.declareProtected(ManageUsers, 'deleteMembers')
473 def deleteMembers(self, member_ids, delete_memberareas=1,
474 delete_localroles=1):
475 """ Delete members specified by member_ids.
476 """
478 # Delete members in acl_users.
479 acl_users = self.acl_users
480 if _checkPermission(ManageUsers, acl_users):
481 if isinstance(member_ids, basestring):
482 member_ids = (member_ids,)
483 member_ids = list(member_ids)
484 for member_id in member_ids[:]:
485 if not acl_users.getUserById(member_id, None):
486 member_ids.remove(member_id)
487 try:
488 acl_users.userFolderDelUsers(member_ids)
489 except (NotImplementedError, 'NotImplemented'):
490 raise NotImplementedError('The underlying User Folder '
491 'doesn\'t support deleting members.')
492 else:
493 raise AccessControl_Unauthorized('You need the \'Manage users\' '
494 'permission for the underlying User Folder.')
496 # Delete member data in portal_memberdata.
497 mdtool = getToolByName(self, 'portal_memberdata', None)
498 if mdtool is not None:
499 for member_id in member_ids:
500 mdtool.deleteMemberData(member_id)
502 # Delete members' home folders including all content items.
503 if delete_memberareas:
504 for member_id in member_ids:
505 self.deleteMemberArea(member_id)
507 # Delete members' local roles.
508 if delete_localroles:
509 utool = getToolByName(self, 'portal_url', None)
510 self.deleteLocalRoles( utool.getPortalObject(), member_ids,
511 reindex=1, recursive=1 )
513 return tuple(member_ids)
515 security.declarePublic('getHomeFolder')
516 def getHomeFolder(self, id=None, verifyPermission=0):
517 """Returns a member's home folder object or None.
518 Set verifyPermission to 1 to return None when the user
519 doesn't have the View permission on the folder.
520 """
521 return None
523 security.declarePublic('getHomeUrl')
524 def getHomeUrl(self, id=None, verifyPermission=0):
525 """Returns the URL to a member's home folder or None.
526 Set verifyPermission to 1 to return None when the user
527 doesn't have the View permission on the folder.
528 """
529 return None
531 InitializeClass(MembershipTool)