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:
helmutm 2009-12-13 15:41:30 +00:00
parent bea4f70ba3
commit 504092b268
11 changed files with 150 additions and 119 deletions

View file

@ -60,7 +60,7 @@ from cybertools.typology.interfaces import IType, ITypeManager
from loops.common import adapted from loops.common import adapted
from loops.config.base import DummyOptions from loops.config.base import DummyOptions
from loops.i18n.browser import I18NView 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.organize.tracking import access
from loops.resource import Resource from loops.resource import Resource
from loops.security.common import canAccessObject, canListObject, canWriteObject from loops.security.common import canAccessObject, canListObject, canWriteObject
@ -413,6 +413,8 @@ class BaseView(GenericView, I18NView):
@Lazy @Lazy
def options(self): def options(self):
if ITypeConcept.providedBy(self.adapted):
return DummyOptions()
return component.queryAdapter(self.adapted, IOptions) or DummyOptions() return component.queryAdapter(self.adapted, IOptions) or DummyOptions()
@Lazy @Lazy

View file

@ -12,6 +12,8 @@
<zope:class class="loops.compound.blog.post.BlogPost"> <zope:class class="loops.compound.blog.post.BlogPost">
<require permission="zope.View" <require permission="zope.View"
interface="loops.compound.blog.interfaces.IBlogPost" /> interface="loops.compound.blog.interfaces.IBlogPost" />
<require permission="zope.View"
attributes="context" />
<require permission="zope.ManageContent" <require permission="zope.ManageContent"
set_schema="loops.compound.blog.interfaces.IBlogPost" /> set_schema="loops.compound.blog.interfaces.IBlogPost" />
</zope:class> </zope:class>

View file

@ -43,24 +43,27 @@ class BlogPostSecuritySetter(BaseSecuritySetter):
def setDefaultPrincipalRoles(self): def setDefaultPrincipalRoles(self):
assignOwner(self.context.context, self.principalId) assignOwner(self.context.context, self.principalId)
def setAcquiredRolePermissions(self, relation, revert=False): def setAcquiredSecurity(self, relation, revert=False):
if isAcquiring(relation.predicate): #if self.isAcquiring(relation.predicate):
if relation.predicate in self.acquiringPredicates:
allowEditingForOwner(relation.second, revert=revert) allowEditingForOwner(relation.second, revert=revert)
if self.context.private: if self.context.private:
restrictView(relation.second, revert=revert) restrictView(relation.second, revert=revert)
def setAcquiredPrincipalRoles(self, relation, revert=False):
if isAcquiring(relation.predicate):
if revert: if revert:
removeOwner(relation.second, self.principalId) removeOwner(relation.second, self.principalId)
else: else:
assignOwner(relation.second, self.principalId) assignOwner(relation.second, self.principalId)
@Lazy
def acquiringPredicates(self):
names = ('ispartof',)
return [self.conceptManager.get(n) for n in names]
@Lazy @Lazy
def principalId(self): def principalId(self):
return getCurrentPrincipal().id return getCurrentPrincipal().id
def isAcquiring(predicate): def isAcquiring(predicate):
# TODO: use a predicate property for this. # TODO: use a predicate option for this.
return getName(predicate) in ('ispartof',) return getName(predicate) in ('ispartof',)

View file

@ -271,11 +271,6 @@ class IBaseResource(ILoopsObject):
source="loops.resourceTypeSource", source="loops.resourceTypeSource",
required=False) required=False)
def getType():
""" Return a concept that provides the object's type, i.e. the
resourceType attribute.
"""
data = schema.Bytes( data = schema.Bytes(
title=_(u'Data'), title=_(u'Data'),
description=_(u'Resource raw data'), description=_(u'Resource raw data'),
@ -290,6 +285,38 @@ class IBaseResource(ILoopsObject):
missing_value='', missing_value='',
required=False) 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): class IBaseResourceSchema(Interface):
""" New schema for resources; to be used by sub-interfaces that will """ 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. 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): class IDocumentSchema(IResourceSchema):

View file

@ -25,13 +25,13 @@
for="loops.interfaces.INode" for="loops.interfaces.INode"
name="register_user.html" name="register_user.html"
class="loops.organize.browser.member.MemberRegistration" class="loops.organize.browser.member.MemberRegistration"
permission="zope.Public" /> permission="zope.View" />
<browser:page <browser:page
for="loops.interfaces.INode" for="loops.interfaces.INode"
name="change_password.html" name="change_password.html"
class="loops.organize.browser.member.PasswordChange" class="loops.organize.browser.member.PasswordChange"
permission="zope.Public" /> permission="zope.View" />
<zope:adapter <zope:adapter
name="task.html" name="task.html"

View file

@ -59,6 +59,11 @@ def getGroupsFolder(context=None, name='gloops'):
return getPrincipalFolder(authPluginId=name, ignoreErrors=True) 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): def getInternalPrincipal(id, context=None, pau=None):
if pau is None: if pau is None:
pau = component.getUtility(IAuthentication, context=context) pau = component.getUtility(IAuthentication, context=context)

View file

@ -50,9 +50,8 @@ class Granting(granting.Granting):
def status(self): def status(self):
value = super(Granting, self).status() value = super(Granting, self).status()
if value: if value:
setter = ISecuritySetter(adapted(self.context), None) setter = ISecuritySetter(adapted(self.context))
if setter is not None: setter.propagateSecurity()
setter.propagatePrincipalRoles()
return value return value
@ -99,9 +98,8 @@ class PermissionView(object):
def update(self, testing=None): def update(self, testing=None):
value = self.delegate.update(testing) value = self.delegate.update(testing)
if value: if value:
setter = ISecuritySetter(self.adapted, None) setter = ISecuritySetter(self.adapted)
if setter is not None: setter.propagateSecurity()
setter.propagateRolePermissions()
return value return value
@Lazy @Lazy

View file

@ -97,6 +97,14 @@ def setRolePermission(rpm, p, r, setting):
else: else:
rpm.unsetPermissionFromRole(p, r) 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): def assignOwner(obj, principalId):
prm = IPrincipalRoleManager(obj) prm = IPrincipalRoleManager(obj)
@ -137,24 +145,21 @@ def restrictView(obj, roles=allRolesExceptOwnerAndMaster, revert=False):
def setDefaultSecurity(obj, event): def setDefaultSecurity(obj, event):
aObj = adapted(obj) aObj = adapted(obj)
setter = ISecuritySetter(aObj) setter = ISecuritySetter(aObj)
setter.setDefaultRolePermissions() setter.setDefaultSecurity()
setter.setDefaultPrincipalRoles()
@component.adapter(IConcept, IAssignmentEvent) @component.adapter(IConcept, IAssignmentEvent)
def grantAcquiredSecurity(obj, event): def grantAcquiredSecurity(obj, event):
aObj = adapted(obj) aObj = adapted(obj)
setter = ISecuritySetter(aObj) setter = ISecuritySetter(aObj)
setter.setAcquiredRolePermissions(event.relation) setter.setAcquiredSecurity(event.relation)
setter.setAcquiredPrincipalRoles(event.relation)
@component.adapter(IConcept, IDeassignmentEvent) @component.adapter(IConcept, IDeassignmentEvent)
def revokeAcquiredSecurity(obj, event): def revokeAcquiredSecurity(obj, event):
aObj = adapted(obj) aObj = adapted(obj)
setter = ISecuritySetter(aObj) setter = ISecuritySetter(aObj)
setter.setAcquiredRolePermissions(event.relation, revert=True) setter.setAcquiredSecurity(event.relation, revert=True)
setter.setAcquiredPrincipalRoles(event.relation, revert=True)
# helper stuff # helper stuff
@ -168,12 +173,12 @@ class WorkspaceInformation(Persistent):
__name__ = u'workspace_information' __name__ = u'workspace_information'
propagatePrincipalRoles = False
propagateRolePermissions = 'workspace' propagateRolePermissions = 'workspace'
def __init__(self, parent): def __init__(self, parent):
self.__parent__ = parent self.__parent__ = parent
self.workspaceGroups = PersistentList() self.workspaceGroupNames = PersistentList()
def getName(self): def getName(self):
return self.__name__ return self.__name__

View file

@ -30,6 +30,11 @@ from loops.util import _
class ISecuritySetter(Interface): class ISecuritySetter(Interface):
def setDefaultSecurity():
""" Set some default role permission assignments (grants) on the
context object.
"""
def setDefaultRolePermissions(): def setDefaultRolePermissions():
""" Set some default role permission assignments (grants) on the """ Set some default role permission assignments (grants) on the
context object. context object.
@ -40,46 +45,31 @@ class ISecuritySetter(Interface):
(e.g. the user that created the object). (e.g. the user that created the object).
""" """
def acquireRolePermissions(): def acquireRolePermissions(revert=False):
""" Check (immediate) parents's settings and set role permission """ Check (immediate) parents's settings and set role permission
assignments on the context object accordingly. 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. """ Grant role permissions on children/resources for the relation given.
If the ``revert`` argument is true unset the corresponding settings. If the ``revert`` argument is true unset the corresponding settings.
Do not update objects in the ``updated`` collection if present. Do not update objects in the ``updated`` collection if present.
""" """
def setAcquiredPrincipalRoles(relation, revert=False, updated=None): def propagateSecurity(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):
""" Update role permissions on all sub-objects according to the """ Update role permissions on all sub-objects according to the
current setting of the context object. current setting of the context object.
Ignore objects in the ``updated`` collection if present. 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): class IWorkspaceInformation(Interface):
""" Additional information belonging to a concept that controls """ Additional information belonging to a concept that controls
security-related stuff for sub-objects. security-related stuff for sub-objects.
""" """
propagatePrincipalRoles = Attribute('Should acquired principal roles be '
'propagated to children?')
propagateRolePermissions = Attribute('Which role permissions should be ' propagateRolePermissions = Attribute('Which role permissions should be '
'propagated to children?') 'propagated to children?')

View file

@ -24,16 +24,18 @@ $Id$
""" """
from zope.app.security.settings import Allow, Deny, Unset from zope.app.security.settings import Allow, Deny, Unset
from zope.app.securitypolicy.interfaces import IRolePermissionMap from zope.app.securitypolicy.interfaces import \
from zope.app.securitypolicy.interfaces import IRolePermissionManager IRolePermissionMap, IRolePermissionManager, \
IPrincipalRoleMap, IPrincipalRoleManager
from zope import component from zope import component
from zope.component import adapts from zope.component import adapts
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from zope.interface import implements, Interface from zope.interface import implements, Interface
from zope.security.proxy import isinstance from zope.security.proxy import isinstance
from loops.common import adapted, AdapterBase from loops.common import adapted, AdapterBase, baseObject
from loops.security.common import overrides, setRolePermission 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.interfaces import IConceptSchema, IBaseResourceSchema, ILoopsAdapter
from loops.security.interfaces import ISecuritySetter from loops.security.interfaces import ISecuritySetter
@ -46,25 +48,39 @@ class BaseSecuritySetter(object):
def __init__(self, context): def __init__(self, context):
self.context = 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): def setDefaultRolePermissions(self):
pass pass
def setDefaultPrincipalRoles(self): def setDefaultPrincipalRoles(self):
pass 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): def acquireRolePermissions(self):
pass pass
def setAcquiredRolePermissions(self, relation, revert=False, updated=None): def copyPrincipalRoles(self, source, revert=False):
pass
def setAcquiredPrincipalRoles(self, relation, revert=False, updated=None):
pass
def propagateRolePermissions(self, updated=None):
pass
def propagatePrincipalRoles(self, updated=None):
pass pass
@ -72,17 +88,21 @@ class LoopsObjectSecuritySetter(BaseSecuritySetter):
parents = [] parents = []
@Lazy
def baseObject(self):
obj = self.context
if isinstance(obj, AdapterBase):
obj = obj.context
return obj
@Lazy @Lazy
def rolePermissionManager(self): def rolePermissionManager(self):
return IRolePermissionManager(self.baseObject) 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): def setDefaultRolePermissions(self):
rpm = self.rolePermissionManager rpm = self.rolePermissionManager
for p, r, s in rpm.getRolesAndPermissions(): for p, r, s in rpm.getRolesAndPermissions():
@ -109,38 +129,44 @@ class LoopsObjectSecuritySetter(BaseSecuritySetter):
for (p, r), s in settings.items(): for (p, r), s in settings.items():
setRolePermission(self.rolePermissionManager, p, r, s) 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): class ConceptSecuritySetter(LoopsObjectSecuritySetter):
adapts(IConceptSchema) 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: if updated and relation.second in updated:
return return
setter = ISecuritySetter(adapted(relation.second), None) if relation.predicate not in self.acquiringPredicates:
if setter is not None: return
setter.acquireRolePermissions() setter = ISecuritySetter(adapted(relation.second))
setter.propagateRolePermissions(updated) setter.setDefaultRolePermissions()
setter.acquireRolePermissions()
setter.copyPrincipalRoles(self, revert)
setter.propagateSecurity(revert, updated)
def setAcquiredPrincipalRoles(self, relation, revert=False, updated=None): def propagateSecurity(self, revert=False, updated=None):
pass
def propagateRolePermissions(self, updated=None):
if updated is None: if updated is None:
updated = set() updated = set()
obj = self.baseObject obj = self.baseObject
updated.add(obj) updated.add(obj)
for r in obj.getChildRelations(): for r in obj.getChildRelations(self.acquiringPredicates):
self.setAcquiredRolePermissions(r, updated=updated) self.setAcquiredSecurity(r, revert, updated)
for r in obj.getResourceRelations(): for r in obj.getResourceRelations(self.acquiringPredicates):
self.setAcquiredRolePermissions(r, updated=updated) self.setAcquiredSecurity(r, revert, updated)
def propagatePrincipalRoles(self, updated=None):
pass
@Lazy @Lazy
def parents(self): def parents(self):
return self.baseObject.getParents() return self.baseObject.getParents(self.acquiringPredicates)
class ResourceSecuritySetter(LoopsObjectSecuritySetter): class ResourceSecuritySetter(LoopsObjectSecuritySetter):
@ -149,5 +175,5 @@ class ResourceSecuritySetter(LoopsObjectSecuritySetter):
@Lazy @Lazy
def parents(self): def parents(self):
return self.baseObject.getConcepts() return self.baseObject.getConcepts(self.acquiringPredicates)

View file

@ -52,13 +52,13 @@ domain concept (if present, otherwise the top-level type concept):
['children', 'description', 'id', 'name', 'parents', 'resources', ['children', 'description', 'id', 'name', 'parents', 'resources',
'title', 'type', 'viewName'] 'title', 'type', 'viewName']
>>> startObj['id'], startObj['name'], startObj['title'], startObj['type'] >>> 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: There are a few standard objects we can retrieve directly:
>>> defaultPred = xrf.getDefaultPredicate() >>> defaultPred = xrf.getDefaultPredicate()
>>> defaultPred['id'], defaultPred['name'] >>> defaultPred['id'], defaultPred['name']
('14', u'standard') ('3', u'standard')
>>> typePred = xrf.getTypePredicate() >>> typePred = xrf.getTypePredicate()
>>> typePred['id'], typePred['name'] >>> typePred['id'], typePred['name']
('1', u'hasType') ('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: 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'] >>> obj2['id'], obj2['name']
('5', u'note') ('4', u'domain')
>>> textdoc = xrf.getObjectByName(u'textdocument') >>> textdoc = xrf.getObjectByName(u'textdocument')
>>> textdoc['id'], textdoc['name'] >>> textdoc['id'], textdoc['name']
('9', u'textdocument') ('10', u'textdocument')
All methods that retrieve one object also returns its children and parents: 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'customer', u'domain', u'file', u'note', u'person', u'predicate',
u'task', u'textdocument', u'topic', u'type'] u'task', u'textdocument', u'topic', u'type']
>>> pa = xrf.getParents('7') >>> pa = xrf.getParents('6')
>>> len(pa) >>> len(pa)
1 1
>>> pa[0]['name'] >>> pa[0]['name']