diff --git a/concept.py b/concept.py index 69bdfad..f87b078 100644 --- a/concept.py +++ b/concept.py @@ -165,16 +165,6 @@ class Concept(Contained, Persistent): pi.relations.append(rel) return result - @property - def isWorkspace(self): - ct = self.conceptType - if ct != self.getConceptManager().getTypeConcept(): - from loops.config.base import DummyOptions - options = component.queryAdapter(adapted(self), IOptions) or DummyOptions() - if options('security.isWorkspace'): - return True - return IOptions(adapted(ct))('security.isWorkspace') - # concept relations def getClients(self, relationships=None): diff --git a/integrator/interfaces.py b/integrator/interfaces.py index d535d87..25c6b93 100644 --- a/integrator/interfaces.py +++ b/integrator/interfaces.py @@ -25,7 +25,7 @@ $Id$ from zope.interface import Interface, Attribute from zope import interface, component, schema -from loops.interfaces import IConceptSchema +from loops.interfaces import IConceptSchema, ILoopsAdapter from loops.util import _ @@ -41,7 +41,7 @@ class IExternalSourceInfo(Interface): # external collections -class IExternalCollection(IConceptSchema): +class IExternalCollection(IConceptSchema, ILoopsAdapter): """ A concept representing a collection of resources that may be actively retrieved from an external system using the parameters given. diff --git a/interfaces.py b/interfaces.py index 19c1a71..fb0f059 100644 --- a/interfaces.py +++ b/interfaces.py @@ -104,12 +104,8 @@ class IConcept(IConceptSchema, ILoopsObject, IPotentialTarget): source="loops.conceptTypeSource", required=True) - isWorkspace = Attribute('Marks a concept as responsible for providing ' - 'special permission settings (children grants) ' - 'for its sub-objects (children or resources).') - workspaceInformation = Attribute('An object with additional ' - 'workspace-related information, e.g. children grants.') + 'workspace-related information.') def getType(): """ Return a concept that provides the object's type. diff --git a/organize/interfaces.py b/organize/interfaces.py index 6e35ea0..3b395d5 100644 --- a/organize/interfaces.py +++ b/organize/interfaces.py @@ -34,6 +34,7 @@ from cybertools.organize.interfaces import IPerson as IBasePerson from cybertools.organize.interfaces import ITask from loops.interfaces import IConceptSchema from loops.organize.util import getPrincipalFolder +from loops.interfaces import ILoopsAdapter from loops import util from loops.util import _ @@ -87,7 +88,7 @@ class LoginName(schema.TextLine): mapping=dict(userId=userId))) -class IPerson(IConceptSchema, IBasePerson): +class IPerson(IConceptSchema, IBasePerson, ILoopsAdapter): """ Resembles a human being with a name (first and last name), a birth date, and a set of addresses. This interface only lists fields used in addition to those provided by the @@ -101,7 +102,7 @@ class IPerson(IConceptSchema, IBasePerson): required=False,) -class IAddress(IConceptSchema, IBaseAddress): +class IAddress(IConceptSchema, IBaseAddress, ILoopsAdapter): """ See cybertools.organize. """ @@ -158,7 +159,7 @@ class IMemberRegistrationManager(Interface): # task -class ITask(IConceptSchema, ITask): +class ITask(IConceptSchema, ITask, ILoopsAdapter): pass diff --git a/security/browser.py b/security/browser.py index 5fef50a..af9bb6c 100644 --- a/security/browser.py +++ b/security/browser.py @@ -23,18 +23,150 @@ $Id$ """ from zope.app.pagetemplate import ViewPageTemplateFile +from zope.app.security.interfaces import IPermission +from zope.app.securitypolicy.browser import granting +from zope.app.securitypolicy.browser.rolepermissionview import RolePermissionView +from zope.app.securitypolicy.interfaces import IPrincipalRoleManager, \ + IRolePermissionMap +from zope.app.securitypolicy.interfaces import IPrincipalPermissionManager, \ + IPrincipalPermissionMap +from zope.app.securitypolicy.zopepolicy import SettingAsBoolean from zope import component from zope.interface import implements from zope.cachedescriptors.property import Lazy from zope.security.proxy import removeSecurityProxy +from zope.traversing.api import getParent, getParents +from loops.common import adapted from loops.security.common import WorkspaceInformation -from loops.security.perm import PermissionView +from loops.security.interfaces import ISecuritySetter permission_template = ViewPageTemplateFile('manage_permissionform.pt') +class Granting(granting.Granting): + + def status(self): + value = super(Granting, self).status() + if value: + setter = ISecuritySetter(adapted(self.context), None) + if setter is not None: + setter.propagatePrincipalRoles() + return value + + +class PermissionView(object): + """ View for permission editing. + """ + + def __init__(self, context, request): + self.context = context + # make sure the real view (delegate) updates our context when + # talking about the context's parent: + self.__parent__ = context + self.request = request + self.delegate = RolePermissionView() + self.delegate.context = self + self.delegate.request = request + self.permissionId = request.get('permission_to_manage') or 'zope.View' + + def pagetip(self): + return self.delegate.pagetip() + + def roles(self): + return self.delegate.roles() + + def permissions(self): + return self.delegate.permissions() + + def availableSettings(self, noacquire=False): + return self.delegate.availableSettings(noacquire) + + def permissionRoles(self): + return self.delegate.permissionRoles() + + def permissionForID(self, pid): + return self.delegate.permissionForID(pid) + + @Lazy + def permission(self): + return self.permissionForID(self.permissionId) + + def roleForID(self, rid): + return self.delegate.roleForID(rid) + + def update(self, testing=None): + value = self.delegate.update(testing) + if value: + setter = ISecuritySetter(self.adapted, None) + if setter is not None: + setter.propagateRolePermissions() + return value + + @Lazy + def adapted(self): + return adapted(self.context) + + def getAcquiredPermissionSetting(self, role, perm): + for obj in getParents(self.context): + rpm = IRolePermissionMap(obj, None) + if rpm is not None: + setting = rpm.getSetting(perm, role) + setting = SettingAsBoolean[setting] + if setting is not None: + return setting and '+' or '-' + return '' + + def listUsersForRole(self, rid): + result = '' + direct = IPrincipalRoleManager(self.context).getPrincipalsForRole(rid) + if direct: + result = '' + self.renderEntry(direct) + '' + acquired = [] + for obj in getParents(self.context): + prm = IPrincipalRoleManager(obj, None) + if prm is not None: + entry = prm.getPrincipalsForRole(rid) + if entry: + acquired.append(self.renderEntry(entry)) + if acquired: + if result: + result += '' + result += ''.join(acquired) + return result + + def renderEntry(self, entry): + result = [] + for e in entry: + value = SettingAsBoolean[e[1]] + value = (value is False and '-') or (value and '+') or '' + result.append(value + e[0]) + return ', '.join(result) + + def getPrincipalPermissions(self): + result = '' + ppm = IPrincipalPermissionMap(self.context) + direct = ppm.getPrincipalsForPermission(self.permissionId) + if direct: + result = '' + self.renderEntry(direct) + '' + acquired = [] + for obj in getParents(self.context): + ppm = IPrincipalPermissionMap(obj, None) + if ppm is not None: + entry = ppm.getPrincipalsForPermission(self.permissionId) + if entry: + acquired.append(self.renderEntry(entry)) + if acquired: + if result: + result += '' + result += ''.join(acquired) + return result + + def getPermissions(self): + return sorted(name for name, perm in component.getUtilitiesFor(IPermission)) + + class ManageWorkspaceView(PermissionView): """ View for managing workspace information. """ @@ -49,3 +181,9 @@ class ManageWorkspaceView(PermissionView): @Lazy def permission_macros(self): return permission_template.macros + + @Lazy + def adapted(self): + return adapted(getParent(self.context)) + + diff --git a/security/common.py b/security/common.py index 634878e..a155d50 100644 --- a/security/common.py +++ b/security/common.py @@ -23,9 +23,11 @@ $Id$ """ from persistent import Persistent +from persistent.list import PersistentList 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.app.securitypolicy.interfaces import IPrincipalRoleManager from zope.app.securitypolicy.interfaces import IRolePermissionManager from zope.cachedescriptors.property import Lazy @@ -39,7 +41,7 @@ from zope.traversing.interfaces import IPhysicallyLocatable from loops.common import adapted from loops.interfaces import ILoopsObject, IConcept from loops.interfaces import IAssignmentEvent, IDeassignmentEvent -from loops.security.interfaces import ISecuritySetter +from loops.security.interfaces import ISecuritySetter, IWorkspaceInformation allRolesExceptOwner = ( @@ -81,7 +83,20 @@ def getCurrentPrincipal(): return None -# functions for setting security properties +# 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 assignOwner(obj, principalId): prm = IPrincipalRoleManager(obj) @@ -149,12 +164,16 @@ class WorkspaceInformation(Persistent): children and resources of the context (=parent) object. """ - implements(IPhysicallyLocatable) + implements(IPhysicallyLocatable, IWorkspaceInformation) __name__ = u'workspace_information' + propagatePrincipalRoles = False + propagateRolePermissions = 'workspace' + def __init__(self, parent): self.__parent__ = parent + self.workspaceGroups = PersistentList() def getName(self): return self.__name__ diff --git a/security/configure.zcml b/security/configure.zcml index 62d7709..bd6a6ab 100644 --- a/security/configure.zcml +++ b/security/configure.zcml @@ -9,6 +9,9 @@ + + + + + @@ -33,9 +39,17 @@ name="permissions.html" permission="zope.Security" template="manage_permissionform.pt" - class="loops.security.perm.PermissionView" + class="loops.security.browser.PermissionView" menu="zmi_actions" title="Edit Permissions" /> + + + + + Granting Roles and Permissions to Principals + + + + + + + ... + + + + Grants for the selected principal + + + + + + + + Roles + Allow + Unset + Deny + + + + + The Label + + + + roles widget + + + + ^ top + + + + + + + Permissions + Allow + Unset + Deny + + + + + The Label + + + + permission widget + + + + ^ top + + + + + + + + + + + +