propagation of security settings (principal roles and role permissions) basically working
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3649 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
bea4f70ba3
commit
504092b268
11 changed files with 150 additions and 119 deletions
|
@ -60,7 +60,7 @@ from cybertools.typology.interfaces import IType, ITypeManager
|
|||
from loops.common import adapted
|
||||
from loops.config.base import DummyOptions
|
||||
from loops.i18n.browser import I18NView
|
||||
from loops.interfaces import IResource, IView, INode
|
||||
from loops.interfaces import IResource, IView, INode, ITypeConcept
|
||||
from loops.organize.tracking import access
|
||||
from loops.resource import Resource
|
||||
from loops.security.common import canAccessObject, canListObject, canWriteObject
|
||||
|
@ -413,6 +413,8 @@ class BaseView(GenericView, I18NView):
|
|||
|
||||
@Lazy
|
||||
def options(self):
|
||||
if ITypeConcept.providedBy(self.adapted):
|
||||
return DummyOptions()
|
||||
return component.queryAdapter(self.adapted, IOptions) or DummyOptions()
|
||||
|
||||
@Lazy
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
<zope:class class="loops.compound.blog.post.BlogPost">
|
||||
<require permission="zope.View"
|
||||
interface="loops.compound.blog.interfaces.IBlogPost" />
|
||||
<require permission="zope.View"
|
||||
attributes="context" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="loops.compound.blog.interfaces.IBlogPost" />
|
||||
</zope:class>
|
||||
|
|
|
@ -43,24 +43,27 @@ class BlogPostSecuritySetter(BaseSecuritySetter):
|
|||
def setDefaultPrincipalRoles(self):
|
||||
assignOwner(self.context.context, self.principalId)
|
||||
|
||||
def setAcquiredRolePermissions(self, relation, revert=False):
|
||||
if isAcquiring(relation.predicate):
|
||||
def setAcquiredSecurity(self, relation, revert=False):
|
||||
#if self.isAcquiring(relation.predicate):
|
||||
if relation.predicate in self.acquiringPredicates:
|
||||
allowEditingForOwner(relation.second, revert=revert)
|
||||
if self.context.private:
|
||||
restrictView(relation.second, revert=revert)
|
||||
|
||||
def setAcquiredPrincipalRoles(self, relation, revert=False):
|
||||
if isAcquiring(relation.predicate):
|
||||
if revert:
|
||||
removeOwner(relation.second, self.principalId)
|
||||
else:
|
||||
assignOwner(relation.second, self.principalId)
|
||||
|
||||
@Lazy
|
||||
def acquiringPredicates(self):
|
||||
names = ('ispartof',)
|
||||
return [self.conceptManager.get(n) for n in names]
|
||||
|
||||
@Lazy
|
||||
def principalId(self):
|
||||
return getCurrentPrincipal().id
|
||||
|
||||
|
||||
def isAcquiring(predicate):
|
||||
# TODO: use a predicate property for this.
|
||||
# TODO: use a predicate option for this.
|
||||
return getName(predicate) in ('ispartof',)
|
||||
|
|
|
@ -271,11 +271,6 @@ class IBaseResource(ILoopsObject):
|
|||
source="loops.resourceTypeSource",
|
||||
required=False)
|
||||
|
||||
def getType():
|
||||
""" Return a concept that provides the object's type, i.e. the
|
||||
resourceType attribute.
|
||||
"""
|
||||
|
||||
data = schema.Bytes(
|
||||
title=_(u'Data'),
|
||||
description=_(u'Resource raw data'),
|
||||
|
@ -290,6 +285,38 @@ class IBaseResource(ILoopsObject):
|
|||
missing_value='',
|
||||
required=False)
|
||||
|
||||
def getType():
|
||||
""" Return a concept that provides the object's type, i.e. the
|
||||
resourceType attribute.
|
||||
"""
|
||||
|
||||
def getClients(relationships=None):
|
||||
""" Return a sequence of objects that the resource is the target of.
|
||||
"""
|
||||
|
||||
def getConcepts(predicates=None):
|
||||
""" Return a tuple of concepts related to self as parent concepts,
|
||||
optionally restricted to the predicates given.
|
||||
"""
|
||||
|
||||
def getConceptRelations(predicates=None, concepts=None):
|
||||
""" Return a sequence of relations to concepts assigned to self
|
||||
as parent concepts, optionally restricted to the predicates given
|
||||
or to a certain concept.
|
||||
"""
|
||||
|
||||
def assignConcept(concept, predicate):
|
||||
""" Assign an existing concept to self using the predicate given.
|
||||
The assigned concept will be a parent concept of self.
|
||||
|
||||
The predicate defaults to the concept manager's default predicate.
|
||||
"""
|
||||
|
||||
def deassignConcept(concept, predicates=None):
|
||||
""" Remove the concept relations to the concept given from self,
|
||||
optionally restricting them to the predicates given.
|
||||
"""
|
||||
|
||||
|
||||
class IBaseResourceSchema(Interface):
|
||||
""" New schema for resources; to be used by sub-interfaces that will
|
||||
|
@ -334,33 +361,6 @@ class IResource(ILoopsObject, IPotentialTarget):
|
|||
available via a view or a concept.
|
||||
"""
|
||||
|
||||
def getClients(relationships=None):
|
||||
""" Return a sequence of objects that the resource is the target of.
|
||||
"""
|
||||
|
||||
def getConcepts(predicates=None):
|
||||
""" Return a tuple of concepts related to self as parent concepts,
|
||||
optionally restricted to the predicates given.
|
||||
"""
|
||||
|
||||
def getConceptRelations(predicates=None, concepts=None):
|
||||
""" Return a sequence of relations to concepts assigned to self
|
||||
as parent concepts, optionally restricted to the predicates given
|
||||
or to a certain concept.
|
||||
"""
|
||||
|
||||
def assignConcept(concept, predicate):
|
||||
""" Assign an existing concept to self using the predicate given.
|
||||
The assigned concept will be a parent concept of self.
|
||||
|
||||
The predicate defaults to the concept manager's default predicate.
|
||||
"""
|
||||
|
||||
def deassignConcept(concept, predicates=None):
|
||||
""" Remove the concept relations to the concept given from self,
|
||||
optionally restricting them to the predicates given.
|
||||
"""
|
||||
|
||||
|
||||
class IDocumentSchema(IResourceSchema):
|
||||
|
||||
|
|
|
@ -25,13 +25,13 @@
|
|||
for="loops.interfaces.INode"
|
||||
name="register_user.html"
|
||||
class="loops.organize.browser.member.MemberRegistration"
|
||||
permission="zope.Public" />
|
||||
permission="zope.View" />
|
||||
|
||||
<browser:page
|
||||
for="loops.interfaces.INode"
|
||||
name="change_password.html"
|
||||
class="loops.organize.browser.member.PasswordChange"
|
||||
permission="zope.Public" />
|
||||
permission="zope.View" />
|
||||
|
||||
<zope:adapter
|
||||
name="task.html"
|
||||
|
|
|
@ -59,6 +59,11 @@ def getGroupsFolder(context=None, name='gloops'):
|
|||
return getPrincipalFolder(authPluginId=name, ignoreErrors=True)
|
||||
|
||||
|
||||
def getGroupId(group):
|
||||
gf = group.__parent__
|
||||
return ''.join((gf.__parent__.prefix, gf._groupid(group)))
|
||||
|
||||
|
||||
def getInternalPrincipal(id, context=None, pau=None):
|
||||
if pau is None:
|
||||
pau = component.getUtility(IAuthentication, context=context)
|
||||
|
|
|
@ -50,9 +50,8 @@ 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()
|
||||
setter = ISecuritySetter(adapted(self.context))
|
||||
setter.propagateSecurity()
|
||||
return value
|
||||
|
||||
|
||||
|
@ -99,9 +98,8 @@ class PermissionView(object):
|
|||
def update(self, testing=None):
|
||||
value = self.delegate.update(testing)
|
||||
if value:
|
||||
setter = ISecuritySetter(self.adapted, None)
|
||||
if setter is not None:
|
||||
setter.propagateRolePermissions()
|
||||
setter = ISecuritySetter(self.adapted)
|
||||
setter.propagateSecurity()
|
||||
return value
|
||||
|
||||
@Lazy
|
||||
|
|
|
@ -97,6 +97,14 @@ def setRolePermission(rpm, p, r, setting):
|
|||
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)
|
||||
|
@ -137,24 +145,21 @@ def restrictView(obj, roles=allRolesExceptOwnerAndMaster, revert=False):
|
|||
def setDefaultSecurity(obj, event):
|
||||
aObj = adapted(obj)
|
||||
setter = ISecuritySetter(aObj)
|
||||
setter.setDefaultRolePermissions()
|
||||
setter.setDefaultPrincipalRoles()
|
||||
setter.setDefaultSecurity()
|
||||
|
||||
|
||||
@component.adapter(IConcept, IAssignmentEvent)
|
||||
def grantAcquiredSecurity(obj, event):
|
||||
aObj = adapted(obj)
|
||||
setter = ISecuritySetter(aObj)
|
||||
setter.setAcquiredRolePermissions(event.relation)
|
||||
setter.setAcquiredPrincipalRoles(event.relation)
|
||||
setter.setAcquiredSecurity(event.relation)
|
||||
|
||||
|
||||
@component.adapter(IConcept, IDeassignmentEvent)
|
||||
def revokeAcquiredSecurity(obj, event):
|
||||
aObj = adapted(obj)
|
||||
setter = ISecuritySetter(aObj)
|
||||
setter.setAcquiredRolePermissions(event.relation, revert=True)
|
||||
setter.setAcquiredPrincipalRoles(event.relation, revert=True)
|
||||
setter.setAcquiredSecurity(event.relation, revert=True)
|
||||
|
||||
|
||||
# helper stuff
|
||||
|
@ -168,12 +173,12 @@ class WorkspaceInformation(Persistent):
|
|||
|
||||
__name__ = u'workspace_information'
|
||||
|
||||
propagatePrincipalRoles = False
|
||||
propagateRolePermissions = 'workspace'
|
||||
|
||||
def __init__(self, parent):
|
||||
self.__parent__ = parent
|
||||
self.workspaceGroups = PersistentList()
|
||||
self.workspaceGroupNames = PersistentList()
|
||||
|
||||
def getName(self):
|
||||
return self.__name__
|
||||
|
||||
|
|
|
@ -30,6 +30,11 @@ from loops.util import _
|
|||
|
||||
class ISecuritySetter(Interface):
|
||||
|
||||
def setDefaultSecurity():
|
||||
""" Set some default role permission assignments (grants) on the
|
||||
context object.
|
||||
"""
|
||||
|
||||
def setDefaultRolePermissions():
|
||||
""" Set some default role permission assignments (grants) on the
|
||||
context object.
|
||||
|
@ -40,46 +45,31 @@ class ISecuritySetter(Interface):
|
|||
(e.g. the user that created the object).
|
||||
"""
|
||||
|
||||
def acquireRolePermissions():
|
||||
def acquireRolePermissions(revert=False):
|
||||
""" Check (immediate) parents's settings and set role permission
|
||||
assignments on the context object accordingly.
|
||||
"""
|
||||
|
||||
def setAcquiredRolePermissions(relation, revert=False, updated=None):
|
||||
def setAcquiredSecurity(relation, revert=False, updated=None):
|
||||
""" Grant role permissions on children/resources for the relation given.
|
||||
|
||||
If the ``revert`` argument is true unset the corresponding settings.
|
||||
Do not update objects in the ``updated`` collection if present.
|
||||
"""
|
||||
|
||||
def setAcquiredPrincipalRoles(relation, revert=False, updated=None):
|
||||
""" Assign roles on children/resources for the relation given.
|
||||
|
||||
If the ``revert`` argument is true unset the corresponding settings.
|
||||
Do not update objects in the ``updated`` collection if present.
|
||||
"""
|
||||
|
||||
def propagateRolePermissions(updated=None):
|
||||
def propagateSecurity(revert=False, updated=None):
|
||||
""" Update role permissions on all sub-objects according to the
|
||||
current setting of the context object.
|
||||
|
||||
Ignore objects in the ``updated`` collection if present.
|
||||
"""
|
||||
|
||||
def propagatePrincipalRoles(updated=None):
|
||||
""" Update roles on all sub-objects according to the
|
||||
current setting of the context object.
|
||||
|
||||
Ignore objects in the ``updated`` collection if present.
|
||||
"""
|
||||
|
||||
|
||||
class IWorkspaceInformation(Interface):
|
||||
""" Additional information belonging to a concept that controls
|
||||
security-related stuff for sub-objects.
|
||||
"""
|
||||
|
||||
propagatePrincipalRoles = Attribute('Should acquired principal roles be '
|
||||
'propagated to children?')
|
||||
propagateRolePermissions = Attribute('Which role permissions should be '
|
||||
'propagated to children?')
|
||||
|
||||
|
|
|
@ -24,16 +24,18 @@ $Id$
|
|||
"""
|
||||
|
||||
from zope.app.security.settings import Allow, Deny, Unset
|
||||
from zope.app.securitypolicy.interfaces import IRolePermissionMap
|
||||
from zope.app.securitypolicy.interfaces import IRolePermissionManager
|
||||
from zope.app.securitypolicy.interfaces import \
|
||||
IRolePermissionMap, IRolePermissionManager, \
|
||||
IPrincipalRoleMap, IPrincipalRoleManager
|
||||
from zope import component
|
||||
from zope.component import adapts
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.interface import implements, Interface
|
||||
from zope.security.proxy import isinstance
|
||||
|
||||
from loops.common import adapted, AdapterBase
|
||||
from loops.security.common import overrides, setRolePermission
|
||||
from loops.common import adapted, AdapterBase, baseObject
|
||||
from loops.organize.util import getPrincipalFolder, getGroupsFolder, getGroupId
|
||||
from loops.security.common import overrides, setRolePermission, setPrincipalRole
|
||||
from loops.interfaces import IConceptSchema, IBaseResourceSchema, ILoopsAdapter
|
||||
from loops.security.interfaces import ISecuritySetter
|
||||
|
||||
|
@ -46,25 +48,39 @@ class BaseSecuritySetter(object):
|
|||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
@Lazy
|
||||
def baseObject(self):
|
||||
return baseObject(self.context)
|
||||
|
||||
@Lazy
|
||||
def conceptManager(self):
|
||||
return self.baseObject.getLoopsRoot().getConceptManager()
|
||||
|
||||
@Lazy
|
||||
def acquiringPredicates(self):
|
||||
names = ('hasType', 'standard',)
|
||||
return [self.conceptManager.get(n) for n in names]
|
||||
|
||||
def setDefaultRolePermissions(self):
|
||||
pass
|
||||
|
||||
def setDefaultPrincipalRoles(self):
|
||||
pass
|
||||
|
||||
def setDefaultSecurity(self):
|
||||
self.setDefaultRolePermissions()
|
||||
self.setDefaultPrincipalRoles()
|
||||
|
||||
def setAcquiredSecurity(self, relation, revert=False, updated=None):
|
||||
pass
|
||||
|
||||
def propagateSecurity(self, revert=False, updated=None):
|
||||
pass
|
||||
|
||||
def acquireRolePermissions(self):
|
||||
pass
|
||||
|
||||
def setAcquiredRolePermissions(self, relation, revert=False, updated=None):
|
||||
pass
|
||||
|
||||
def setAcquiredPrincipalRoles(self, relation, revert=False, updated=None):
|
||||
pass
|
||||
|
||||
def propagateRolePermissions(self, updated=None):
|
||||
pass
|
||||
|
||||
def propagatePrincipalRoles(self, updated=None):
|
||||
def copyPrincipalRoles(self, source, revert=False):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -72,17 +88,21 @@ class LoopsObjectSecuritySetter(BaseSecuritySetter):
|
|||
|
||||
parents = []
|
||||
|
||||
@Lazy
|
||||
def baseObject(self):
|
||||
obj = self.context
|
||||
if isinstance(obj, AdapterBase):
|
||||
obj = obj.context
|
||||
return obj
|
||||
|
||||
@Lazy
|
||||
def rolePermissionManager(self):
|
||||
return IRolePermissionManager(self.baseObject)
|
||||
|
||||
@Lazy
|
||||
def principalRoleManager(self):
|
||||
return IPrincipalRoleManager(self.baseObject)
|
||||
|
||||
@Lazy
|
||||
def workspacePrincipals(self):
|
||||
gFolder = getGroupsFolder(self.baseObject, 'gloops_ws')
|
||||
if gFolder is None:
|
||||
return []
|
||||
return [getGroupId(g) for g in gFolder.values()]
|
||||
|
||||
def setDefaultRolePermissions(self):
|
||||
rpm = self.rolePermissionManager
|
||||
for p, r, s in rpm.getRolesAndPermissions():
|
||||
|
@ -109,38 +129,44 @@ class LoopsObjectSecuritySetter(BaseSecuritySetter):
|
|||
for (p, r), s in settings.items():
|
||||
setRolePermission(self.rolePermissionManager, p, r, s)
|
||||
|
||||
def copyPrincipalRoles(self, source, revert=False):
|
||||
prm = IPrincipalRoleMap(baseObject(source.context))
|
||||
for r, p, s in prm.getPrincipalsAndRoles():
|
||||
if p in self.workspacePrincipals:
|
||||
if revert:
|
||||
setPrincipalRole(self.principalRoleManager, r, p, Unset)
|
||||
else:
|
||||
setPrincipalRole(self.principalRoleManager, r, p, s)
|
||||
|
||||
|
||||
class ConceptSecuritySetter(LoopsObjectSecuritySetter):
|
||||
|
||||
adapts(IConceptSchema)
|
||||
|
||||
def setAcquiredRolePermissions(self, relation, revert=False, updated=None):
|
||||
def setAcquiredSecurity(self, relation, revert=False, updated=None):
|
||||
if updated and relation.second in updated:
|
||||
return
|
||||
setter = ISecuritySetter(adapted(relation.second), None)
|
||||
if setter is not None:
|
||||
if relation.predicate not in self.acquiringPredicates:
|
||||
return
|
||||
setter = ISecuritySetter(adapted(relation.second))
|
||||
setter.setDefaultRolePermissions()
|
||||
setter.acquireRolePermissions()
|
||||
setter.propagateRolePermissions(updated)
|
||||
setter.copyPrincipalRoles(self, revert)
|
||||
setter.propagateSecurity(revert, updated)
|
||||
|
||||
def setAcquiredPrincipalRoles(self, relation, revert=False, updated=None):
|
||||
pass
|
||||
|
||||
def propagateRolePermissions(self, updated=None):
|
||||
def propagateSecurity(self, revert=False, updated=None):
|
||||
if updated is None:
|
||||
updated = set()
|
||||
obj = self.baseObject
|
||||
updated.add(obj)
|
||||
for r in obj.getChildRelations():
|
||||
self.setAcquiredRolePermissions(r, updated=updated)
|
||||
for r in obj.getResourceRelations():
|
||||
self.setAcquiredRolePermissions(r, updated=updated)
|
||||
|
||||
def propagatePrincipalRoles(self, updated=None):
|
||||
pass
|
||||
for r in obj.getChildRelations(self.acquiringPredicates):
|
||||
self.setAcquiredSecurity(r, revert, updated)
|
||||
for r in obj.getResourceRelations(self.acquiringPredicates):
|
||||
self.setAcquiredSecurity(r, revert, updated)
|
||||
|
||||
@Lazy
|
||||
def parents(self):
|
||||
return self.baseObject.getParents()
|
||||
return self.baseObject.getParents(self.acquiringPredicates)
|
||||
|
||||
|
||||
class ResourceSecuritySetter(LoopsObjectSecuritySetter):
|
||||
|
@ -149,5 +175,5 @@ class ResourceSecuritySetter(LoopsObjectSecuritySetter):
|
|||
|
||||
@Lazy
|
||||
def parents(self):
|
||||
return self.baseObject.getConcepts()
|
||||
return self.baseObject.getConcepts(self.acquiringPredicates)
|
||||
|
||||
|
|
|
@ -52,13 +52,13 @@ domain concept (if present, otherwise the top-level type concept):
|
|||
['children', 'description', 'id', 'name', 'parents', 'resources',
|
||||
'title', 'type', 'viewName']
|
||||
>>> startObj['id'], startObj['name'], startObj['title'], startObj['type']
|
||||
('3', u'domain', u'Domain', '0')
|
||||
('4', u'domain', u'Domain', '0')
|
||||
|
||||
There are a few standard objects we can retrieve directly:
|
||||
|
||||
>>> defaultPred = xrf.getDefaultPredicate()
|
||||
>>> defaultPred['id'], defaultPred['name']
|
||||
('14', u'standard')
|
||||
('3', u'standard')
|
||||
>>> typePred = xrf.getTypePredicate()
|
||||
>>> typePred['id'], typePred['name']
|
||||
('1', u'hasType')
|
||||
|
@ -78,12 +78,12 @@ applied in an explicit assignment.
|
|||
|
||||
We can also retrieve a certain object by its id or its name:
|
||||
|
||||
>>> obj2 = xrf.getObjectById('5')
|
||||
>>> obj2 = xrf.getObjectById('4')
|
||||
>>> obj2['id'], obj2['name']
|
||||
('5', u'note')
|
||||
('4', u'domain')
|
||||
>>> textdoc = xrf.getObjectByName(u'textdocument')
|
||||
>>> textdoc['id'], textdoc['name']
|
||||
('9', u'textdocument')
|
||||
('10', u'textdocument')
|
||||
|
||||
All methods that retrieve one object also returns its children and parents:
|
||||
|
||||
|
@ -115,7 +115,7 @@ We can also retrieve children and parents explicitely:
|
|||
[u'customer', u'domain', u'file', u'note', u'person', u'predicate',
|
||||
u'task', u'textdocument', u'topic', u'type']
|
||||
|
||||
>>> pa = xrf.getParents('7')
|
||||
>>> pa = xrf.getParents('6')
|
||||
>>> len(pa)
|
||||
1
|
||||
>>> pa[0]['name']
|
||||
|
|
Loading…
Add table
Reference in a new issue