[Checkins] SVN: z3c.objectpolicy/ The objectpolicy package makes it
easy to override the default
Adam Groszer
adamg at fw.hu
Sat Oct 28 06:11:09 EDT 2006
Log message for revision 70939:
The objectpolicy package makes it easy to override the default
zope.app.securitypolicy.zopepolicy on an object by object basis.
Changed:
A z3c.objectpolicy/branches/
A z3c.objectpolicy/tags/
A z3c.objectpolicy/trunk/
A z3c.objectpolicy/trunk/src/
A z3c.objectpolicy/trunk/src/z3c/
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/README.txt
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/__init__.py
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/configure.zcml
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/highlevel.txt
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/interfaces.py
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/lowlevel.txt
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/objectpolicy.py
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/tests.py
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/z3c.objectpolicy-configure.zcml
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/zopepolicy_copy.txt
A z3c.objectpolicy/trunk/src/z3c/objectpolicy/zopepolicy_objectpolicy.txt
-=-
Added: z3c.objectpolicy/trunk/src/z3c/objectpolicy/README.txt
===================================================================
--- z3c.objectpolicy/trunk/src/z3c/objectpolicy/README.txt 2006-10-28 10:07:05 UTC (rev 70938)
+++ z3c.objectpolicy/trunk/src/z3c/objectpolicy/README.txt 2006-10-28 10:11:06 UTC (rev 70939)
@@ -0,0 +1,35 @@
+The objectpolicy package makes it easy to override the default
+zope.app.securitypolicy.zopepolicy on an object by object basis.
+
+By default all objects use the zopepolicy. Objects that want to have
+their own policy should have a marker interface `IObjectPolicyMarker`
+and have an adapter to `IObjectPolicy`.
+
+-----------
+Levels
+-----------
+
+There are two levels supported.
+o The low level is the SecurityMap.getCell level.
+ Here are the permissions stored by principal or role.
+ This works also with ZopePolicy as the security policy.
+ Uses Allow, Deny, Unset values.
+ Permissions descend (with ZopePolicy) to child objects or views.
+ See:
+ - IObjectPolicy.getPrincipalPermission
+ - IObjectPolicy.getRolePermission
+ - lowlevel.txt
+ Installation:
+ Drop the z3c.objectpolicy-configure.zcml in the instance/etc folder.
+
+o The high level is the ISecurityPolicy.checkPermission level.
+ Here the permission is usually `summarized` for the principal by it's
+ roles, groups and object parent/child relations.
+ ZopePolicy has to be overridden by the ObjectsPolicy security policy.
+ Permissions do not decend to child objects or views.
+ Uses True -- access, False -- no access values.
+ See:
+ - IObjectPolicy.checkPermission
+ - highlevel.txt
+ Installation:
+ Override ZopePolicy in the instance/etc/securitypolicy.zcml
Property changes on: z3c.objectpolicy/trunk/src/z3c/objectpolicy/README.txt
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.objectpolicy/trunk/src/z3c/objectpolicy/__init__.py
===================================================================
Property changes on: z3c.objectpolicy/trunk/src/z3c/objectpolicy/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.objectpolicy/trunk/src/z3c/objectpolicy/configure.zcml
===================================================================
--- z3c.objectpolicy/trunk/src/z3c/objectpolicy/configure.zcml 2006-10-28 10:07:05 UTC (rev 70938)
+++ z3c.objectpolicy/trunk/src/z3c/objectpolicy/configure.zcml 2006-10-28 10:11:06 UTC (rev 70939)
@@ -0,0 +1,42 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="zope"
+ >
+
+ <adapter
+ factory=".objectpolicy.ObjectPrincipalPermissionManager"
+ provides="zope.app.securitypolicy.interfaces.IPrincipalPermissionManager"
+ for=".interfaces.IObjectPolicyMarker"
+ trusted="true"
+ />
+
+ <class class=".objectpolicy.ObjectPrincipalPermissionManager">
+ <require
+ permission="zope.Security"
+ attributes="grantPermissionToPrincipal denyPermissionToPrincipal
+ unsetPermissionForPrincipal"
+ />
+ <allow
+ interface="zope.app.securitypolicy.interfaces.IPrincipalPermissionMap"
+ />
+ </class>
+
+ <adapter
+ factory=".objectpolicy.ObjectRolePermissionManager"
+ provides="zope.app.securitypolicy.interfaces.IRolePermissionManager"
+ for=".interfaces.IObjectPolicyMarker"
+ trusted="true"
+ />
+
+ <class class=".objectpolicy.ObjectRolePermissionManager">
+ <require
+ permission="zope.Security"
+ attributes="grantPermissionToRole denyPermissionToRole
+ unsetPermissionFromRole"
+ />
+ <allow
+ interface="zope.app.securitypolicy.interfaces.IRolePermissionMap"
+ />
+ </class>
+
+</configure>
Property changes on: z3c.objectpolicy/trunk/src/z3c/objectpolicy/configure.zcml
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.objectpolicy/trunk/src/z3c/objectpolicy/highlevel.txt
===================================================================
--- z3c.objectpolicy/trunk/src/z3c/objectpolicy/highlevel.txt 2006-10-28 10:07:05 UTC (rev 70938)
+++ z3c.objectpolicy/trunk/src/z3c/objectpolicy/highlevel.txt 2006-10-28 10:11:06 UTC (rev 70939)
@@ -0,0 +1,280 @@
+----------------------------------
+Basic Setup (for high level tests)
+----------------------------------
+
+Good but not perfect example is the `the user needs to be able to modify
+it's own properties` problem.
+
+ >>> from zope import interface, component
+ >>> from zope.annotation.interfaces import IAttributeAnnotatable
+ >>> from zope.app.container.interfaces import IContained
+ >>> from zope.app.container.contained import Contained
+ >>> from z3c.objectpolicy.interfaces import IObjectPolicy
+ >>> from z3c.objectpolicy.interfaces import IObjectPolicyMarker
+ >>> class IPerson(interface.Interface):
+ ... """a person interface for a person class"""
+ ...
+ >>> class Person(Contained):
+ ... interface.implements(
+ ... IObjectPolicyMarker,
+ ... IAttributeAnnotatable,
+ ... IPerson)
+ ... def __init__(self, id, name):
+ ... self.id = id
+ ... self.name = name
+ ... self.groups = []
+ ...
+ >>> class otherKlass(object):
+ ... #This class does NOT implement IObjectPolicyMarker
+ ... interface.implements(
+ ... IAttributeAnnotatable)
+ ... def __init__(self, id):
+ ... self.id = id
+
+These permissions will be allowed for the principal on the Person object
+if the current principal == Person
+
+ >>> ALLOW_FOR_SELF = ["zope.View",
+ ... "zope.app.dublincore.view",
+ ... "zope.ManageContent"]
+
+Counter to see how many times the adapter fires
+
+ >>> TRIP_WIRE = 0
+
+This is the custom policy adapter which determines the permission.
+Watch out, this is just a little bit different from the lowlevel example!
+
+ >>> from z3c.objectpolicy.objectpolicy import DefaultObjectPolicyAdapter
+ >>> class PersonPolicy(DefaultObjectPolicyAdapter):
+ ... component.adapts(IPerson)
+ ... interface.implements(IObjectPolicy)
+ ...
+ ... def __init__(self, context):
+ ... #context is a Person
+ ... self.context = context
+ ...
+ ... def checkPermission(self, manager, permissionid):
+ ... #print permissionid, str(self.context)
+ ... return self.checkPermissionForParticipation(manager, permissionid)
+ ...
+ ... def checkPermissionForParticipant(self, manager, principal, permissionid):
+ ... global TRIP_WIRE
+ ... TRIP_WIRE += 1
+ ... if principal.id == self.context.id:
+ ... #we have the same Person in the participation
+ ... if permissionid in ALLOW_FOR_SELF:
+ ... #we have the Person and the Permission
+ ... return True
+ ...
+ ... #no Person or Permission found
+ ... #return the Z3 default permissions
+ ... return super(PersonPolicy, self).checkPermissionForParticipant(
+ ... manager, principal, permissionid)
+ ...
+ >>> component.provideAdapter(PersonPolicy)
+
+Install the ObjectPolicy, setup for testing.
+
+ >>> from z3c.objectpolicy.objectpolicy import ObjectPrincipalPermissionManager
+ >>> from z3c.objectpolicy.objectpolicy import ObjectRolePermissionManager
+ >>> from z3c.objectpolicy.objectpolicy import ObjectPolicy
+
+ >>> component.provideAdapter(ObjectPrincipalPermissionManager)
+ >>> component.provideAdapter(ObjectRolePermissionManager)
+
+ >>> bela = Person('b-id', 'bela')
+ >>> joe = Person('j-id', 'joe')
+
+ >>> class Participation:
+ ... interaction = None
+ >>> participation = Participation()
+ >>> participation.principal = joe
+ >>> import zope.security.management
+ >>> oldPolicy = zope.security.management.setSecurityPolicy(ObjectPolicy)
+ >>> zope.security.management.endInteraction()
+ >>> zope.security.management.newInteraction(participation)
+ >>> interaction = zope.security.management.getInteraction()
+
+Let's see a simple permission check
+-----------------------------------
+
+`joe` has `ManageContent` access to `joe` without granting any permission
+
+ >>> interaction.checkPermission('zope.ManageContent', joe)
+ True
+ >>> TRIP_WIRE
+ 1
+
+`joe` has no `SomePermission` access to `joe` because that's not listed
+in ALLOW_FOR_SELF
+
+ >>> interaction.checkPermission('myapp.SomePermission', joe)
+ False
+ >>> TRIP_WIRE
+ 2
+
+`joe` has NO `ManageContent` access to `bela`
+
+ >>> interaction.checkPermission('zope.ManageContent', bela)
+ False
+ >>> TRIP_WIRE
+ 3
+
+ >>> from zope.app.securitypolicy.interfaces import IPrincipalPermissionManager
+ >>> prinperBela = IPrincipalPermissionManager(bela)
+ >>> prinperJoe = IPrincipalPermissionManager(joe)
+ >>> prinperBela.grantPermissionToPrincipal('zope.ManageContent', 'j-id')
+
+When we grant permission `joe` to `bela`,
+`joe` has `ManageContent` access to `bela`
+
+ >>> interaction.checkPermission('zope.ManageContent', bela)
+ True
+ >>> TRIP_WIRE
+ 4
+
+Granting permission works for any arbitrary permission also
+
+ >>> prinperJoe.grantPermissionToPrincipal('myapp.SomePermission', 'j-id')
+ >>> interaction.checkPermission('myapp.SomePermission', joe)
+ True
+ >>> TRIP_WIRE
+ 5
+
+Objects without IObjectPolicyMarker behave as before.
+Without granting -- no permission
+
+ >>> otherObject = otherKlass('o-id')
+ >>> prinperOther = IPrincipalPermissionManager(otherObject)
+ >>> interaction.checkPermission('zope.ManageContent', otherObject)
+ False
+ >>> TRIP_WIRE
+ 5
+
+ >>> prinperOther.grantPermissionToPrincipal('zope.ManageContent', 'j-id')
+ >>> interaction.checkPermission('zope.ManageContent', otherObject)
+ True
+ >>> TRIP_WIRE
+ 5
+
+Check what's up when the marker is there, but no adapter
+
+ >>> class otherKlassWOadapter(object):
+ ... #This class does NOT implement IObjectPolicyMarker
+ ... interface.implements(
+ ... IAttributeAnnotatable,
+ ... IObjectPolicyMarker)
+ ... def __init__(self, id):
+ ... self.id = id
+
+ >>> otherObjectWO = otherKlassWOadapter('oa-id')
+ >>> interaction.checkPermission('zope.ManageContent', otherObjectWO)
+ False
+
+No permission, maybe something should be written to the log?
+
+Now a more complicated, parent-child setup
+------------------------------------------
+
+ >>> from zope.app.container.sample import SampleContainer
+ >>> from zope.location.location import locate
+ >>> class IPersonContainer(interface.Interface):
+ ... """a person container interface"""
+ ...
+ >>> class PersonContainer(SampleContainer):
+ ... interface.implements(
+ ... IAttributeAnnotatable,
+ ... IPersonContainer)
+ ... def __init__(self, id):
+ ... self.id = id
+ ... super(PersonContainer, self).__init__()
+ ...
+ >>> class BrowserView(object):
+ ... interface.implements(
+ ... IContained)
+ ...
+
+The layout is:
+ users(PersonContainer)
+ jack(Person)
+ editJack(BrowserView)
+ jane(Person)
+ editJane(BrowserView)
+
+ >>> users = PersonContainer('users')
+ >>> jack = Person('jack-id','jack')
+ >>> users['jack'] = jack
+ >>> locate(jack, users, 'jack')
+ >>> jane = Person('jane-id','jane')
+ >>> users['jane'] = jane
+ >>> locate(jane, users, 'jane')
+
+ >>> editJack = BrowserView()
+ >>> locate(editJack, jack, None)
+ >>> editJane = BrowserView()
+ >>> locate(editJane, jane, None)
+
+ >>> prinperUsers = IPrincipalPermissionManager(users)
+ >>> prinperJack = IPrincipalPermissionManager(jack)
+ >>> prinperJane = IPrincipalPermissionManager(jane)
+
+ >>> participation = Participation()
+
+The principal acting is jack
+
+ >>> participation.principal = jack
+ >>> zope.security.management.endInteraction()
+ >>> zope.security.management.newInteraction(participation)
+ >>> interaction = zope.security.management.getInteraction()
+
+When we don't grant permission, only jack has permission to itself and to it's
+editView.
+
+ >>> interaction.checkPermission('zope.ManageContent', users)
+ False
+ >>> interaction.checkPermission('zope.ManageContent', jack)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', editJack)
+ False
+ >>> interaction.checkPermission('zope.ManageContent', jane)
+ False
+ >>> interaction.checkPermission('zope.ManageContent', editJane)
+ False
+
+When we grant jane permission, jack still has the same.
+
+ >>> prinperUsers.grantPermissionToPrincipal('zope.ManageContent', 'jane-id')
+ >>> interaction.checkPermission('zope.ManageContent', users)
+ False
+ >>> interaction.checkPermission('zope.ManageContent', jack)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', editJack)
+ False
+ >>> interaction.checkPermission('zope.ManageContent', jane)
+ False
+ >>> interaction.checkPermission('zope.ManageContent', editJane)
+ False
+
+When we grant jack permission, he will have permission for the whole pack.
+
+ >>> prinperUsers.grantPermissionToPrincipal('zope.ManageContent', 'jack-id')
+ >>> interaction.checkPermission('zope.ManageContent', users)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', jack)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', editJack)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', jane)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', editJane)
+ True
+
+
+Cleanup
+-------
+
+We clean up the changes we made in these examples:
+
+ >>> zope.security.management.endInteraction()
+ >>> ignore = zope.security.management.setSecurityPolicy(oldPolicy)
Property changes on: z3c.objectpolicy/trunk/src/z3c/objectpolicy/highlevel.txt
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.objectpolicy/trunk/src/z3c/objectpolicy/interfaces.py
===================================================================
--- z3c.objectpolicy/trunk/src/z3c/objectpolicy/interfaces.py 2006-10-28 10:07:05 UTC (rev 70938)
+++ z3c.objectpolicy/trunk/src/z3c/objectpolicy/interfaces.py 2006-10-28 10:11:06 UTC (rev 70939)
@@ -0,0 +1,76 @@
+# -*- coding: UTF-8 -*-
+
+from zope.interface import Interface
+
+class IObjectPolicyMarker(Interface):
+ """Marker interface to mark objects wanting their own security policy"""
+
+class IObjectPolicy(Interface):
+ """ """
+ def getPrincipalPermission(self, manager, permissionid, principalid, default):
+ """Return whether security policy allows permission
+ on the context object to the principal.
+
+ Arguments:
+ manager -- The default Z3 AnnotationPrincipalPermissionManager
+ which gets the permission from the annotations
+ permissionid -- A permission ID
+ principalid -- A principal ID (participation.principal.id)
+ default -- The default value proposed by AnnotationPrincipalPermissionManager
+
+ return:
+ one of zope.app.securitypolicy.interfaces.[Allow, Deny, Unset]
+ """
+
+ def getRolePermission(self, manager, permissionid, roleid):
+ """Return whether security policy allows permission
+ on the context object to the role.
+
+ Arguments:
+ manager -- The default Z3 AnnotationRolePermissionManager
+ which gets the permission from the annotations
+ permissionid -- A permission ID
+ roleid -- A role ID (determined by ZopeSecurityPolicy)
+
+ return:
+ one of zope.app.securitypolicy.interfaces.[Allow, Deny, Unset]
+ """
+
+ def checkPermission(manager, permissionid):
+ """Return whether security policy allows permission
+ on the context object.
+
+ manager -- The default Z3 ZopePolicy,
+ which can be used to get default permissions
+ permissionid -- A permission ID
+
+ The method should go through manager.participations.principal's
+ to check permissions, see checkPermissionForParticipation
+
+ return:
+ True -- access granted
+ False -- no access
+ """
+
+ def checkPermissionForParticipation(manager, permissionid):
+ """Go thrugh manager.participations.principal's
+ call self.checkPermissionForParticipant for each one
+ convinience method
+
+ return:
+ True -- access granted
+ False -- no access
+ """
+
+ def checkPermissionForParticipant(self, manager, principal, permissionid):
+ """Called by checkPermissionForParticipation for each principal
+
+ manager -- The default Z3 ZopePolicy,
+ which can be used to get default permissions
+ principal -- A principal
+ permissionid -- A permission ID
+
+ return:
+ True -- access granted
+ False -- no access
+ """
\ No newline at end of file
Property changes on: z3c.objectpolicy/trunk/src/z3c/objectpolicy/interfaces.py
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.objectpolicy/trunk/src/z3c/objectpolicy/lowlevel.txt
===================================================================
--- z3c.objectpolicy/trunk/src/z3c/objectpolicy/lowlevel.txt 2006-10-28 10:07:05 UTC (rev 70938)
+++ z3c.objectpolicy/trunk/src/z3c/objectpolicy/lowlevel.txt 2006-10-28 10:11:06 UTC (rev 70939)
@@ -0,0 +1,276 @@
+---------------------------------
+Basic Setup (for low level tests)
+---------------------------------
+
+Good but not perfect example is the `the user needs to be able to modify
+it's own properties` problem.
+
+ >>> from zope import interface, component
+ >>> from zope.annotation.interfaces import IAttributeAnnotatable
+ >>> from zope.app.container.interfaces import IContained
+ >>> from zope.app.container.contained import Contained
+ >>> from z3c.objectpolicy.interfaces import IObjectPolicy
+ >>> from z3c.objectpolicy.interfaces import IObjectPolicyMarker
+ >>> class IPerson(interface.Interface):
+ ... """a person interface for a person class"""
+ ...
+ >>> class Person(Contained):
+ ... interface.implements(
+ ... IObjectPolicyMarker,
+ ... IAttributeAnnotatable,
+ ... IPerson)
+ ... def __init__(self, id, name):
+ ... self.id = id
+ ... self.name = name
+ ... self.groups = []
+ ...
+ >>> class otherKlass(object):
+ ... #This class does NOT implement IObjectPolicyMarker
+ ... interface.implements(
+ ... IAttributeAnnotatable)
+ ... def __init__(self, id):
+ ... self.id = id
+
+These permissions will be allowed for the principal on the Person object
+if the current principal == Person
+
+ >>> ALLOW_FOR_SELF = ["zope.View",
+ ... "zope.app.dublincore.view",
+ ... "zope.ManageContent"]
+
+Counter to see how many times the adapter fires
+
+ >>> TRIP_WIRE = 0
+
+This is the custom policy adapter which determines the permission.
+
+ >>> from zope.app.securitypolicy.interfaces import Allow, Deny, Unset
+ >>> from z3c.objectpolicy.objectpolicy import DefaultObjectPolicyAdapter
+ >>> class PersonPolicy(DefaultObjectPolicyAdapter):
+ ... component.adapts(IPerson)
+ ... interface.implements(IObjectPolicy)
+ ...
+ ... def __init__(self, context):
+ ... #context is a Person
+ ... self.context = context
+ ...
+ ... def getPrincipalPermission(self, manager, permissionid, principalid, default):
+ ... global TRIP_WIRE
+ ... TRIP_WIRE += 1
+ ... if principalid == self.context.id:
+ ... #we have the same Person in the participation
+ ... if permissionid in ALLOW_FOR_SELF:
+ ... #we have the Person and the Permission
+ ... return Allow
+ ...
+ ... #no Person or Permission found
+ ... #return the Z3 default permissions
+ ... return super(PersonPolicy, self).getPrincipalPermission(
+ ... manager, permissionid, principalid, default)
+ ...
+ >>> component.provideAdapter(PersonPolicy)
+
+Install the ObjectPolicy, setup for testing.
+
+ >>> from z3c.objectpolicy.objectpolicy import ObjectPrincipalPermissionManager
+ >>> from z3c.objectpolicy.objectpolicy import ObjectRolePermissionManager
+
+ >>> component.provideAdapter(ObjectPrincipalPermissionManager)
+ >>> component.provideAdapter(ObjectRolePermissionManager)
+
+ >>> bela = Person('b-id', 'bela')
+ >>> joe = Person('j-id', 'joe')
+
+ >>> class Participation:
+ ... interaction = None
+ >>> participation = Participation()
+ >>> participation.principal = joe
+ >>> import zope.security.management
+ >>> from zope.app.securitypolicy.zopepolicy import ZopeSecurityPolicy
+ >>> oldPolicy = zope.security.management.setSecurityPolicy(ZopeSecurityPolicy)
+ >>> zope.security.management.endInteraction()
+ >>> zope.security.management.newInteraction(participation)
+ >>> interaction = zope.security.management.getInteraction()
+
+Let's see a simple permission check
+-----------------------------------
+
+`joe` has `ManageContent` access to `joe` without granting any permission
+
+ >>> interaction.checkPermission('zope.ManageContent', joe)
+ True
+ >>> TRIP_WIRE
+ 1
+
+`joe` has no `SomePermission` access to `joe` because that's not listed
+in ALLOW_FOR_SELF
+
+ >>> interaction.checkPermission('myapp.SomePermission', joe)
+ False
+ >>> TRIP_WIRE
+ 2
+
+`joe` has NO `ManageContent` access to `bela`
+
+ >>> interaction.checkPermission('zope.ManageContent', bela)
+ False
+ >>> TRIP_WIRE
+ 3
+
+ >>> from zope.app.securitypolicy.interfaces import IPrincipalPermissionManager
+ >>> prinperBela = IPrincipalPermissionManager(bela)
+ >>> prinperJoe = IPrincipalPermissionManager(joe)
+ >>> prinperBela.grantPermissionToPrincipal('zope.ManageContent', 'j-id')
+
+When we grant permission `joe` to `bela`,
+`joe` has `ManageContent` access to `bela`
+
+ >>> interaction.checkPermission('zope.ManageContent', bela)
+ True
+ >>> TRIP_WIRE
+ 4
+
+Granting permission works for any arbitrary permission also
+
+ >>> prinperJoe.grantPermissionToPrincipal('myapp.SomePermission', 'j-id')
+ >>> interaction.checkPermission('myapp.SomePermission', joe)
+ True
+ >>> TRIP_WIRE
+ 5
+
+Objects without IObjectPolicyMarker behave as before.
+Without granting -- no permission
+
+ >>> otherObject = otherKlass('o-id')
+ >>> prinperOther = IPrincipalPermissionManager(otherObject)
+ >>> interaction.checkPermission('zope.ManageContent', otherObject)
+ False
+ >>> TRIP_WIRE
+ 5
+
+ >>> prinperOther.grantPermissionToPrincipal('zope.ManageContent', 'j-id')
+ >>> interaction.checkPermission('zope.ManageContent', otherObject)
+ True
+ >>> TRIP_WIRE
+ 5
+
+Check what's up when the marker is there, but no adapter
+
+ >>> class otherKlassWOadapter(object):
+ ... #This class does NOT implement IObjectPolicyMarker
+ ... interface.implements(
+ ... IAttributeAnnotatable,
+ ... IObjectPolicyMarker)
+ ... def __init__(self, id):
+ ... self.id = id
+
+ >>> otherObjectWO = otherKlassWOadapter('oa-id')
+ >>> interaction.checkPermission('zope.ManageContent', otherObjectWO)
+ False
+
+No permission, maybe something should be written to the log?
+
+Now a more complicated, parent-child setup
+------------------------------------------
+
+ >>> from zope.app.container.sample import SampleContainer
+ >>> from zope.location.location import locate
+ >>> class IPersonContainer(interface.Interface):
+ ... """a person container interface"""
+ ...
+ >>> class PersonContainer(SampleContainer):
+ ... interface.implements(
+ ... IAttributeAnnotatable,
+ ... IPersonContainer)
+ ... def __init__(self, id):
+ ... self.id = id
+ ... super(PersonContainer, self).__init__()
+ ...
+ >>> class BrowserView(object):
+ ... interface.implements(
+ ... IContained)
+ ...
+
+The layout is:
+ users(PersonContainer)
+ jack(Person)
+ editJack(BrowserView)
+ jane(Person)
+ editJane(BrowserView)
+
+ >>> users = PersonContainer('users')
+ >>> jack = Person('jack-id','jack')
+ >>> users['jack'] = jack
+ >>> locate(jack, users, 'jack')
+ >>> jane = Person('jane-id','jane')
+ >>> users['jane'] = jane
+ >>> locate(jane, users, 'jane')
+
+ >>> editJack = BrowserView()
+ >>> locate(editJack, jack, None)
+ >>> editJane = BrowserView()
+ >>> locate(editJane, jane, None)
+
+ >>> prinperUsers = IPrincipalPermissionManager(users)
+ >>> prinperJack = IPrincipalPermissionManager(jack)
+ >>> prinperJane = IPrincipalPermissionManager(jane)
+
+ >>> participation = Participation()
+
+The principal acting is jack
+
+ >>> participation.principal = jack
+ >>> zope.security.management.endInteraction()
+ >>> zope.security.management.newInteraction(participation)
+ >>> interaction = zope.security.management.getInteraction()
+
+When we don't grant permission, only jack has permission to itself and to it's
+editView.
+
+ >>> interaction.checkPermission('zope.ManageContent', users)
+ False
+ >>> interaction.checkPermission('zope.ManageContent', jack)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', editJack)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', jane)
+ False
+ >>> interaction.checkPermission('zope.ManageContent', editJane)
+ False
+
+When we grant jane permission, jack still has the same.
+
+ >>> prinperUsers.grantPermissionToPrincipal('zope.ManageContent', 'jane-id')
+ >>> interaction.checkPermission('zope.ManageContent', users)
+ False
+ >>> interaction.checkPermission('zope.ManageContent', jack)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', editJack)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', jane)
+ False
+ >>> interaction.checkPermission('zope.ManageContent', editJane)
+ False
+
+When we grant jack permission, he will have permission for the whole pack.
+
+ >>> prinperUsers.grantPermissionToPrincipal('zope.ManageContent', 'jack-id')
+ >>> interaction.checkPermission('zope.ManageContent', users)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', jack)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', editJack)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', jane)
+ True
+ >>> interaction.checkPermission('zope.ManageContent', editJane)
+ True
+
+
+Cleanup
+-------
+
+We clean up the changes we made in these examples:
+
+ >>> zope.security.management.endInteraction()
+ >>> ignore = zope.security.management.setSecurityPolicy(oldPolicy)
Property changes on: z3c.objectpolicy/trunk/src/z3c/objectpolicy/lowlevel.txt
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.objectpolicy/trunk/src/z3c/objectpolicy/objectpolicy.py
===================================================================
--- z3c.objectpolicy/trunk/src/z3c/objectpolicy/objectpolicy.py 2006-10-28 10:07:05 UTC (rev 70938)
+++ z3c.objectpolicy/trunk/src/z3c/objectpolicy/objectpolicy.py 2006-10-28 10:11:06 UTC (rev 70939)
@@ -0,0 +1,247 @@
+"""
+The objectpolicy package makes it easy to override the default
+zope.app.securitypolicy.zopepolicy on an object by object basis.
+
+By default all objects use the zopepolicy. Objects that want to have
+their own policy should have a marker interface `IObjectPolicyMarker`
+and have an adapter to `IObjectPolicy`.
+
+"""
+
+from zope import interface, component
+
+from zope.app.securitypolicy.zopepolicy import ZopeSecurityPolicy
+from zope.security.checker import CheckerPublic
+from zope.security.proxy import removeSecurityProxy
+from zope.security.management import system_user
+
+from z3c.objectpolicy.interfaces import IObjectPolicyMarker
+from z3c.objectpolicy.interfaces import IObjectPolicy
+
+from zope.app.securitypolicy.interfaces import Allow, Deny, Unset
+
+from zope.app.securitypolicy.interfaces import IPrincipalPermissionManager
+from zope.app.securitypolicy.principalpermission import AnnotationPrincipalPermissionManager
+from zope.app.securitypolicy.interfaces import IRolePermissionManager
+from zope.app.securitypolicy.rolepermission import AnnotationRolePermissionManager
+
+class ObjectPolicy(ZopeSecurityPolicy):
+
+ def checkZopePermission(self, permission, object):
+ return ZopeSecurityPolicy.checkPermission(self, permission, object)
+
+ def checkPermission(self, permission, object):
+ if permission is CheckerPublic:
+ return True
+
+ object = removeSecurityProxy(object)
+
+ if IObjectPolicyMarker.providedBy(object):
+ #print permission, str(object)
+ #if 'Modi' in permission:
+ # from pub.dbgpclient import brk
+ # brk()
+ try:
+ adapted = IObjectPolicy(object)
+ except TypeError:
+ return self.checkZopePermission(permission, object)
+
+ return adapted.checkPermission(self, permission)
+ else:
+ return self.checkZopePermission(permission, object)
+
+class ObjectPrincipalPermissionManager(AnnotationPrincipalPermissionManager):
+ component.adapts(IObjectPolicyMarker)
+ interface.implements(IPrincipalPermissionManager)
+
+ def __init__(self, context):
+ super(ObjectPrincipalPermissionManager, self).__init__(context)
+
+ try:
+ self._adapted = IObjectPolicy(context)
+ except TypeError:
+ self.getSetting = self.getZopePrincipalSetting
+
+ def getPrincipalsForPermission(self, permission_id):
+ """Get the principas that have a permission.
+
+ Return the list of (principal_id, setting) tuples that describe
+ security assertions for this permission.
+
+ If no principals have been set for this permission, then the empty
+ list is returned.
+ """
+ raise NotImplementedError("Seemed like nobody calls getPrincipalsForPermission")
+ return super(ObjectPrincipalPermissionManager, self).getPrincipalsForPermission(
+ permission_id)
+
+ def getPermissionsForPrincipal(self, principal_id):
+ """Get the permissions granted to a principal.
+
+ Return the list of (permission, setting) tuples that describe
+ security assertions for this principal.
+
+ If no permissions have been set for this principal, then the empty
+ list is returned.
+ """
+ raise NotImplementedError("Seemed like nobody calls getPermissionsForPrincipal")
+ return super(ObjectPrincipalPermissionManager, self).getPermissionsForPrincipal(
+ principal_id)
+
+ def getZopePrincipalSetting(self, permission_id, principal_id, default=Unset):
+ return super(ObjectPrincipalPermissionManager, self).getSetting(
+ permission_id, principal_id, default)
+
+ def getSetting(self, permission_id, principal_id, default=Unset):
+ """Get the setting for a permission and principal.
+
+ Get the setting (Allow/Deny/Unset) for a given permission and
+ principal.
+ """
+ return self._adapted.getPrincipalPermission(
+ self, permission_id, principal_id, default)
+
+ def getPrincipalsAndPermissions(self):
+ """Get all principal permission settings.
+
+ Get the principal security assertions here in the form
+ of a list of three tuple containing
+ (permission id, principal id, setting)
+ """
+ raise NotImplementedError("Seemed like nobody calls getPrincipalsAndPermissions")
+ return super(ObjectPrincipalPermissionManager, self).getPrincipalsAndPermissions()
+
+ #def grantPermissionToPrincipal(self, permission_id, principal_id):
+ # """Assert that the permission is allowed for the principal.
+ # """
+ #
+ #def denyPermissionToPrincipal(self, permission_id, principal_id):
+ # """Assert that the permission is denied to the principal.
+ # """
+ #
+ #def unsetPermissionForPrincipal(self, permission_id, principal_id):
+ # """Remove the permission (either denied or allowed) from the
+ # principal.
+ # """
+
+class ObjectRolePermissionManager(AnnotationRolePermissionManager):
+ component.adapts(IObjectPolicyMarker)
+ interface.implements(IRolePermissionManager)
+
+ def __init__(self, context):
+ super(ObjectRolePermissionManager, self).__init__(context)
+
+ try:
+ self._adapted = IObjectPolicy(context)
+ except TypeError:
+ self.getSetting = self.getZopeRoleSetting
+
+ def getPermissionsForRole(self, role_id):
+ """Get the premissions granted to a role.
+
+ Return a sequence of (permission id, setting) tuples for the given
+ role.
+
+ If no permissions have been granted to this
+ role, then the empty list is returned.
+ """
+ print "ROLE:getPermissionsForRole"
+ return super(ObjectRolePermissionManager, self).getPermissionsForRole(
+ role_id)
+
+ def getRolesForPermission(permission_id):
+ """Get the roles that have a permission.
+
+ Return a sequence of (role id, setting) tuples for the given
+ permission.
+
+ If no roles have been granted this permission, then the empty list is
+ returned.
+ """
+ print "ROLE:getRolesForPermission"
+ return super(ObjectRolePermissionManager, self).getRolesForPermission(
+ permission_id)
+
+ def getZopeRoleSetting(self, permission_id, role_id):
+ return super(ObjectRolePermissionManager, self).getSetting(
+ permission_id, role_id)
+
+ def getSetting(self, permission_id, role_id):
+ """Return the setting for the given permission id and role id
+
+ If there is no setting, Unset is returned
+ """
+ return self._adapted.getRolePermission(
+ self, permission_id, role_id)
+
+ def getRolesAndPermissions():
+ """Return a sequence of (permission_id, role_id, setting) here.
+
+ The settings are returned as a sequence of permission, role,
+ setting tuples.
+
+ If no principal/role assertions have been made here, then the empty
+ list is returned.
+ """
+ print "ROLE:getRolesAndPermissions"
+ return super(ObjectRolePermissionManager, self).getRolesAndPermissions()
+
+
+ #def grantPermissionToRole(permission_id, role_id):
+ # """Bind the permission to the role.
+ # """
+ #
+ #def denyPermissionToRole(permission_id, role_id):
+ # """Deny the permission to the role
+ # """
+ #
+ #def unsetPermissionFromRole(permission_id, role_id):
+ # """Clear the setting of the permission to the role.
+ # """
+
+class DefaultObjectPolicyAdapter(object):
+ interface.implements(IObjectPolicy)
+
+ def __init__(self, context):
+ #context is a Person
+ self.context = context
+
+ def getPrincipalPermission(self, manager, permissionid, principalid, default):
+ #return the Z3 default permissions
+ return manager.getZopePrincipalSetting(
+ permissionid, principalid, default)
+
+ def getRolePermission(self, manager, permissionid, roleid):
+ #return the Z3 default permissions
+ return manager.getZopeRoleSetting(
+ permissionid, roleid)
+
+ def checkPermission(self, manager, permissionid):
+ #print permissionid, str(self.context)
+ return manager.checkZopePermission(permissionid, self.context)
+
+ def checkPermissionForParticipation(self, manager, permissionid):
+ object = self.context
+ seen = {}
+ for participation in manager.participations:
+ principal = participation.principal
+ if principal is system_user:
+ continue # always allow system_user
+
+ if principal.id in seen:
+ continue
+
+ if not self.checkPermissionForParticipant(
+ manager, principal, permissionid,
+ ):
+ return False
+
+ seen[principal.id] = 1
+
+ return True
+
+ def checkPermissionForParticipant(self, manager, principal, permissionid):
+ return manager.cached_decision(
+ self.context, principal.id,
+ manager._groupsFor(principal), permissionid,
+ )
Property changes on: z3c.objectpolicy/trunk/src/z3c/objectpolicy/objectpolicy.py
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.objectpolicy/trunk/src/z3c/objectpolicy/tests.py
===================================================================
--- z3c.objectpolicy/trunk/src/z3c/objectpolicy/tests.py 2006-10-28 10:07:05 UTC (rev 70938)
+++ z3c.objectpolicy/trunk/src/z3c/objectpolicy/tests.py 2006-10-28 10:11:06 UTC (rev 70939)
@@ -0,0 +1,42 @@
+import unittest
+from zope import component
+from zope.app.testing import placelesssetup
+from zope.configuration import xmlconfig
+from zope.testing import module
+from zope.app.securitypolicy.tests.test_zopepolicy import setUp
+
+from z3c.objectpolicy.objectpolicy import ObjectPrincipalPermissionManager
+from z3c.objectpolicy.objectpolicy import ObjectRolePermissionManager
+
+def setUpOP(test):
+ setUp(test)
+
+ component.provideAdapter(ObjectPrincipalPermissionManager)
+ component.provideAdapter(ObjectRolePermissionManager)
+
+def tearDown(test):
+ placelesssetup.tearDown()
+
+def test_suite():
+ from zope.testing import doctest
+ return unittest.TestSuite((
+ doctest.DocFileSuite(
+ 'lowlevel.txt',
+ setUp=setUp, tearDown=tearDown,
+ ),
+ doctest.DocFileSuite(
+ 'highlevel.txt',
+ setUp=setUp, tearDown=tearDown,
+ ),
+ doctest.DocFileSuite(
+ 'zopepolicy_copy.txt',
+ setUp=setUpOP, tearDown=tearDown,
+ ),
+ doctest.DocFileSuite(
+ 'zopepolicy_objectpolicy.txt',
+ setUp=setUpOP, tearDown=tearDown,
+ ),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: z3c.objectpolicy/trunk/src/z3c/objectpolicy/tests.py
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.objectpolicy/trunk/src/z3c/objectpolicy/z3c.objectpolicy-configure.zcml
===================================================================
--- z3c.objectpolicy/trunk/src/z3c/objectpolicy/z3c.objectpolicy-configure.zcml 2006-10-28 10:07:05 UTC (rev 70938)
+++ z3c.objectpolicy/trunk/src/z3c/objectpolicy/z3c.objectpolicy-configure.zcml 2006-10-28 10:11:06 UTC (rev 70939)
@@ -0,0 +1 @@
+<include package="z3c.objectpolicy" />
\ No newline at end of file
Property changes on: z3c.objectpolicy/trunk/src/z3c/objectpolicy/z3c.objectpolicy-configure.zcml
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.objectpolicy/trunk/src/z3c/objectpolicy/zopepolicy_copy.txt
===================================================================
--- z3c.objectpolicy/trunk/src/z3c/objectpolicy/zopepolicy_copy.txt 2006-10-28 10:07:05 UTC (rev 70938)
+++ z3c.objectpolicy/trunk/src/z3c/objectpolicy/zopepolicy_copy.txt 2006-10-28 10:11:06 UTC (rev 70939)
@@ -0,0 +1,656 @@
+THIS IS A COPY of zope.app.securitypolicy.zopepolicy.txt
+Because all test there have to pass with the objectpolicy installed.
+
+Classic Zope Security Policy
+============================
+
+This package implements a role-based security policy similar to the
+policy found in Zope 2. The security policy is responsible for
+deciding whether an interaction has a permission on an object. This
+security policy does this using grant and denial information. Managers
+can grant or deny:
+
+ - roles to principals,
+
+ - permissions to principals, and
+
+ - permissions to roles
+
+Grants and denials are stored as annotations on objects. To store
+grants and denials, objects must be annotatable:
+
+ >>> import zope.interface
+ >>> from zope.annotation.interfaces import IAttributeAnnotatable
+ >>> class Ob:
+ ... zope.interface.implements(IAttributeAnnotatable)
+
+ >>> ob = Ob()
+
+We use objects to represent principals. These objects implement an
+interface named `IPrincipal`, but the security policy only uses the `id`
+and `groups` attributes:
+
+ >>> class Principal:
+ ... def __init__(self, id):
+ ... self.id = id
+ ... self.groups = []
+
+ >>> principal = Principal('bob')
+
+Roles and permissions are also represented by objects, however, for
+the purposes of the security policy, only string `ids` are used.
+
+The security policy provides a factory for creating interactions:
+
+ >>> import zope.app.securitypolicy.zopepolicy
+ >>> interaction = zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy()
+
+An interaction represents a specific interaction between some
+principals (normally users) and the system. Normally, we are only
+concerned with the interaction of one principal with the system, although
+we can have interactions of multiple principals. Multiple-principal
+interactions normally occur when untrusted users store code on a
+system for later execution. When untrusted code is executing, the
+authors of the code participate in the interaction. An
+interaction has a permission on an object only if all of the
+principals participating in the interaction have access to the object.
+
+The `checkPermission` method on interactions is used to test whether
+an interaction has a permission for an object. An interaction without
+participants always has every permission:
+
+ >>> interaction.checkPermission('P1', ob)
+ True
+
+In this example, 'P1' is a permission id.
+
+Normally, interactions have participants:
+
+ >>> class Participation:
+ ... interaction = None
+ >>> participation = Participation()
+ >>> participation.principal = principal
+ >>> interaction.add(participation)
+
+If we have participants, then we don't have a permission unless there
+are grants:
+
+ >>> interaction.checkPermission('P1', ob)
+ False
+
+Note, however, that we always have the CheckerPublic permission:
+
+ >>> from zope.security.checker import CheckerPublic
+ >>> interaction.checkPermission(CheckerPublic, ob)
+ True
+
+We make grants and denials on objects by adapting them to various
+granting interfaces. The objects returned from the adaptation are
+object-specific manager objects:
+
+ >>> from zope.app.securitypolicy import interfaces
+ >>> roleper = interfaces.IRolePermissionManager(ob)
+ >>> prinrole = interfaces.IPrincipalRoleManager(ob)
+ >>> prinper = interfaces.IPrincipalPermissionManager(ob)
+
+The computations involved in checking permissions can be
+significant. To reduce the computational cost, caching is used
+extensively. We could invalidate the cache as we make grants, but the
+adapters for making grants will automatically invalidate the cache of
+the current interaction. They use the security-management apis to do
+this. To take advantage of the cache invalidation, we'll need to let
+the security-management system manage our interactions. First, we'll
+set our security policy as the default:
+
+ >>> import zope.security.management
+ >>> oldpolicy = zope.security.management.setSecurityPolicy(
+ ... zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy)
+
+and then we'll create a new interaction:
+
+ >>> participation = Participation()
+ >>> participation.principal = principal
+ >>> zope.security.management.newInteraction(participation)
+ >>> interaction = zope.security.management.getInteraction()
+
+We normally provide access by granting permissions to roles for an object:
+
+ >>> roleper.grantPermissionToRole('P1', 'R1')
+
+and then granting roles to principals for an object (local roles):
+
+ >>> prinrole.assignRoleToPrincipal('R1', 'bob')
+
+The combination of these grants, which we call a role-based grant,
+provides the permission:
+
+ >>> interaction.checkPermission('P1', ob)
+ True
+
+We can also provide a permission directly:
+
+ >>> prinper.grantPermissionToPrincipal('P2', 'bob')
+ >>> interaction.checkPermission('P2', ob)
+ True
+
+Permission grants or denials override role-based grant or denials. So
+if we deny P1:
+
+ >>> prinper.denyPermissionToPrincipal('P1', 'bob')
+
+we cause the interaction to lack the permission, despite the role
+grants:
+
+ >>> interaction.checkPermission('P1', ob)
+ False
+
+Similarly, even if we have a role-based denial of P2:
+
+ >>> roleper.denyPermissionToRole('P2', 'R1')
+
+we still have access, because of the permission-based grant:
+
+ >>> interaction.checkPermission('P2', ob)
+ True
+
+A role-based denial doesn't actually deny a permission; rather it
+prevents the granting of a permission. So, if we have both grants and
+denials based on roles, we have access:
+
+ >>> roleper.grantPermissionToRole('P3', 'R1')
+ >>> roleper.grantPermissionToRole('P3', 'R2')
+ >>> roleper.denyPermissionToRole('P3', 'R3')
+ >>> prinrole.removeRoleFromPrincipal('R2', 'bob')
+ >>> prinrole.assignRoleToPrincipal('R3', 'bob')
+
+ >>> interaction.checkPermission('P3', ob)
+ True
+
+Global grants
+-------------
+
+Grants made to an object are said to be "local". We can also make
+global grants:
+
+ >>> from zope.app.securitypolicy.rolepermission \
+ ... import rolePermissionManager as roleperG
+ >>> from zope.app.securitypolicy.principalpermission \
+ ... import principalPermissionManager as prinperG
+ >>> from zope.app.securitypolicy.principalrole \
+ ... import principalRoleManager as prinroleG
+
+And the same rules apply to global grants and denials.
+
+ >>> roleperG.grantPermissionToRole('P1G', 'R1G', False)
+
+In these tests, we aren't bothering to define any roles, permissions,
+or principals, so we pass an extra argument that tells the granting
+routines not to check the validity of the values.
+
+ >>> prinroleG.assignRoleToPrincipal('R1G', 'bob', False)
+ >>> interaction.checkPermission('P1G', ob)
+ True
+
+ >>> prinperG.grantPermissionToPrincipal('P2G', 'bob', False)
+ >>> interaction.checkPermission('P2G', ob)
+ True
+
+ >>> prinperG.denyPermissionToPrincipal('P1G', 'bob', False)
+ >>> interaction.checkPermission('P1G', ob)
+ False
+
+ >>> roleperG.denyPermissionToRole('P2G', 'R1G', False)
+ >>> interaction.checkPermission('P2G', ob)
+ True
+
+ >>> roleperG.grantPermissionToRole('P3G', 'R1G', False)
+ >>> roleperG.grantPermissionToRole('P3G', 'R2G', False)
+ >>> roleperG.denyPermissionToRole('P3G', 'R3G', False)
+ >>> prinroleG.removeRoleFromPrincipal('R2G', 'bob', False)
+ >>> prinroleG.assignRoleToPrincipal('R3G', 'bob', False)
+ >>> interaction.checkPermission('P3G', ob)
+ True
+
+Local versus global grants
+--------------------------
+
+We, of course, acquire global grants by default:
+
+ >>> interaction.checkPermission('P1G', ob)
+ False
+ >>> interaction.checkPermission('P2G', ob)
+ True
+ >>> interaction.checkPermission('P3G', ob)
+ True
+
+Local role-based grants do not override global principal-specific denials:
+
+ >>> roleper.grantPermissionToRole('P1G', 'R1G')
+ >>> prinrole.assignRoleToPrincipal('R1G', 'bob')
+ >>> interaction.checkPermission('P1G', ob)
+ False
+
+And local role-based denials don't override global
+principal-grants:
+
+ >>> roleper.denyPermissionToRole('P2G', 'R1G')
+ >>> interaction.checkPermission('P2G', ob)
+ True
+
+A local role-based deny can cancel a global role-based grant:
+
+ >>> roleper.denyPermissionToRole('P3G', 'R1G')
+ >>> interaction.checkPermission('P3G', ob)
+ False
+
+and a local role-based grant can override a global role-based denial:
+
+ >>> roleperG.denyPermissionToRole('P4G', 'R1G', False)
+ >>> prinroleG.assignRoleToPrincipal('R1G', "bob", False)
+ >>> interaction.checkPermission('P4G', ob)
+ False
+ >>> roleper.grantPermissionToRole('P4G', 'R1G')
+ >>> interaction.checkPermission('P4G', ob)
+ True
+ >>> prinroleG.removeRoleFromPrincipal('R1G', "bob", False)
+ >>> interaction.checkPermission('P4G', ob)
+ True
+
+Of course, a local permission-based grant or denial overrides any
+global setting and overrides local role-based grants or denials:
+
+ >>> prinper.grantPermissionToPrincipal('P3G', 'bob')
+ >>> interaction.checkPermission('P3G', ob)
+ True
+
+ >>> prinper.denyPermissionToPrincipal('P2G', 'bob')
+ >>> interaction.checkPermission('P2G', ob)
+ False
+
+Sublocations
+------------
+
+We can have sub-locations. A sublocation of a location is an object
+whose `__parent__` attribute is the location:
+
+ >>> ob2 = Ob()
+ >>> ob2.__parent__ = ob
+
+By default, sublocations acquire grants from higher locations:
+
+ >>> interaction.checkPermission('P1', ob2)
+ False
+ >>> interaction.checkPermission('P2', ob2)
+ True
+ >>> interaction.checkPermission('P3', ob2)
+ True
+ >>> interaction.checkPermission('P1G', ob2)
+ False
+ >>> interaction.checkPermission('P2G', ob2)
+ False
+ >>> interaction.checkPermission('P3G', ob2)
+ True
+ >>> interaction.checkPermission('P4G', ob2)
+ True
+
+Sublocation role-based grants do not override their parent
+principal-specific denials:
+
+ >>> roleper2 = interfaces.IRolePermissionManager(ob2)
+ >>> prinrole2 = interfaces.IPrincipalRoleManager(ob2)
+ >>> prinper2 = interfaces.IPrincipalPermissionManager(ob2)
+
+ >>> roleper2.grantPermissionToRole('P1', 'R1')
+ >>> prinrole2.assignRoleToPrincipal('R1', 'bob')
+ >>> interaction.checkPermission('P1', ob2)
+ False
+
+And local role-based denials don't override their parents
+principal-grant:
+
+ >>> roleper2.denyPermissionToRole('P2', 'R1')
+ >>> interaction.checkPermission('P2', ob2)
+ True
+
+A local role-based deny can cancel a parent role-based grant:
+
+ >>> roleper2.denyPermissionToRole('P3', 'R1')
+ >>> interaction.checkPermission('P3', ob2)
+ False
+
+and a local role-based grant can override a parent role-based denial:
+
+ >>> roleper.denyPermissionToRole('P4', 'R1')
+ >>> prinrole.assignRoleToPrincipal('R1', 'bob')
+ >>> interaction.checkPermission('P4', ob2)
+ False
+ >>> roleper2.grantPermissionToRole('P4', 'R1')
+ >>> interaction.checkPermission('P4', ob2)
+ True
+ >>> prinrole.removeRoleFromPrincipal('R1', 'bob')
+ >>> interaction.checkPermission('P4', ob2)
+ True
+
+
+Of course, a local permission-based grant or denial overrides any
+global setting and overrides local role-based grants or denials:
+
+ >>> prinper.grantPermissionToPrincipal('P3', 'bob')
+ >>> interaction.checkPermission('P3', ob2)
+ True
+
+ >>> prinper.denyPermissionToPrincipal('P2', 'bob')
+ >>> interaction.checkPermission('P2', ob2)
+ False
+
+If an object is not annotatable, but does have a parent, it will get
+its grants from its parent:
+
+ >>> class C:
+ ... pass
+
+ >>> ob3 = C()
+ >>> ob3.__parent__ = ob
+
+ >>> interaction.checkPermission('P1', ob3)
+ False
+ >>> interaction.checkPermission('P2', ob3)
+ False
+ >>> interaction.checkPermission('P3', ob3)
+ True
+ >>> interaction.checkPermission('P1G', ob3)
+ False
+ >>> interaction.checkPermission('P2G', ob3)
+ False
+ >>> interaction.checkPermission('P3G', ob3)
+ True
+ >>> interaction.checkPermission('P4G', ob3)
+ True
+
+The same results will be had if there are multiple non-annotatable
+objects:
+
+ >>> ob3.__parent__ = C()
+ >>> ob3.__parent__.__parent__ = ob
+
+ >>> interaction.checkPermission('P1', ob3)
+ False
+ >>> interaction.checkPermission('P2', ob3)
+ False
+ >>> interaction.checkPermission('P3', ob3)
+ True
+ >>> interaction.checkPermission('P1G', ob3)
+ False
+ >>> interaction.checkPermission('P2G', ob3)
+ False
+ >>> interaction.checkPermission('P3G', ob3)
+ True
+ >>> interaction.checkPermission('P4G', ob3)
+ True
+
+and if an object doesn't have a parent:
+
+ >>> ob4 = C()
+
+it will have whatever grants were made globally:
+
+ >>> interaction.checkPermission('P1', ob4)
+ False
+ >>> interaction.checkPermission('P2', ob4)
+ False
+ >>> interaction.checkPermission('P3', ob4)
+ False
+ >>> interaction.checkPermission('P1G', ob4)
+ False
+ >>> interaction.checkPermission('P2G', ob4)
+ True
+ >>> interaction.checkPermission('P3G', ob4)
+ False
+ >>> interaction.checkPermission('P4G', ob4)
+ False
+
+ >>> prinroleG.assignRoleToPrincipal('R1G', "bob", False)
+ >>> interaction.checkPermission('P3G', ob4)
+ True
+
+We'll get the same result if we have a non-annotatable parent without a
+parent:
+
+ >>> ob3.__parent__ = C()
+
+ >>> interaction.checkPermission('P1', ob3)
+ False
+ >>> interaction.checkPermission('P2', ob3)
+ False
+ >>> interaction.checkPermission('P3', ob3)
+ False
+ >>> interaction.checkPermission('P1G', ob3)
+ False
+ >>> interaction.checkPermission('P2G', ob3)
+ True
+ >>> interaction.checkPermission('P3G', ob3)
+ True
+ >>> interaction.checkPermission('P4G', ob3)
+ False
+
+The Anonymous role
+------------------
+
+The security policy defines a special role named "zope.Anonymous". All
+principals have this role and the role cannot be taken away.
+
+ >>> roleperG.grantPermissionToRole('P5', 'zope.Anonymous', False)
+ >>> interaction.checkPermission('P5', ob2)
+ True
+
+Proxies
+-------
+
+Objects may be proxied:
+
+ >>> from zope.security.checker import ProxyFactory
+ >>> ob = ProxyFactory(ob)
+ >>> interaction.checkPermission('P1', ob)
+ False
+ >>> interaction.checkPermission('P2', ob)
+ False
+ >>> interaction.checkPermission('P3', ob)
+ True
+ >>> interaction.checkPermission('P1G', ob)
+ False
+ >>> interaction.checkPermission('P2G', ob)
+ False
+ >>> interaction.checkPermission('P3G', ob)
+ True
+ >>> interaction.checkPermission('P4G', ob)
+ True
+
+as may their parents:
+
+ >>> ob3 = C()
+ >>> ob3.__parent__ = ob
+
+ >>> interaction.checkPermission('P1', ob3)
+ False
+ >>> interaction.checkPermission('P2', ob3)
+ False
+ >>> interaction.checkPermission('P3', ob3)
+ True
+ >>> interaction.checkPermission('P1G', ob3)
+ False
+ >>> interaction.checkPermission('P2G', ob3)
+ False
+ >>> interaction.checkPermission('P3G', ob3)
+ True
+ >>> interaction.checkPermission('P4G', ob3)
+ True
+
+Groups
+------
+
+Principals may have groups. Groups are also principals (and, thus,
+may have groups).
+
+If a principal has groups, the groups are available as group ids in
+the principal's `groups` attribute. The interaction has to convert
+these group ids to group objects, so that it can tell whether the
+groups have groups. It does this by calling the `getPrincipal` method
+on the principal authentication service, which is responsible for,
+among other things, converting a principal id to a principal.
+For our examples here, we'll create and register a stub principal
+authentication service:
+
+ >>> from zope.app.security.interfaces import IAuthentication
+ >>> class FauxPrincipals(dict):
+ ... zope.interface.implements(IAuthentication)
+ ... def getPrincipal(self, id):
+ ... return self[id]
+
+ >>> auth = FauxPrincipals()
+
+ >>> from zope.app.testing import ztapi
+ >>> ztapi.provideUtility(IAuthentication, auth)
+ >>> from zope.app import zapi
+
+Let's define a group:
+
+ >>> auth['g1'] = Principal('g1')
+
+Let's put the principal in our group. We do that by adding the group id
+to the new principals groups:
+
+ >>> principal.groups.append('g1')
+
+Of course, the principal doesn't have permissions not granted:
+
+ >>> interaction.checkPermission('gP1', ob)
+ False
+
+Now, if we grant a permission to the group:
+
+ >>> prinper.grantPermissionToPrincipal('gP1', 'g1')
+
+We see that our principal has the permission:
+
+ >>> interaction.checkPermission('gP1', ob)
+ True
+
+This works even if the group grant is global:
+
+ >>> interaction.checkPermission('gP1G', ob)
+ False
+
+ >>> prinperG.grantPermissionToPrincipal('gP1G', 'g1', True)
+
+ >>> interaction.checkPermission('gP1G', ob)
+ True
+
+Grants are, of course, acquired:
+
+ >>> interaction.checkPermission('gP1', ob2)
+ True
+
+ >>> interaction.checkPermission('gP1G', ob2)
+ True
+
+Inner grants can override outer grants:
+
+ >>> prinper2.denyPermissionToPrincipal('gP1', 'g1')
+ >>> interaction.checkPermission('gP1', ob2)
+ False
+
+But principal grants always trump group grants:
+
+ >>> prinper2.grantPermissionToPrincipal('gP1', 'bob')
+ >>> interaction.checkPermission('gP1', ob2)
+ True
+
+Groups can have groups too:
+
+ >>> auth['g2'] = Principal('g2')
+ >>> auth['g1'].groups.append('g2')
+
+If we grant to the new group:
+
+ >>> prinper.grantPermissionToPrincipal('gP2', 'g2')
+
+Then we, of course have that permission too:
+
+ >>> interaction.checkPermission('gP2', ob2)
+ True
+
+Just as principal grants override group grants, group grants can
+override other group grants:
+
+ >>> prinper.denyPermissionToPrincipal('gP2', 'g1')
+ >>> interaction.checkPermission('gP2', ob2)
+ False
+
+Principals can be in more than one group. Let's define a new group:
+
+ >>> auth['g3'] = Principal('g3')
+ >>> principal.groups.append('g3')
+ >>> prinper.grantPermissionToPrincipal('gP2', 'g3')
+
+Now, the principal has two groups. In one group, the permission 'gP2'
+is denied, but in the other, it is allowed. In a case like this, the
+permission is allowed:
+
+ >>> interaction.checkPermission('gP2', ob2)
+ True
+
+In a case where a principal has two or more groups, the group denies
+prevent allows from their parents. They don't prevent the principal
+from getting an allow from another principal.
+
+Grants can be inherited from ancestor groups through multiple paths.
+Let's grant a permission to g2 and deny it to g1:
+
+ >>> prinper.grantPermissionToPrincipal('gP3', 'g2')
+ >>> prinper.denyPermissionToPrincipal('gP3', 'g1')
+
+Now, as before, the deny in g1 blocks the grant in g2:
+
+ >>> interaction.checkPermission('gP3', ob2)
+ False
+
+Let's make g2 a group of g3:
+
+ >>> auth['g3'].groups.append('g2')
+
+Now, we get g2's grant through g3, and access is allowed:
+
+ >>> interaction.invalidate_cache()
+ >>> interaction.checkPermission('gP3', ob2)
+ True
+
+We can assign roles to groups:
+
+ >>> prinrole.assignRoleToPrincipal('gR1', 'g2')
+
+and get permissions through the roles:
+
+ >>> roleper.grantPermissionToRole('gP4', 'gR1')
+ >>> interaction.checkPermission('gP4', ob2)
+ True
+
+we can override role assignments to groups through subgroups:
+
+ >>> prinrole.removeRoleFromPrincipal('gR1', 'g1')
+ >>> prinrole.removeRoleFromPrincipal('gR1', 'g3')
+ >>> interaction.checkPermission('gP4', ob2)
+ False
+
+and through principals:
+
+ >>> prinrole.assignRoleToPrincipal('gR1', 'bob')
+ >>> interaction.checkPermission('gP4', ob2)
+ True
+
+Cleanup
+-------
+
+We clean up the changes we made in these examples:
+
+ >>> zope.security.management.endInteraction()
+ >>> ignore = zope.security.management.setSecurityPolicy(oldpolicy)
Property changes on: z3c.objectpolicy/trunk/src/z3c/objectpolicy/zopepolicy_copy.txt
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.objectpolicy/trunk/src/z3c/objectpolicy/zopepolicy_objectpolicy.txt
===================================================================
--- z3c.objectpolicy/trunk/src/z3c/objectpolicy/zopepolicy_objectpolicy.txt 2006-10-28 10:07:05 UTC (rev 70938)
+++ z3c.objectpolicy/trunk/src/z3c/objectpolicy/zopepolicy_objectpolicy.txt 2006-10-28 10:11:06 UTC (rev 70939)
@@ -0,0 +1,659 @@
+THIS IS A COPY of zope.app.securitypolicy.zopepolicy.txt
+Because all test there have to pass with the objectpolicy installed.
+
+This one uses ObjectPolicy as the security policy.
+
+Classic Zope Security Policy
+============================
+
+This package implements a role-based security policy similar to the
+policy found in Zope 2. The security policy is responsible for
+deciding whether an interaction has a permission on an object. This
+security policy does this using grant and denial information. Managers
+can grant or deny:
+
+ - roles to principals,
+
+ - permissions to principals, and
+
+ - permissions to roles
+
+Grants and denials are stored as annotations on objects. To store
+grants and denials, objects must be annotatable:
+
+ >>> import zope.interface
+ >>> from zope.annotation.interfaces import IAttributeAnnotatable
+ >>> class Ob:
+ ... zope.interface.implements(IAttributeAnnotatable)
+
+ >>> ob = Ob()
+
+We use objects to represent principals. These objects implement an
+interface named `IPrincipal`, but the security policy only uses the `id`
+and `groups` attributes:
+
+ >>> class Principal:
+ ... def __init__(self, id):
+ ... self.id = id
+ ... self.groups = []
+
+ >>> principal = Principal('bob')
+
+Roles and permissions are also represented by objects, however, for
+the purposes of the security policy, only string `ids` are used.
+
+The security policy provides a factory for creating interactions:
+
+ >>> import zope.app.securitypolicy.zopepolicy
+ >>> import z3c.objectpolicy.objectpolicy
+ >>> interaction = z3c.objectpolicy.objectpolicy.ObjectPolicy()
+
+An interaction represents a specific interaction between some
+principals (normally users) and the system. Normally, we are only
+concerned with the interaction of one principal with the system, although
+we can have interactions of multiple principals. Multiple-principal
+interactions normally occur when untrusted users store code on a
+system for later execution. When untrusted code is executing, the
+authors of the code participate in the interaction. An
+interaction has a permission on an object only if all of the
+principals participating in the interaction have access to the object.
+
+The `checkPermission` method on interactions is used to test whether
+an interaction has a permission for an object. An interaction without
+participants always has every permission:
+
+ >>> interaction.checkPermission('P1', ob)
+ True
+
+In this example, 'P1' is a permission id.
+
+Normally, interactions have participants:
+
+ >>> class Participation:
+ ... interaction = None
+ >>> participation = Participation()
+ >>> participation.principal = principal
+ >>> interaction.add(participation)
+
+If we have participants, then we don't have a permission unless there
+are grants:
+
+ >>> interaction.checkPermission('P1', ob)
+ False
+
+Note, however, that we always have the CheckerPublic permission:
+
+ >>> from zope.security.checker import CheckerPublic
+ >>> interaction.checkPermission(CheckerPublic, ob)
+ True
+
+We make grants and denials on objects by adapting them to various
+granting interfaces. The objects returned from the adaptation are
+object-specific manager objects:
+
+ >>> from zope.app.securitypolicy import interfaces
+ >>> roleper = interfaces.IRolePermissionManager(ob)
+ >>> prinrole = interfaces.IPrincipalRoleManager(ob)
+ >>> prinper = interfaces.IPrincipalPermissionManager(ob)
+
+The computations involved in checking permissions can be
+significant. To reduce the computational cost, caching is used
+extensively. We could invalidate the cache as we make grants, but the
+adapters for making grants will automatically invalidate the cache of
+the current interaction. They use the security-management apis to do
+this. To take advantage of the cache invalidation, we'll need to let
+the security-management system manage our interactions. First, we'll
+set our security policy as the default:
+
+ >>> import zope.security.management
+ >>> oldpolicy = zope.security.management.setSecurityPolicy(
+ ... z3c.objectpolicy.objectpolicy.ObjectPolicy)
+
+and then we'll create a new interaction:
+
+ >>> participation = Participation()
+ >>> participation.principal = principal
+ >>> zope.security.management.newInteraction(participation)
+ >>> interaction = zope.security.management.getInteraction()
+
+We normally provide access by granting permissions to roles for an object:
+
+ >>> roleper.grantPermissionToRole('P1', 'R1')
+
+and then granting roles to principals for an object (local roles):
+
+ >>> prinrole.assignRoleToPrincipal('R1', 'bob')
+
+The combination of these grants, which we call a role-based grant,
+provides the permission:
+
+ >>> interaction.checkPermission('P1', ob)
+ True
+
+We can also provide a permission directly:
+
+ >>> prinper.grantPermissionToPrincipal('P2', 'bob')
+ >>> interaction.checkPermission('P2', ob)
+ True
+
+Permission grants or denials override role-based grant or denials. So
+if we deny P1:
+
+ >>> prinper.denyPermissionToPrincipal('P1', 'bob')
+
+we cause the interaction to lack the permission, despite the role
+grants:
+
+ >>> interaction.checkPermission('P1', ob)
+ False
+
+Similarly, even if we have a role-based denial of P2:
+
+ >>> roleper.denyPermissionToRole('P2', 'R1')
+
+we still have access, because of the permission-based grant:
+
+ >>> interaction.checkPermission('P2', ob)
+ True
+
+A role-based denial doesn't actually deny a permission; rather it
+prevents the granting of a permission. So, if we have both grants and
+denials based on roles, we have access:
+
+ >>> roleper.grantPermissionToRole('P3', 'R1')
+ >>> roleper.grantPermissionToRole('P3', 'R2')
+ >>> roleper.denyPermissionToRole('P3', 'R3')
+ >>> prinrole.removeRoleFromPrincipal('R2', 'bob')
+ >>> prinrole.assignRoleToPrincipal('R3', 'bob')
+
+ >>> interaction.checkPermission('P3', ob)
+ True
+
+Global grants
+-------------
+
+Grants made to an object are said to be "local". We can also make
+global grants:
+
+ >>> from zope.app.securitypolicy.rolepermission \
+ ... import rolePermissionManager as roleperG
+ >>> from zope.app.securitypolicy.principalpermission \
+ ... import principalPermissionManager as prinperG
+ >>> from zope.app.securitypolicy.principalrole \
+ ... import principalRoleManager as prinroleG
+
+And the same rules apply to global grants and denials.
+
+ >>> roleperG.grantPermissionToRole('P1G', 'R1G', False)
+
+In these tests, we aren't bothering to define any roles, permissions,
+or principals, so we pass an extra argument that tells the granting
+routines not to check the validity of the values.
+
+ >>> prinroleG.assignRoleToPrincipal('R1G', 'bob', False)
+ >>> interaction.checkPermission('P1G', ob)
+ True
+
+ >>> prinperG.grantPermissionToPrincipal('P2G', 'bob', False)
+ >>> interaction.checkPermission('P2G', ob)
+ True
+
+ >>> prinperG.denyPermissionToPrincipal('P1G', 'bob', False)
+ >>> interaction.checkPermission('P1G', ob)
+ False
+
+ >>> roleperG.denyPermissionToRole('P2G', 'R1G', False)
+ >>> interaction.checkPermission('P2G', ob)
+ True
+
+ >>> roleperG.grantPermissionToRole('P3G', 'R1G', False)
+ >>> roleperG.grantPermissionToRole('P3G', 'R2G', False)
+ >>> roleperG.denyPermissionToRole('P3G', 'R3G', False)
+ >>> prinroleG.removeRoleFromPrincipal('R2G', 'bob', False)
+ >>> prinroleG.assignRoleToPrincipal('R3G', 'bob', False)
+ >>> interaction.checkPermission('P3G', ob)
+ True
+
+Local versus global grants
+--------------------------
+
+We, of course, acquire global grants by default:
+
+ >>> interaction.checkPermission('P1G', ob)
+ False
+ >>> interaction.checkPermission('P2G', ob)
+ True
+ >>> interaction.checkPermission('P3G', ob)
+ True
+
+Local role-based grants do not override global principal-specific denials:
+
+ >>> roleper.grantPermissionToRole('P1G', 'R1G')
+ >>> prinrole.assignRoleToPrincipal('R1G', 'bob')
+ >>> interaction.checkPermission('P1G', ob)
+ False
+
+And local role-based denials don't override global
+principal-grants:
+
+ >>> roleper.denyPermissionToRole('P2G', 'R1G')
+ >>> interaction.checkPermission('P2G', ob)
+ True
+
+A local role-based deny can cancel a global role-based grant:
+
+ >>> roleper.denyPermissionToRole('P3G', 'R1G')
+ >>> interaction.checkPermission('P3G', ob)
+ False
+
+and a local role-based grant can override a global role-based denial:
+
+ >>> roleperG.denyPermissionToRole('P4G', 'R1G', False)
+ >>> prinroleG.assignRoleToPrincipal('R1G', "bob", False)
+ >>> interaction.checkPermission('P4G', ob)
+ False
+ >>> roleper.grantPermissionToRole('P4G', 'R1G')
+ >>> interaction.checkPermission('P4G', ob)
+ True
+ >>> prinroleG.removeRoleFromPrincipal('R1G', "bob", False)
+ >>> interaction.checkPermission('P4G', ob)
+ True
+
+Of course, a local permission-based grant or denial overrides any
+global setting and overrides local role-based grants or denials:
+
+ >>> prinper.grantPermissionToPrincipal('P3G', 'bob')
+ >>> interaction.checkPermission('P3G', ob)
+ True
+
+ >>> prinper.denyPermissionToPrincipal('P2G', 'bob')
+ >>> interaction.checkPermission('P2G', ob)
+ False
+
+Sublocations
+------------
+
+We can have sub-locations. A sublocation of a location is an object
+whose `__parent__` attribute is the location:
+
+ >>> ob2 = Ob()
+ >>> ob2.__parent__ = ob
+
+By default, sublocations acquire grants from higher locations:
+
+ >>> interaction.checkPermission('P1', ob2)
+ False
+ >>> interaction.checkPermission('P2', ob2)
+ True
+ >>> interaction.checkPermission('P3', ob2)
+ True
+ >>> interaction.checkPermission('P1G', ob2)
+ False
+ >>> interaction.checkPermission('P2G', ob2)
+ False
+ >>> interaction.checkPermission('P3G', ob2)
+ True
+ >>> interaction.checkPermission('P4G', ob2)
+ True
+
+Sublocation role-based grants do not override their parent
+principal-specific denials:
+
+ >>> roleper2 = interfaces.IRolePermissionManager(ob2)
+ >>> prinrole2 = interfaces.IPrincipalRoleManager(ob2)
+ >>> prinper2 = interfaces.IPrincipalPermissionManager(ob2)
+
+ >>> roleper2.grantPermissionToRole('P1', 'R1')
+ >>> prinrole2.assignRoleToPrincipal('R1', 'bob')
+ >>> interaction.checkPermission('P1', ob2)
+ False
+
+And local role-based denials don't override their parents
+principal-grant:
+
+ >>> roleper2.denyPermissionToRole('P2', 'R1')
+ >>> interaction.checkPermission('P2', ob2)
+ True
+
+A local role-based deny can cancel a parent role-based grant:
+
+ >>> roleper2.denyPermissionToRole('P3', 'R1')
+ >>> interaction.checkPermission('P3', ob2)
+ False
+
+and a local role-based grant can override a parent role-based denial:
+
+ >>> roleper.denyPermissionToRole('P4', 'R1')
+ >>> prinrole.assignRoleToPrincipal('R1', 'bob')
+ >>> interaction.checkPermission('P4', ob2)
+ False
+ >>> roleper2.grantPermissionToRole('P4', 'R1')
+ >>> interaction.checkPermission('P4', ob2)
+ True
+ >>> prinrole.removeRoleFromPrincipal('R1', 'bob')
+ >>> interaction.checkPermission('P4', ob2)
+ True
+
+
+Of course, a local permission-based grant or denial overrides any
+global setting and overrides local role-based grants or denials:
+
+ >>> prinper.grantPermissionToPrincipal('P3', 'bob')
+ >>> interaction.checkPermission('P3', ob2)
+ True
+
+ >>> prinper.denyPermissionToPrincipal('P2', 'bob')
+ >>> interaction.checkPermission('P2', ob2)
+ False
+
+If an object is not annotatable, but does have a parent, it will get
+its grants from its parent:
+
+ >>> class C:
+ ... pass
+
+ >>> ob3 = C()
+ >>> ob3.__parent__ = ob
+
+ >>> interaction.checkPermission('P1', ob3)
+ False
+ >>> interaction.checkPermission('P2', ob3)
+ False
+ >>> interaction.checkPermission('P3', ob3)
+ True
+ >>> interaction.checkPermission('P1G', ob3)
+ False
+ >>> interaction.checkPermission('P2G', ob3)
+ False
+ >>> interaction.checkPermission('P3G', ob3)
+ True
+ >>> interaction.checkPermission('P4G', ob3)
+ True
+
+The same results will be had if there are multiple non-annotatable
+objects:
+
+ >>> ob3.__parent__ = C()
+ >>> ob3.__parent__.__parent__ = ob
+
+ >>> interaction.checkPermission('P1', ob3)
+ False
+ >>> interaction.checkPermission('P2', ob3)
+ False
+ >>> interaction.checkPermission('P3', ob3)
+ True
+ >>> interaction.checkPermission('P1G', ob3)
+ False
+ >>> interaction.checkPermission('P2G', ob3)
+ False
+ >>> interaction.checkPermission('P3G', ob3)
+ True
+ >>> interaction.checkPermission('P4G', ob3)
+ True
+
+and if an object doesn't have a parent:
+
+ >>> ob4 = C()
+
+it will have whatever grants were made globally:
+
+ >>> interaction.checkPermission('P1', ob4)
+ False
+ >>> interaction.checkPermission('P2', ob4)
+ False
+ >>> interaction.checkPermission('P3', ob4)
+ False
+ >>> interaction.checkPermission('P1G', ob4)
+ False
+ >>> interaction.checkPermission('P2G', ob4)
+ True
+ >>> interaction.checkPermission('P3G', ob4)
+ False
+ >>> interaction.checkPermission('P4G', ob4)
+ False
+
+ >>> prinroleG.assignRoleToPrincipal('R1G', "bob", False)
+ >>> interaction.checkPermission('P3G', ob4)
+ True
+
+We'll get the same result if we have a non-annotatable parent without a
+parent:
+
+ >>> ob3.__parent__ = C()
+
+ >>> interaction.checkPermission('P1', ob3)
+ False
+ >>> interaction.checkPermission('P2', ob3)
+ False
+ >>> interaction.checkPermission('P3', ob3)
+ False
+ >>> interaction.checkPermission('P1G', ob3)
+ False
+ >>> interaction.checkPermission('P2G', ob3)
+ True
+ >>> interaction.checkPermission('P3G', ob3)
+ True
+ >>> interaction.checkPermission('P4G', ob3)
+ False
+
+The Anonymous role
+------------------
+
+The security policy defines a special role named "zope.Anonymous". All
+principals have this role and the role cannot be taken away.
+
+ >>> roleperG.grantPermissionToRole('P5', 'zope.Anonymous', False)
+ >>> interaction.checkPermission('P5', ob2)
+ True
+
+Proxies
+-------
+
+Objects may be proxied:
+
+ >>> from zope.security.checker import ProxyFactory
+ >>> ob = ProxyFactory(ob)
+ >>> interaction.checkPermission('P1', ob)
+ False
+ >>> interaction.checkPermission('P2', ob)
+ False
+ >>> interaction.checkPermission('P3', ob)
+ True
+ >>> interaction.checkPermission('P1G', ob)
+ False
+ >>> interaction.checkPermission('P2G', ob)
+ False
+ >>> interaction.checkPermission('P3G', ob)
+ True
+ >>> interaction.checkPermission('P4G', ob)
+ True
+
+as may their parents:
+
+ >>> ob3 = C()
+ >>> ob3.__parent__ = ob
+
+ >>> interaction.checkPermission('P1', ob3)
+ False
+ >>> interaction.checkPermission('P2', ob3)
+ False
+ >>> interaction.checkPermission('P3', ob3)
+ True
+ >>> interaction.checkPermission('P1G', ob3)
+ False
+ >>> interaction.checkPermission('P2G', ob3)
+ False
+ >>> interaction.checkPermission('P3G', ob3)
+ True
+ >>> interaction.checkPermission('P4G', ob3)
+ True
+
+Groups
+------
+
+Principals may have groups. Groups are also principals (and, thus,
+may have groups).
+
+If a principal has groups, the groups are available as group ids in
+the principal's `groups` attribute. The interaction has to convert
+these group ids to group objects, so that it can tell whether the
+groups have groups. It does this by calling the `getPrincipal` method
+on the principal authentication service, which is responsible for,
+among other things, converting a principal id to a principal.
+For our examples here, we'll create and register a stub principal
+authentication service:
+
+ >>> from zope.app.security.interfaces import IAuthentication
+ >>> class FauxPrincipals(dict):
+ ... zope.interface.implements(IAuthentication)
+ ... def getPrincipal(self, id):
+ ... return self[id]
+
+ >>> auth = FauxPrincipals()
+
+ >>> from zope.app.testing import ztapi
+ >>> ztapi.provideUtility(IAuthentication, auth)
+ >>> from zope.app import zapi
+
+Let's define a group:
+
+ >>> auth['g1'] = Principal('g1')
+
+Let's put the principal in our group. We do that by adding the group id
+to the new principals groups:
+
+ >>> principal.groups.append('g1')
+
+Of course, the principal doesn't have permissions not granted:
+
+ >>> interaction.checkPermission('gP1', ob)
+ False
+
+Now, if we grant a permission to the group:
+
+ >>> prinper.grantPermissionToPrincipal('gP1', 'g1')
+
+We see that our principal has the permission:
+
+ >>> interaction.checkPermission('gP1', ob)
+ True
+
+This works even if the group grant is global:
+
+ >>> interaction.checkPermission('gP1G', ob)
+ False
+
+ >>> prinperG.grantPermissionToPrincipal('gP1G', 'g1', True)
+
+ >>> interaction.checkPermission('gP1G', ob)
+ True
+
+Grants are, of course, acquired:
+
+ >>> interaction.checkPermission('gP1', ob2)
+ True
+
+ >>> interaction.checkPermission('gP1G', ob2)
+ True
+
+Inner grants can override outer grants:
+
+ >>> prinper2.denyPermissionToPrincipal('gP1', 'g1')
+ >>> interaction.checkPermission('gP1', ob2)
+ False
+
+But principal grants always trump group grants:
+
+ >>> prinper2.grantPermissionToPrincipal('gP1', 'bob')
+ >>> interaction.checkPermission('gP1', ob2)
+ True
+
+Groups can have groups too:
+
+ >>> auth['g2'] = Principal('g2')
+ >>> auth['g1'].groups.append('g2')
+
+If we grant to the new group:
+
+ >>> prinper.grantPermissionToPrincipal('gP2', 'g2')
+
+Then we, of course have that permission too:
+
+ >>> interaction.checkPermission('gP2', ob2)
+ True
+
+Just as principal grants override group grants, group grants can
+override other group grants:
+
+ >>> prinper.denyPermissionToPrincipal('gP2', 'g1')
+ >>> interaction.checkPermission('gP2', ob2)
+ False
+
+Principals can be in more than one group. Let's define a new group:
+
+ >>> auth['g3'] = Principal('g3')
+ >>> principal.groups.append('g3')
+ >>> prinper.grantPermissionToPrincipal('gP2', 'g3')
+
+Now, the principal has two groups. In one group, the permission 'gP2'
+is denied, but in the other, it is allowed. In a case like this, the
+permission is allowed:
+
+ >>> interaction.checkPermission('gP2', ob2)
+ True
+
+In a case where a principal has two or more groups, the group denies
+prevent allows from their parents. They don't prevent the principal
+from getting an allow from another principal.
+
+Grants can be inherited from ancestor groups through multiple paths.
+Let's grant a permission to g2 and deny it to g1:
+
+ >>> prinper.grantPermissionToPrincipal('gP3', 'g2')
+ >>> prinper.denyPermissionToPrincipal('gP3', 'g1')
+
+Now, as before, the deny in g1 blocks the grant in g2:
+
+ >>> interaction.checkPermission('gP3', ob2)
+ False
+
+Let's make g2 a group of g3:
+
+ >>> auth['g3'].groups.append('g2')
+
+Now, we get g2's grant through g3, and access is allowed:
+
+ >>> interaction.invalidate_cache()
+ >>> interaction.checkPermission('gP3', ob2)
+ True
+
+We can assign roles to groups:
+
+ >>> prinrole.assignRoleToPrincipal('gR1', 'g2')
+
+and get permissions through the roles:
+
+ >>> roleper.grantPermissionToRole('gP4', 'gR1')
+ >>> interaction.checkPermission('gP4', ob2)
+ True
+
+we can override role assignments to groups through subgroups:
+
+ >>> prinrole.removeRoleFromPrincipal('gR1', 'g1')
+ >>> prinrole.removeRoleFromPrincipal('gR1', 'g3')
+ >>> interaction.checkPermission('gP4', ob2)
+ False
+
+and through principals:
+
+ >>> prinrole.assignRoleToPrincipal('gR1', 'bob')
+ >>> interaction.checkPermission('gP4', ob2)
+ True
+
+Cleanup
+-------
+
+We clean up the changes we made in these examples:
+
+ >>> zope.security.management.endInteraction()
+ >>> ignore = zope.security.management.setSecurityPolicy(oldpolicy)
Property changes on: z3c.objectpolicy/trunk/src/z3c/objectpolicy/zopepolicy_objectpolicy.txt
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
More information about the Checkins
mailing list