[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