X-Git-Url: https://scm.cri.ensmp.fr/git/Plinn.git/blobdiff_plain/3c4367d8e03450e9a73e61f4247145d2b6c86a33..959d888c17d1403d2eeecc19bc4b5e2c8d1debf6:/Products/Plinn/MembershipTool.py?ds=inline diff --git a/Products/Plinn/MembershipTool.py b/Products/Plinn/MembershipTool.py new file mode 100755 index 0000000..3b3da9a --- /dev/null +++ b/Products/Plinn/MembershipTool.py @@ -0,0 +1,431 @@ +# -*- coding: utf-8 -*- +####################################################################################### +# Plinn - http://plinn.org # +# Copyright (C) 2005-2007 Benoît PIN # +# # +# 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; if not, write to the Free Software # +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # +####################################################################################### +""" Plinn portal_membership + + + +""" + +from AccessControl import ClassSecurityInfo, getSecurityManager +from AccessControl.unauthorized import Unauthorized +from AccessControl.SpecialUsers import nobody +from AccessControl.Permission import Permission +from Acquisition import aq_base, aq_inner +from Globals import InitializeClass, MessageDialog +from Products.PageTemplates.PageTemplateFile import PageTemplateFile + +from Products.CMFDefault.MembershipTool import MembershipTool as BaseTool +from Products.CMFCore.permissions import View, ListPortalMembers, ManagePortal, SetOwnPassword, ChangePermissions +from permissions import RemoveMember, SetLocalRoles, CheckMemberPermission +from utils import _checkMemberPermission +from Products.CMFCore.utils import _checkPermission, _getAuthenticatedUser +from Products.CMFCore.utils import getUtilityByInterfaceName +from utils import formatFullName, translate +from Products.CMFDefault.utils import decode +from Products.CMFDefault.Document import addDocument + +from sets import Set +from types import TupleType + + +from time import time +from logging import getLogger +console = getLogger('Plinn.MembershipTool') + + +class MembershipTool( BaseTool ): + """ Implement 'portal_membership' interface using "stock" policies. + """ + + + meta_type = 'Plinn Membership Tool' + + manage_options=( ({ 'label' : 'Configuration' + , 'action' : 'manage_mapRoles' + },) + BaseTool.manage_options[1:]) + + security = ClassSecurityInfo() + + security.declareProtected(ManagePortal, 'manage_mapRoles') + manage_mapRoles = PageTemplateFile('www/configureMembershipTool', globals(), + __name__='manage_mapRoles') + + # + # 'portal_membership' interface methods + # + + # change security settings for inherited methods + security.declareProtected(ListPortalMembers, 'getMemberById') + + + memberareaPortalType = 'Huge Plinn Folder' + + +# security.declareProtected(SetOwnPassword, 'setPassword') +# def setPassword(self, password, domains=None): +# '''Allows the authenticated member to set his/her own password. +# ''' +# user_folder = self.__getPUS() +# if user_folder.meta_type == 'Group User Folder' : +# registration = getToolByName(self, 'portal_registration', None) +# if not self.isAnonymousUser(): +# member = self.getAuthenticatedMember() +# if registration: +# failMessage = registration.testPasswordValidity(password) +# if failMessage is not None: +# raise 'Bad Request', failMessage +# member.setSecurityProfile(password=password, domains=domains) +# member.changePassword(password) +# else: +# raise 'Bad Request', 'Not logged in.' +# +# else : +# BaseTool.setPassword(self, password, domains=None) + + + + security.declareProtected(ListPortalMembers, 'listMemberIds') + def listMemberIds(self): + '''Lists the ids of all members. This may eventually be + replaced with a set of methods for querying pieces of the + list rather than the entire list at once. + ''' + user_folder = self.__getPUS() + if user_folder.meta_type == 'Group User Folder' : + return user_folder.getPureUserNames() + else : + return [ x.getId() for x in user_folder.getUsers() ] + + + security.declareProtected(CheckMemberPermission, 'checkMemberPermission') + def checkMemberPermission(self, userid, permissionName, object, subobjectName=None): + ''' + Checks whether the current user has the given permission on + the given object or subobject. + ''' + if subobjectName is not None: + object = getattr(object, subobjectName) + + return _checkMemberPermission(userid, permissionName, object) + + security.declareProtected(ListPortalMembers, 'listMembers') + def listMembers(self): + '''Gets the list of all members. + ''' + user_folder = self.__getPUS() + if user_folder.meta_type == 'Group User Folder' : + return map(self.wrapUser, user_folder.getPureUsers()) + else : + return map(self.wrapUser, user_folder.getUsers()) + + + security.declareProtected(View, 'getCandidateLocalRoles') + def getCandidateLocalRoles(self, obj) : + """ What local roles can I assign? + """ + member = self.getAuthenticatedMember() + valid_roles = obj.valid_roles() + if 'Manager' in member.getRoles(): + local_roles = [r for r in valid_roles if r != 'Anonymous'] + else: + sm = getSecurityManager() + allPermissions = self.ac_inherited_permissions(1) + + # construct a dictionary of permissions indexed by role + # and get permissions of user in obj context + memberPermissions = Set() + rolesMappings = {} + for role in valid_roles : + rolesMappings[role] = Set() + + for p in allPermissions: + name, value = p[:2] + + p=Permission(name,value,obj) + rolesOfPerm = p.getRoles() + + for role in rolesOfPerm : + try : rolesMappings[role].add(name) + except KeyError : + trName = p._p + if hasattr(obj, trName): + l = list(getattr(obj, trName)) + l.remove(role) + setattr(obj, trName, tuple(l)) + msg = '%s role has been removed for %s permission on %s ' % (role, name, obj.absolute_url()) + #LOG('portal_membership', WARNING, msg) + + parent = obj.aq_inner.aq_parent + while type(rolesOfPerm) != TupleType : + p=Permission(name, value, parent) + rolesOfPerm = p.getRoles() + for role in rolesOfPerm : + try : rolesMappings[role].add(name) + except KeyError : pass + try : parent = parent.aq_inner.aq_parent + except AttributeError : break + + + if sm.checkPermission(name, obj) : + memberPermissions.add(name) + + local_roles = [] + for role in valid_roles : + if rolesMappings[role] and rolesMappings[role].issubset(memberPermissions) : + local_roles.append(role) + + local_roles = [ role for role in local_roles if role not in ('Shared', 'Authenticated', 'Member', 'Anonymous') ] + local_roles.sort() + return tuple(local_roles) + + + security.declareProtected(View, 'setLocalRoles') + def setLocalRoles( self, obj, member_ids, role, remove=0, reindex=1 ): + """ Set local roles on an item """ + if role not in self.getCandidateLocalRoles(obj) : + raise Unauthorized, "You are not allowed to manage %s role" % role + + if self.checkPermission(SetLocalRoles, obj) : + if not remove : + for member_id in member_ids : + # current roles for user id in obj + roles = list(obj.get_local_roles_for_userid( userid=member_id )) + if role not in roles : + roles.append(role) + obj.manage_setLocalRoles( member_id, roles) + else : + for member_id in member_ids : + # current roles for user id in obj + roles = list(obj.get_local_roles_for_userid( userid=member_id )) + try : roles.remove(role) + except ValueError : pass + else : + if len(roles) >= 1 : + obj.manage_setLocalRoles( member_id, roles) + else : + obj.manage_delLocalRoles( userids=[member_id] ) + + else : + raise Unauthorized + + if reindex: + # It is assumed that all objects have the method + # reindexObjectSecurity, which is in CMFCatalogAware and + # thus PortalContent and PortalFolder. + obj.reindexObjectSecurity() + + + security.declarePublic('getMemberFullNameById') + def getMemberFullNameById(self, userid, nameBefore = 1) : + """ Return the best formated representation of user fullname. """ + + memberFullName = '' + if userid and userid != 'No owner' : + # No owner is a possible value returned by DefaultDublinCoreImpl.Creator + member = self.getMemberById(userid) + if not member : + return userid + memberFullName = member.getMemberFullName(nameBefore=nameBefore) + + return memberFullName + + security.declareProtected(ListPortalMembers, 'getMembers') + def getMembers(self, users) : + """ Return wraped users """ + members = [] + for user in users : + members.append(self.getMemberById(user)) + + members = filter(None, members) + members.sort( lambda m0, m1 : cmp(m0.getMemberSortableFormat(), m1.getMemberSortableFormat()) ) + return members + + + security.declareProtected(ListPortalMembers, 'getOtherMembers') + def getOtherMembers(self, users) : + """ Return members who are not in users list""" + allMemberIds = self.listMemberIds() + otherMemberIds = [ userId for userId in allMemberIds if userId not in users ] + return self.getMembers(otherMemberIds) + + + + security.declareProtected(ListPortalMembers, 'getMembersMetadata') + def getMembersMetadata(self, users) : + """ return metadata from portal_catalog """ + userDict = {} + for u in users : userDict[u] = True + ctool = getUtilityByInterfaceName('Products.CMFCore.interfaces.ICatalogTool') + memberBrains = ctool(portal_type='Member Data', sort_on='getMemberSortableFormat') + memberList = [] + complementList = [] + + if users : + for mb in memberBrains : + metadata = {'id' : mb.getId, 'fullname' : mb.getMemberFullName} + if userDict.has_key(mb.getId) : + memberList.append(metadata) + else : + complementList.append(metadata) + else : + complementList = [{'id' : mb.getId, 'fullname' : mb.getMemberFullName} for mb in memberBrains] + + return {'memberList' : memberList, 'complementList' : complementList} + + + + security.declareProtected(RemoveMember, 'removeMembers') + def removeMembers(self, memberIds = []) : + """ remove member + """ + # TODO : remove member document ? + mdtool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IMemberDataTool') + for m in self.getMembers(memberIds) : + m.manage_beforeDelete() + mdtool.deleteMemberData(m.getId()) + + self.aq_inner.acl_users.deleteUsers(users = memberIds) + + + + security.declareProtected(ManagePortal, 'setMemberAreaPortalType') + def setMemberAreaPortalType(self, member_folder_portal_type): + """ Set member area portal type to construct.""" + ttool = getUtilityByInterfaceName('Products.CMFCore.interfaces.ITypesTool') + if member_folder_portal_type not in ttool.objectIds() : + raise ValueError, "Unknown portal type : %s" % str(member_folder_portal_type) + + self.memberareaPortalType = member_folder_portal_type + return MessageDialog(title ='Type updated', + message='The member area type have been updated', + action ='manage_mapRoles') + + def getMemberAreaPortalType(self) : + return self.memberareaPortalType + + + def getHomeFolder(self, id=None, verifyPermission=0): + """ Return a member's home folder object, or None. + """ + if id is None: + member = self.getAuthenticatedMember() + if not hasattr(member, 'getMemberId'): + return None + id = member.getMemberId() + members = self.getMembersFolder() + if members is not None: + if not hasattr(members, id) and getattr(self, 'memberareaCreationFlag', 0) != 0 : + self.createMemberArea(id) + try: + folder = members._getOb(id) + if verifyPermission and not _checkPermission(View, folder): + # Don't return the folder if the user can't get to it. + return None + return folder + except (AttributeError, TypeError, KeyError): + pass + return None + + security.declarePublic('createMemberArea') + def createMemberArea(self, member_id=''): + """ Create a member area for 'member_id' or authenticated user. + """ + if not self.getMemberareaCreationFlag(): + return None + members = self.getMembersFolder() + if not members: + return None + if self.isAnonymousUser(): + return None + # Note: We can't use getAuthenticatedMember() and getMemberById() + # because they might be wrapped by MemberDataTool. + user = _getAuthenticatedUser(self) + user_id = user.getId() + if member_id in ('', user_id): + member = user + member_id = user_id + else: + if _checkPermission(ManageUsers, self): + member = self.acl_users.getUserById(member_id, None) + if member: + member = member.__of__(self.acl_users) + else: + raise ValueError, 'Member %s does not exist' % member_id + else: + return None + + if hasattr( aq_base(members), member_id ): + return None + + ttool = getUtilityByInterfaceName('Products.CMFCore.interfaces.ITypesTool') + info = getattr(ttool, self.memberareaPortalType) + + memberFullName = self.getMemberFullNameById(member_id, nameBefore = 0) + f = info._constructInstance( members, member_id, title=memberFullName ) + + # Grant Ownership and Owner role to Member + f.changeOwnership(user) + f.__ac_local_roles__ = None + f.manage_setLocalRoles(member_id, ['Owner']) + + f.reindexObjectSecurity() + return f + + + security.declareProtected(ListPortalMembers, 'looseSearchMembers') + def looseSearchMembers(self, searchString) : + """ """ + + words = searchString.strip().split() + words = [word.lower() for word in words] + + mdtool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IMemberDataTool') + mdProperties = mdtool.propertyIds() + searchableProperties = [ p['id'] for p in mdtool.propertyMap() if p['type'] == 'string' ] + ['id'] + try : searchableProperties.remove('portal_skin') + except ValueError : pass + + match = [] + for m in self.listMembers() : + allWordsMatch = False + for word in words : + for p in searchableProperties : + if str(m.getProperty(p, '')).lower().find(word) != -1 : + allWordsMatch = True + break + else : + allWordsMatch = False + + if not allWordsMatch : + break + else : + match.append(m) + + return match + + def __getPUS(self): + # CMFCore.MembershipTool.MembershipTool tests 'getUsers' method but : + # "enumeration" methods ('getUserNames', 'getUsers') are *not* + # part of the contract! See IEnumerableUserFolder. + # (from PluggableAuthService.interfaces.authservice #233) + return self.acl_users + + +InitializeClass(MembershipTool)