# # Copyright (c) 2015 Helmut Merz helmutm@cy55.de # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Common functions and other stuff for working with permissions and roles. """ from persistent import Persistent from zope import component from zope.annotation.interfaces import IAttributeAnnotatable from zope.app.container.interfaces import IObjectAddedEvent from zope.app.security.settings import Allow, Deny, Unset from zope.cachedescriptors.property import Lazy from zope.interface import implements from zope.lifecycleevent import IObjectCreatedEvent, IObjectModifiedEvent from zope.location.interfaces import IRoot, ILocation from zope.security import canAccess, canWrite from zope.security import checkPermission as baseCheckPermission from zope.security.management import getInteraction from zope.securitypolicy.interfaces import IPrincipalRoleManager from zope.securitypolicy.interfaces import IRolePermissionManager from zope.traversing.api import getName, getParents from zope.traversing.interfaces import IPhysicallyLocatable from cybertools.meta.interfaces import IOptions from loops.common import adapted from loops.interfaces import ILoopsObject, IConcept from loops.interfaces import IAssignmentEvent, IDeassignmentEvent from loops.security.interfaces import ISecuritySetter, IWorkspaceInformation allRolesExceptOwner = ( #'zope.SiteManager' - no, not this one... 'zope.Anonymous', 'zope.Member', 'zope.ContentManager', 'loops.Staff', 'loops.xmlrpc.ConceptManager', # relevant for local security? #'loops.SiteManager', 'loops.Person', 'loops.Member', 'loops.Master') allRolesExceptOwnerAndMaster = tuple(allRolesExceptOwner[:-1]) minorPrivilegedRoles = ('zope.Anonymous', 'zope.Member',) localRoles = ('zope.Anonymous', 'zope.Member', 'zope.ContentManager', 'loops.SiteManager', 'loops.Staff', 'loops.Member', 'loops.Master', 'loops.Owner', 'loops.Person') localPermissions = ('zope.ManageContent', 'zope.View', 'loops.ManageWorkspaces', 'loops.ViewRestricted', 'loops.EditRestricted', 'loops.AssignAsParent',) acquiringPredicateNames = ('hasType', 'standard', 'ownedby', 'ispartof') allocationPredicateNames = ('isowner', 'ismaster', 'ismember',) workspaceGroupsFolderName = 'gloops_ws' # checking and querying functions # TODO: activate canAccessObjectWithOption depending on custom.config def getOption(obj, option, checkType=True): opts = component.queryAdapter(adapted(obj), IOptions) if opts is not None: opt = opts(option, None) if opt is True: return opt if opt: # TODO: log return opt[0] if not checkType: return None typeMethod = getattr(obj, 'getType', None) if typeMethod is not None: opts = component.queryAdapter(adapted(typeMethod()), IOptions) if opts is not None: opt = opts(option, None) if opt is True: return opt if opt: # TODO: log return opt[0] return None def canAccessObjectWithOption(obj): if not canAccess(obj, 'title'): return False perm = getOption(obj, 'access_permission') if not perm: return True return checkPermission(perm, obj) def canAccessObject(obj): return canAccess(obj, 'title') def canListObject(obj, noCheck=False): if noCheck: return True return canAccessObject(obj) def canAccessRestricted(obj): return checkPermission('loops.ViewRestricted', obj) def canWriteObject(obj): return canWrite(obj, 'title') or canAssignAsParent(obj) def canEditRestricted(obj): return checkPermission('loops.EditRestricted', obj) def canAssignAsParent(obj): return checkPermission('loops.AssignAsParent', obj) def checkPermission(permission, obj): return baseCheckPermission(permission, obj) def getCurrentPrincipal(): interaction = getInteraction() if interaction is not None: parts = interaction.participations if parts: return parts[0].principal return None # functions for checking and setting security properties def overrides(s1, s2): settings = [Allow, Deny, Unset] return settings.index(s1) < settings.index(s2) def setRolePermission(rpm, p, r, setting): if setting == Allow: rpm.grantPermissionToRole(p, r) elif setting == Deny: rpm.denyPermissionToRole(p, r) else: rpm.unsetPermissionFromRole(p, r) def setPrincipalRole(prm, r, p, setting): if setting == Allow: prm.assignRoleToPrincipal(r, p) elif setting == Deny: prm.removeRoleFromPrincipal(r, p) else: prm.unsetRoleForPrincipal(r, p) def assignOwner(obj, principalId): prm = IPrincipalRoleManager(obj, None) if prm is not None: prm.assignRoleToPrincipal('loops.Owner', principalId) def removeOwner(obj, principalId): prm = IPrincipalRoleManager(obj, None) if prm is not None: prm.unsetRoleForPrincipal('loops.Owner', principalId) def assignPersonRole(obj, principalId): prm = IPrincipalRoleManager(obj) prm.assignRoleToPrincipal('loops.Person', principalId) def removePersonRole(obj, principalId): prm = IPrincipalRoleManager(obj) prm.unsetRoleForPrincipal('loops.Person', principalId) def allowEditingForOwner(obj, deny=allRolesExceptOwner, revert=False): rpm = IRolePermissionManager(obj) if revert: for role in deny: rpm.unsetPermissionFromRole('zope.ManageContent', role) rpm.unsetPermissionFromRole('zope.ManageContent', 'loops.Owner') else: for role in deny: rpm.denyPermissionToRole('zope.ManageContent', role) rpm.grantPermissionToRole('zope.ManageContent', 'loops.Owner') def restrictView(obj, roles=allRolesExceptOwnerAndMaster, revert=False): rpm = IRolePermissionManager(obj) if revert: for role in roles: rpm.unsetPermissionFromRole('zope.View', role) else: for role in roles: rpm.denyPermissionToRole('zope.View', role) # event handlers #@component.adapter(ILoopsObject, IObjectAddedEvent) #@component.adapter(ILoopsObject, IObjectModifiedEvent) @component.adapter(ILoopsObject, IObjectCreatedEvent) def setDefaultSecurity(obj, event): aObj = adapted(obj) setter = ISecuritySetter(aObj) setter.setDefaultSecurity() principal = getCurrentPrincipal() if principal is not None: assignOwner(obj, principal.id) @component.adapter(IConcept, IAssignmentEvent) def grantAcquiredSecurity(obj, event): aObj = adapted(obj) setter = ISecuritySetter(aObj) setter.setAcquiredSecurity(event.relation) @component.adapter(IConcept, IDeassignmentEvent) def revokeAcquiredSecurity(obj, event): aObj = adapted(obj) setter = ISecuritySetter(aObj) setter.setAcquiredSecurity(event.relation, revert=True) # workspace handling class WorkspaceInformation(Persistent): """ For storing security-related stuff pertaining to children and resources of the context (=parent) object. """ implements(IPhysicallyLocatable, IWorkspaceInformation) __name__ = u'workspace_information' #propagateRolePermissions = 'object' # or 'none' propagateRolePermissions = 'workspace' propagateParentSecurity = True # False #propagateParentSecurity = False allocationPredicateNames = allocationPredicateNames workspaceGroupsFolderName = workspaceGroupsFolderName def __init__(self, parent): self.__parent__ = parent self.workspaceGroupNames = {} def getName(self): return self.__name__ def getParent(self): return self.__parent__ def getParents(self): p = self.getParent() return [p] + getParents(p) class LocationWSI(object): implements(ILocation) component.adapts(WorkspaceInformation) def __init__(self, context): self.context = context self.__name__ = context.__name__ self.__parent__ = context.__parent__ def getWorkspaceGroup(obj, predicate): wsi = obj.workspaceInformation if wsi is None: return None pn = getName(predicate) if pn in wsi.allocationPredicateNames: gn = wsi.workspaceGroupNames if not isinstance(gn, dict): # backwards compatibility return None groupName = gn.get(pn) if groupName: gfName = wsi.workspaceGroupsFolderName if gfName: from loops.organize.util import getGroupsFolder gf = getGroupsFolder(wsi, gfName) if gf is not None: return gf.get(groupName) return None @component.adapter(ILoopsObject, IAssignmentEvent) def addGroupMembershipOnAssignment(obj, event): group = getWorkspaceGroup(obj, event.relation.predicate) if group is not None: person = adapted(event.relation.second) from loops.organize.interfaces import IPerson if IPerson.providedBy(person): userId = person.getUserId() if userId: members = list(group.principals) if userId not in members: members.append(userId) group.principals = tuple(members) #print '*** assign', group.__name__, userId, group.principals @component.adapter(ILoopsObject, IDeassignmentEvent) def removeGroupMembershipOnDeassignment(obj, event): group = getWorkspaceGroup(obj, event.relation.predicate) if group is not None: person = adapted(event.relation.second) from loops.organize.interfaces import IPerson if IPerson.providedBy(person): userId = person.getUserId() if userId: members = list(group.principals) if userId in members: members.remove(userId) group.principals = tuple(members) #print '*** remove', group.__name__, userId, group.principals