[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/securitypolicy/ -
Fixed a bug (actually a missfeature). It wasn't possible
Jim Fulton
jim at zope.com
Wed Jul 21 18:52:23 EDT 2004
Log message for revision 26668:
- Fixed a bug (actually a missfeature). It wasn't possible
for local settings to override global (zcml) settings.
- Changed the way role denies work. A role deny simply prevents
a principal from having a role. A principal may still
have access through other roles or through principal grants.
Role grants or denies never override principal grants or denies
*even* if the role-based grants or denies are more local.
- Implemented a caching scheme that provides huge performance
benefits when the authenticated principal is defined in a local auth
service, rather than a global one (zcml).
- Refactored the way security maps were implemented and used.
Especially changed the way annotations were handled.
Changed:
U Zope3/trunk/src/zope/app/securitypolicy/browser/configure.zcml
U Zope3/trunk/src/zope/app/securitypolicy/browser/rolepermissionview.py
U Zope3/trunk/src/zope/app/securitypolicy/configure.zcml
A Zope3/trunk/src/zope/app/securitypolicy/grantinfo.py
U Zope3/trunk/src/zope/app/securitypolicy/interfaces.py
D Zope3/trunk/src/zope/app/securitypolicy/permissionroles.py
U Zope3/trunk/src/zope/app/securitypolicy/principalpermission.py
U Zope3/trunk/src/zope/app/securitypolicy/principalrole.py
U Zope3/trunk/src/zope/app/securitypolicy/rolepermission.py
U Zope3/trunk/src/zope/app/securitypolicy/securitymap.py
U Zope3/trunk/src/zope/app/securitypolicy/tests/test_securitymap.py
U Zope3/trunk/src/zope/app/securitypolicy/tests/test_zopepolicy.py
U Zope3/trunk/src/zope/app/securitypolicy/zopepolicy.py
A Zope3/trunk/src/zope/app/securitypolicy/zopepolicy.txt
-=-
Modified: Zope3/trunk/src/zope/app/securitypolicy/browser/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/browser/configure.zcml 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/browser/configure.zcml 2004-07-21 22:52:23 UTC (rev 26668)
@@ -76,6 +76,17 @@
template="grant.pt"
menu="zmi_actions" title="Grant" />
+ <zope:class class=".rolepermissionview.PermissionRoles">
+ <zope:require permission="zope.Security"
+ attributes="roles rolesInfo id title description" />
+ </zope:class>
+
+ <zope:class class=".rolepermissionview.RolePermissions">
+ <zope:require
+ permission="zope.Security"
+ attributes="permissions permissionsInfo id title description" />
+ </zope:class>
+
<!-- Principal Roles -->
<page
Modified: Zope3/trunk/src/zope/app/securitypolicy/browser/rolepermissionview.py
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/browser/rolepermissionview.py 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/browser/rolepermissionview.py 2004-07-21 22:52:23 UTC (rev 26668)
@@ -17,14 +17,13 @@
"""
from datetime import datetime
+from zope.interface import implements
+
from zope.app import zapi
from zope.app.i18n import ZopeMessageIDFactory as _
from zope.app.security.settings import Unset, Allow, Deny
from zope.app.security.interfaces import IPermission
-
from zope.app.securitypolicy.interfaces import IRole, IRolePermissionManager
-from zope.app.securitypolicy.permissionroles import PermissionRoles
-from zope.app.securitypolicy.rolepermission import RolePermissions
class RolePermissionView(object):
@@ -141,3 +140,76 @@
return status
+
+class PermissionRoles(object):
+
+ implements(IPermission)
+
+ def __init__(self, permission, context, roles):
+ self._permission = permission
+ self._context = context
+ self._roles = roles
+
+ def _getId(self):
+ return self._permission.id
+
+ id = property(_getId)
+
+ def _getTitle(self):
+ return self._permission.title
+
+ title = property(_getTitle)
+
+ def _getDescription(self):
+ return self._permission.description
+
+ description = property(_getDescription)
+
+ def roleSettings(self):
+ """
+ Returns the list of setting names of each role for this permission.
+ """
+ prm = IRolePermissionManager(self._context)
+ proles = prm.getRolesForPermission(self._permission.id)
+ settings = {}
+ for role, setting in proles:
+ settings[role] = setting.getName()
+ nosetting = Unset.getName()
+ return [settings.get(role.id, nosetting) for role in self._roles]
+
+class RolePermissions(object):
+
+ implements(IRole)
+
+ def __init__(self, role, context, permissions):
+ self._role = role
+ self._context = context
+ self._permissions = permissions
+
+ def _getId(self):
+ return self._role.id
+
+ id = property(_getId)
+
+ def _getTitle(self):
+ return self._role.title
+
+ title = property(_getTitle)
+
+ def _getDescription(self):
+ return self._role.description
+
+ description = property(_getDescription)
+
+ def permissionsInfo(self):
+ prm = IRolePermissionManager(self._context)
+ rperms = prm.getPermissionsForRole(self._role.id)
+ settings = {}
+ for permission, setting in rperms:
+ settings[permission] = setting.getName()
+ nosetting = Unset.getName()
+ return [{'id': permission.id,
+ 'title': permission.title,
+ 'setting': settings.get(permission.id, nosetting)}
+ for permission in self._permissions]
+
Modified: Zope3/trunk/src/zope/app/securitypolicy/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/configure.zcml 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/configure.zcml 2004-07-21 22:52:23 UTC (rev 26668)
@@ -3,58 +3,56 @@
i18n_domain="zope"
>
- <class class=".permissionroles.PermissionRoles">
- <require permission="zope.Security"
- attributes="roles rolesInfo id title description" />
- </class>
-
- <class class=".rolepermission.RolePermissions">
- <require permission="zope.Security"
- attributes="permissions permissionsInfo id title description" />
- </class>
-
<adapter factory=".rolepermission.AnnotationRolePermissionManager"
provides=".interfaces.IRolePermissionManager"
for="zope.app.annotation.interfaces.IAnnotatable"
- trusted="true" />
+ trusted="true"
+ />
<class class=".rolepermission.AnnotationRolePermissionManager">
<require
permission="zope.Security"
attributes="grantPermissionToRole denyPermissionToRole
- unsetPermissionFromRole" />
- <allow
- interface=".interfaces.IRolePermissionMap" />
+ unsetPermissionFromRole"
+ />
+ <allow interface=".interfaces.IRolePermissionMap" />
</class>
<adapter factory=".principalrole.AnnotationPrincipalRoleManager"
provides=".interfaces.IPrincipalRoleManager"
for="zope.app.annotation.interfaces.IAnnotatable"
- trusted="true" />
+ trusted="true"
+ />
<class class=".principalrole.AnnotationPrincipalRoleManager">
<require
permission="zope.Security"
attributes="assignRoleToPrincipal removeRoleFromPrincipal
- unsetRoleForPrincipal" />
- <allow
- interface=".interfaces.IPrincipalRoleMap" />
+ unsetRoleForPrincipal"
+ />
+ <allow interface=".interfaces.IPrincipalRoleMap" />
</class>
<adapter factory=".principalpermission.AnnotationPrincipalPermissionManager"
provides=".interfaces.IPrincipalPermissionManager"
for="zope.app.annotation.interfaces.IAnnotatable"
- trusted="true"/>
+ trusted="true"
+ />
<class class=".principalpermission.AnnotationPrincipalPermissionManager">
<require
permission="zope.Security"
attributes="grantPermissionToRole denyPermissionToRole
- unsetPermissionFromRole" />
- <allow
- interface=".interfaces.IPrincipalPermissionMap" />
+ unsetPermissionFromRole"
+ />
+ <allow interface=".interfaces.IPrincipalPermissionMap" />
</class>
+ <adapter factory=".grantinfo.AnnotationGrantInfo"
+ provides=".interfaces.IGrantInfo"
+ for="zope.app.annotation.interfaces.IAnnotatable"
+ />
+
<!-- protect Roles and Permissions -->
<content class=".role.Role">
<allow interface=".interfaces.IRole" />
Added: Zope3/trunk/src/zope/app/securitypolicy/grantinfo.py
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/grantinfo.py 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/grantinfo.py 2004-07-21 22:52:23 UTC (rev 26668)
@@ -0,0 +1,78 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Grant info
+
+$Id$
+"""
+
+from zope.app.securitypolicy.interfaces import IGrantInfo
+
+from zope.app.annotation.interfaces import IAnnotations
+
+from zope.app.securitypolicy.principalpermission \
+ import AnnotationPrincipalPermissionManager
+prinperkey = AnnotationPrincipalPermissionManager.key
+del AnnotationPrincipalPermissionManager
+
+from zope.app.securitypolicy.principalrole \
+ import AnnotationPrincipalRoleManager
+prinrolekey = AnnotationPrincipalRoleManager.key
+del AnnotationPrincipalRoleManager
+
+from zope.app.securitypolicy.rolepermission \
+ import AnnotationRolePermissionManager
+rolepermkey = AnnotationRolePermissionManager.key
+del AnnotationRolePermissionManager
+
+
+class AnnotationGrantInfo(object):
+
+ prinper = prinrole = permrole = {}
+
+ def __init__(self, context):
+ self._context = context
+ annotations = IAnnotations(context, None)
+ if annotations is not None:
+
+ prinper = annotations.get(prinperkey)
+ if prinper is not None:
+ self.prinper = prinper._bycol # by principals
+
+ prinrole = annotations.get(prinrolekey)
+ if prinrole is not None:
+ self.prinrole = prinrole._bycol # by principals
+
+ roleper = annotations.get(rolepermkey)
+ if roleper is not None:
+ self.permrole = roleper._byrow # by permission
+
+ def __nonzero__(self):
+ return bool(self.prinper or self.prinrole or self.permrole)
+
+ def principalPermissionGrant(self, principal, permission):
+ prinper = self.prinper.get(principal)
+ if prinper:
+ return prinper.get(permission)
+
+ def getRolesForPermission(self, permission):
+ permrole = self.permrole.get(permission)
+ if permrole:
+ return permrole.items()
+ return ()
+
+ def getRolesForPrincipal(self, principal):
+ prinrole = self.prinrole.get(principal)
+ if prinrole:
+ return prinrole.items()
+ return ()
Property changes on: Zope3/trunk/src/zope/app/securitypolicy/grantinfo.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Modified: Zope3/trunk/src/zope/app/securitypolicy/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/interfaces.py 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/interfaces.py 2004-07-21 22:52:23 UTC (rev 26668)
@@ -18,36 +18,6 @@
from zope.interface import Interface
from zope.schema import TextLine, Text
-class ISecurityMap(Interface):
- """Security map to hold matrix-like relationships."""
-
- def addCell(rowentry, colentry, value):
- """Add a cell"""
-
- def delCell(rowentry, colentry):
- """Delete a cell"""
-
- def queryCell(rowentry, colentry, default=None):
- """Return the value of a cell by row, entry
-
- Return `default`, if the cell is not found.
- """
-
- def getCell(rowentry, colentry):
- """Return the value of a cell by row, entry
-
- Raise an error, if the cell is not found.
- """
-
- def getRow(rowentry):
- """Return a list of (colentry, value) tuples from a row"""
-
- def getCol(colentry):
- """Return a list of (rowentry, value) tuples from a col"""
-
- def getAllCells():
- """Return a list of (rowentry, colentry, value)"""
-
class IRole(Interface):
"""A role object."""
@@ -228,3 +198,27 @@
"""Remove the permission (either denied or allowed) from the
principal.
"""
+
+class IGrantInfo(Interface):
+ """Get grant info needed for checking access
+ """
+
+ def principalPermissionGrant(principal, permission):
+ """Return the principal-permission grant if any
+
+ The return value is one of Allow, Deny, or Unset
+ """
+
+ def getRolesForPermission(permission):
+ """Return the role grants for the permission
+
+ The role grants are an iterable of role, setting tuples, where
+ setting is either Allow or Deny.
+ """
+
+ def getRolesForPrincipal(principal):
+ """Return the role grants for the principal
+
+ The role grants are an iterable of role, setting tuples, where
+ setting is either Allow or Deny.
+ """
Deleted: Zope3/trunk/src/zope/app/securitypolicy/permissionroles.py
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/permissionroles.py 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/permissionroles.py 2004-07-21 22:52:23 UTC (rev 26668)
@@ -1,58 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Permission to Roles Map implementation
-
-$Id$
-"""
-from zope.interface import implements
-
-from zope.app.security.interfaces import IPermission
-from zope.app.security.settings import Unset
-from zope.app.securitypolicy.interfaces import IRolePermissionManager
-
-class PermissionRoles(object):
-
- implements(IPermission)
-
- def __init__(self, permission, context, roles):
- self._permission = permission
- self._context = context
- self._roles = roles
-
- def _getId(self):
- return self._permission.id
-
- id = property(_getId)
-
- def _getTitle(self):
- return self._permission.title
-
- title = property(_getTitle)
-
- def _getDescription(self):
- return self._permission.description
-
- description = property(_getDescription)
-
- def roleSettings(self):
- """
- Returns the list of setting names of each role for this permission.
- """
- prm = IRolePermissionManager(self._context)
- proles = prm.getRolesForPermission(self._permission.id)
- settings = {}
- for role, setting in proles:
- settings[role] = setting.getName()
- nosetting = Unset.getName()
- return [settings.get(role.id, nosetting) for role in self._roles]
Modified: Zope3/trunk/src/zope/app/securitypolicy/principalpermission.py
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/principalpermission.py 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/principalpermission.py 2004-07-21 22:52:23 UTC (rev 26668)
@@ -17,7 +17,6 @@
"""
from zope.interface import implements
-from zope.app.annotation.interfaces import IAnnotations
from zope.app.securitypolicy.interfaces import IPrincipalPermissionManager
from zope.app.security.settings import Allow, Deny, Unset
@@ -25,87 +24,37 @@
from zope.app.security.permission import checkPermission, allPermissions
from zope.app.securitypolicy.securitymap import SecurityMap
+from zope.app.securitypolicy.securitymap import AnnotationSecurityMap
-# This is misspelled, but that's OK. It just has to be unique.
-# we'll keep it as is, to prevent breaking on data:
-annotation_key = 'zopel.app.security.AnnotationPrincipalPermissionManager'
-class AnnotationPrincipalPermissionManager(object):
+class AnnotationPrincipalPermissionManager(AnnotationSecurityMap):
"""Mappings between principals and permissions."""
+ # the annotation key is a holdover from this module's old
+ # location, but cannot change without breaking existing databases
+ # It is also is misspelled, but that's OK. It just has to be unique.
+ # we'll keep it as is, to prevent breaking old data:
+ key = 'zopel.app.security.AnnotationPrincipalPermissionManager'
+
implements(IPrincipalPermissionManager)
- def __init__(self, context):
- self._context = context
-
def grantPermissionToPrincipal(self, permission_id, principal_id):
- ''' See the interface IPrincipalPermissionManager '''
- pp = self._getPrincipalPermissions(create=1)
- pp.addCell(permission_id, principal_id, Allow)
- self._context._p_changed = 1
+ AnnotationSecurityMap.addCell(self, permission_id, principal_id, Allow)
def denyPermissionToPrincipal(self, permission_id, principal_id):
- ''' See the interface IPrincipalPermissionManager '''
- pp = self._getPrincipalPermissions(create=1)
- pp.addCell(permission_id, principal_id, Deny)
- self._context._p_changed = 1
+ AnnotationSecurityMap.addCell(self, permission_id, principal_id, Deny)
- def unsetPermissionForPrincipal(self, permission_id, principal_id):
- ''' See the interface IPrincipalPermissionManager '''
- pp = self._getPrincipalPermissions()
- # Only unset if there is a security map, otherwise, we're done
- if pp:
- pp.delCell(permission_id, principal_id)
- self._context._p_changed = 1
+ unsetPermissionForPrincipal = AnnotationSecurityMap.delCell
+ getPrincipalsForPermission = AnnotationSecurityMap.getRow
+ getPermissionsForPrincipal = AnnotationSecurityMap.getCol
- def getPrincipalsForPermission(self, permission_id):
- ''' See the interface IPrincipalPermissionManager '''
- pp = self._getPrincipalPermissions()
- if pp:
- return pp.getRow(permission_id)
- return []
+ def getSetting(self, permission_id, principal_id, default=Unset):
+ return AnnotationSecurityMap.queryCell(
+ self, permission_id, principal_id, default)
+
+ getPrincipalsAndPermissions = AnnotationSecurityMap.getAllCells
- def getPermissionsForPrincipal(self, principal_id):
- ''' See the interface IPrincipalPermissionManager '''
- pp = self._getPrincipalPermissions()
- if pp:
- return pp.getCol(principal_id)
- return []
- def getSetting(self, permission_id, principal_id):
- ''' See the interface IPrincipalPermissionManager '''
- pp = self._getPrincipalPermissions()
- if pp:
- return pp.queryCell(permission_id, principal_id, default=Unset)
- return []
-
- def getPrincipalsAndPermissions(self):
- ''' See the interface IPrincipalPermissionManager '''
- pp = self._getPrincipalPermissions()
- if pp:
- return pp.getAllCells()
- return []
-
- # Implementation helpers
-
- def _getPrincipalPermissions(self, create=0):
- """ Get the principal permission map stored in the context, optionally
- creating one if necessary """
- # need to remove security proxies here, otherwise we enter
- # an infinite loop, becuase checking security depends on
- # getting PrincipalPermissions.
- from zope.proxy import removeAllProxies
- context = removeAllProxies(self._context)
- annotations = IAnnotations(context)
- try:
- return annotations[annotation_key]
- except KeyError:
- if create:
- rp = annotations[annotation_key] = SecurityMap()
- return rp
- return None
-
-
class PrincipalPermissionManager(SecurityMap):
"""Mappings between principals and permissions."""
@@ -151,9 +100,9 @@
''' See the interface IPrincipalPermissionManager '''
return self.getCol(principal_id)
- def getSetting(self, permission_id, principal_id):
+ def getSetting(self, permission_id, principal_id, default=Unset):
''' See the interface IPrincipalPermissionManager '''
- return self.queryCell(permission_id, principal_id, default=Unset)
+ return self.queryCell(permission_id, principal_id, default)
def getPrincipalsAndPermissions(self):
''' See the interface IPrincipalPermissionManager '''
Modified: Zope3/trunk/src/zope/app/securitypolicy/principalrole.py
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/principalrole.py 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/principalrole.py 2004-07-21 22:52:23 UTC (rev 26668)
@@ -16,95 +16,46 @@
$Id$
"""
from zope.interface import implements
-from zope.security.proxy import trustedRemoveSecurityProxy
-from zope.app.annotation.interfaces import IAnnotations
from zope.app.securitypolicy.interfaces import IPrincipalRoleManager
-from zope.app.securitypolicy.interfaces import IPrincipalRoleMap
from zope.app.security.settings import Allow, Deny, Unset
from zope.app.securitypolicy.securitymap import SecurityMap
-from zope.app.securitypolicy.securitymap import PersistentSecurityMap
+from zope.app.securitypolicy.securitymap import AnnotationSecurityMap
from zope.app.security.principal import checkPrincipal
from zope.app.securitypolicy.role import checkRole
-annotation_key = 'zope.app.security.AnnotationPrincipalRoleManager'
-
-class AnnotationPrincipalRoleManager(object):
+class AnnotationPrincipalRoleManager(AnnotationSecurityMap):
"""Mappings between principals and roles."""
+ # the annotation key is a holdover from this module's old
+ # location, but cannot change without breaking existing databases
+ key = 'zope.app.security.AnnotationPrincipalRoleManager'
+
implements(IPrincipalRoleManager)
- def __init__(self, context):
- self._context = context
-
def assignRoleToPrincipal(self, role_id, principal_id):
- ''' See the interface IPrincipalRoleManager '''
- pp = self._getPrincipalRoles(create=1)
- pp.addCell(role_id, principal_id, Allow)
+ AnnotationSecurityMap.addCell(self, role_id, principal_id, Allow)
def removeRoleFromPrincipal(self, role_id, principal_id):
- ''' See the interface IPrincipalRoleManager '''
- pp = self._getPrincipalRoles(create=1)
- pp.addCell(role_id, principal_id, Deny)
+ AnnotationSecurityMap.addCell(self, role_id, principal_id, Deny)
- def unsetRoleForPrincipal(self, role_id, principal_id):
- ''' See the interface IPrincipalRoleManager '''
- pp = self._getPrincipalRoles()
- # Only unset if there is a security map, otherwise, we're done
- if pp:
- pp.delCell(role_id, principal_id)
-
- def getPrincipalsForRole(self, role_id):
- ''' See the interface IPrincipalRoleManager '''
- pp = self._getPrincipalRoles()
- if pp:
- return pp.getRow(role_id)
- return []
-
- def getRolesForPrincipal(self, principal_id):
- ''' See the interface IPrincipalRoleManager '''
- pp = self._getPrincipalRoles()
- if pp:
- return pp.getCol(principal_id)
- return []
-
+ unsetRoleForPrincipal = AnnotationSecurityMap.delCell
+ getPrincipalsForRole = AnnotationSecurityMap.getRow
+ getRolesForPrincipal = AnnotationSecurityMap.getCol
+
def getSetting(self, role_id, principal_id):
- ''' See the interface IPrincipalRoleManager '''
- pp = self._getPrincipalRoles()
- if pp:
- return pp.queryCell(role_id, principal_id, default=Unset)
- return Unset
+ return AnnotationSecurityMap.queryCell(
+ self, role_id, principal_id, default=Unset)
- def getPrincipalsAndRoles(self):
- ''' See the interface IPrincipalRoleManager '''
- pp = self._getPrincipalRoles()
- if pp:
- return pp.getAllCells()
- return []
+ getPrincipalsAndRoles = AnnotationSecurityMap.getAllCells
- # Implementation helpers
- def _getPrincipalRoles(self, create=0):
- """ Get the principal role map stored in the context, optionally
- creating one if necessary """
- annotations = IAnnotations(self._context)
- try:
- # there's a chance that annotations is security proxied -
- # remove proxy to avoid authentication failure on role lookup
- return trustedRemoveSecurityProxy(annotations)[annotation_key]
- except KeyError:
- if create:
- rp = annotations[annotation_key] = PersistentSecurityMap()
- return rp
- return None
-
-
class PrincipalRoleManager(SecurityMap):
"""Mappings between principals and roles."""
- implements(IPrincipalRoleManager, IPrincipalRoleMap)
+ implements(IPrincipalRoleManager)
def assignRoleToPrincipal(self, role_id, principal_id, check=True):
''' See the interface IPrincipalRoleManager '''
Modified: Zope3/trunk/src/zope/app/securitypolicy/rolepermission.py
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/rolepermission.py 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/rolepermission.py 2004-07-21 22:52:23 UTC (rev 26668)
@@ -18,7 +18,6 @@
from zope.interface import implements
from zope.app import zapi
-from zope.app.annotation.interfaces import IAnnotations
from zope.app.security.settings import Allow, Deny, Unset
from zope.app.security.permission import checkPermission, allPermissions
@@ -27,135 +26,36 @@
from zope.app.securitypolicy.interfaces import IRolePermissionManager
from zope.app.securitypolicy.interfaces import IRole
from zope.app.securitypolicy.interfaces import IRolePermissionMap
-from zope.app.securitypolicy.securitymap import PersistentSecurityMap
+from zope.app.securitypolicy.securitymap import AnnotationSecurityMap
from zope.app.securitypolicy.securitymap import SecurityMap
-# the annotation_key is a holdover from this module's old location, but cannot
-# change without breaking existing databases
-annotation_key = 'zope.app.security.AnnotationRolePermissionManager'
-class AnnotationRolePermissionManager(object):
+class AnnotationRolePermissionManager(AnnotationSecurityMap):
"""Provide adapter that manages role permission data in an object attribute
"""
- implements(IRolePermissionManager, IRolePermissionMap)
+ # the annotation key is a holdover from this module's old
+ # location, but cannot change without breaking existing databases
+ key = 'zope.app.security.AnnotationRolePermissionManager'
- def __init__(self, context):
- self._context = context
+ implements(IRolePermissionManager)
def grantPermissionToRole(self, permission_id, role_id):
- ''' See the interface IRolePermissionManager '''
- rp = self._getRolePermissions(create=1)
- rp.addCell(permission_id, role_id, Allow)
- # probably not needed, as annotations should manage
- # their own persistence
- #self._context._p_changed = 1
+ AnnotationSecurityMap.addCell(self, permission_id, role_id, Allow)
def denyPermissionToRole(self, permission_id, role_id):
- ''' See the interface IRolePermissionManager '''
- rp = self._getRolePermissions(create=1)
- rp.addCell(permission_id, role_id, Deny)
- # probably not needed, as annotations should manage
- # their own persistence
- #self._context._p_changed = 1
+ AnnotationSecurityMap.addCell(self, permission_id, role_id, Deny)
- def unsetPermissionFromRole(self, permission_id, role_id):
- ''' See the interface IRolePermissionManager '''
- rp = self._getRolePermissions()
- # Only unset if there is a security map, otherwise, we're done
- if rp:
- rp.delCell(permission_id, role_id)
- # probably not needed, as annotations should manage
- # their own persistence
- #self._context._p_changed = 1
+ unsetPermissionFromRole = AnnotationSecurityMap.delCell
+ getRolesForPermission = AnnotationSecurityMap.getRow
+ getPermissionsForRole = AnnotationSecurityMap.getCol
+ getRolesAndPermissions = AnnotationSecurityMap.getAllCells
- def getRolesForPermission(self, permission_id):
- '''See interface IRolePermissionMap'''
- rp = self._getRolePermissions()
- if rp:
- return rp.getRow(permission_id)
- else:
- return []
-
- def getPermissionsForRole(self, role_id):
- '''See interface IRolePermissionMap'''
- rp = self._getRolePermissions()
- if rp:
- return rp.getCol(role_id)
- else:
- return []
-
- def getRolesAndPermissions(self):
- '''See interface IRolePermissionMap'''
- rp = self._getRolePermissions()
- if rp:
- return rp.getAllCells()
- else:
- return []
-
def getSetting(self, permission_id, role_id):
- '''See interface IRolePermissionMap'''
- rp = self._getRolePermissions()
- if rp:
- return rp.queryCell(permission_id, role_id)
- else:
- return Unset
+ return AnnotationSecurityMap.queryCell(
+ self, permission_id, role_id, default=Unset)
- def _getRolePermissions(self, create=0):
- """Get the role permission map stored in the context, optionally
- creating one if necessary"""
- # need to remove security proxies here, otherwise we enter
- # an infinite loop, becuase checking security depends on
- # getting RolePermissions.
- from zope.proxy import removeAllProxies
- context = removeAllProxies(self._context)
- annotations = IAnnotations(context)
- try:
- return annotations[annotation_key]
- except KeyError:
- if create:
- rp = annotations[annotation_key] = PersistentSecurityMap()
- return rp
- return None
-class RolePermissions(object):
-
- implements(IRole)
-
- def __init__(self, role, context, permissions):
- self._role = role
- self._context = context
- self._permissions = permissions
-
-
- def _getId(self):
- return self._role.id
-
- id = property(_getId)
-
- def _getTitle(self):
- return self._role.title
-
- title = property(_getTitle)
-
- def _getDescription(self):
- return self._role.description
-
- description = property(_getDescription)
-
- def permissionsInfo(self):
- prm = IRolePermissionManager(self._context)
- rperms = prm.getPermissionsForRole(self._role.id)
- settings = {}
- for permission, setting in rperms:
- settings[permission] = setting.getName()
- nosetting = Unset.getName()
- return [{'id': permission.id,
- 'title': permission.title,
- 'setting': settings.get(permission.id, nosetting)}
- for permission in self._permissions]
-
-
class RolePermissionManager(SecurityMap):
"""Mappings between roles and permissions."""
Modified: Zope3/trunk/src/zope/app/securitypolicy/securitymap.py
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/securitymap.py 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/securitymap.py 2004-07-21 22:52:23 UTC (rev 26668)
@@ -16,14 +16,11 @@
$Id$
"""
from persistent import Persistent
-from persistent.dict import PersistentDict
-from zope.interface import implements
-from zope.app.securitypolicy.interfaces import ISecurityMap
+from zope.app.annotation import IAnnotations
+from zope.security.management import queryInteraction
class SecurityMap(object):
- implements(ISecurityMap)
-
def __init__(self):
self._clear()
@@ -31,42 +28,66 @@
self._byrow = {}
self._bycol = {}
- def _empty_mapping(self):
- return {}
+ def __nonzero__(self):
+ return bool(self._byrow)
def addCell(self, rowentry, colentry, value):
- """See ISecurityMap"""
# setdefault may get expensive if an empty mapping is
# expensive to create, for PersistentDict for instance.
- row = self._byrow.setdefault(rowentry, self._empty_mapping())
+ row = self._byrow.get(rowentry)
+ if row:
+ if row.get(colentry) is value:
+ return False
+ else:
+ row = self._byrow[rowentry] = {}
+
+ col = self._bycol.get(colentry)
+ if not col:
+ col = self._bycol[colentry] = {}
+
row[colentry] = value
-
- col = self._bycol.setdefault(colentry, self._empty_mapping())
col[rowentry] = value
- try:
- del self._v_cells
- except AttributeError:
- pass
+ self._invalidated_interaction_cache()
+
+ return True
+
+ def _invalidated_interaction_cache(self):
+ # Invalidate this threads interaction cache
+ interaction = queryInteraction()
+ if interaction is not None:
+ try:
+ invalidate_cache = interaction.invalidate_cache
+ except AttributeError:
+ pass
+ else:
+ invalidate_cache()
+
def delCell(self, rowentry, colentry):
- """See ISecurityMap"""
row = self._byrow.get(rowentry)
if row and (colentry in row):
- del self._byrow[rowentry][colentry]
- del self._bycol[colentry][rowentry]
- try:
- del self._v_cells
- except AttributeError:
- pass
+ del row[colentry]
+ if not row:
+ del self._byrow[rowentry]
+ col = self._bycol[colentry]
+ del col[rowentry]
+ if not col:
+ del self._bycol[colentry]
+ self._invalidated_interaction_cache()
+
+ return True
+
+ return False
+
def queryCell(self, rowentry, colentry, default=None):
- """See ISecurityMap"""
row = self._byrow.get(rowentry)
- if row: return row.get(colentry, default)
- else: return default
+ if row:
+ return row.get(colentry, default)
+ else:
+ return default
def getCell(self, rowentry, colentry):
- """See ISecurityMap"""
marker = object()
cell = self.queryCell(rowentry, colentry, marker)
if cell is marker:
@@ -74,40 +95,66 @@
return cell
def getRow(self, rowentry):
- """See ISecurityMap"""
row = self._byrow.get(rowentry)
if row:
return row.items()
- else: return []
+ else:
+ return []
def getCol(self, colentry):
- """See ISecurityMap"""
col = self._bycol.get(colentry)
if col:
return col.items()
- else: return []
+ else:
+ return []
def getAllCells(self):
- """See ISecurityMap"""
- try:
- return self._v_cells
- except AttributeError:
- pass
res = []
for r in self._byrow.keys():
for c in self._byrow[r].items():
res.append((r,) + c)
- self._v_cells = res
return res
-
class PersistentSecurityMap(SecurityMap, Persistent):
- implements(ISecurityMap)
+ def addCell(self, rowentry, colentry, value):
+ if SecurityMap.addCell(self, rowentry, colentry, value):
+ self._p_changed = 1
- def _clear(self):
- self._byrow = PersistentDict()
- self._bycol = PersistentDict()
+ def delCell(self, rowentry, colentry):
+ if SecurityMap.delCell(self, rowentry, colentry):
+ self._p_changed = 1
- def _empty_mapping(self):
- return PersistentDict()
+class AnnotationSecurityMap(SecurityMap):
+
+ def __init__(self, context):
+ self._context = context
+ annotations = IAnnotations(self._context)
+ map = annotations.get(self.key)
+ if map is None:
+ self._byrow = {}
+ self._bycol = {}
+ else:
+ self._byrow = map._byrow
+ self._bycol = map._bycol
+ self.map = map
+
+ def _changed(self):
+ map = self.map
+ if isinstance(map, PersistentSecurityMap):
+ map._p_changed
+ else:
+ map = PersistentSecurityMap()
+ map._byrow = self._byrow
+ map._bycol = self._bycol
+ annotations = IAnnotations(self._context)
+ annotations[self.key] = map
+
+ def addCell(self, rowentry, colentry, value):
+ if SecurityMap.addCell(self, rowentry, colentry, value):
+ self._changed()
+
+ def delCell(self, rowentry, colentry):
+ if SecurityMap.delCell(self, rowentry, colentry):
+ self._changed()
+
Modified: Zope3/trunk/src/zope/app/securitypolicy/tests/test_securitymap.py
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/tests/test_securitymap.py 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/tests/test_securitymap.py 2004-07-21 22:52:23 UTC (rev 26668)
@@ -11,35 +11,51 @@
# FOR A PARTICULAR PURPOSE.
#
#############################################################################
-"""Test ISecurityMap implementations
+"""Test SecurityMap implementations
$Id$
"""
import unittest
from zope.interface.verify import verifyClass
-from zope.app.securitypolicy.interfaces import ISecurityMap
from zope.app.securitypolicy.securitymap import SecurityMap
from zope.app.securitypolicy.securitymap import PersistentSecurityMap
+from zope.security.management import setSecurityPolicy, getInteraction
+from zope.security.management import newInteraction, endInteraction
+class InteractionStub:
+ invalidated = 0
+ def invalidate_cache(self):
+ self.invalidated += 1
+
+
class TestSecurityMap(unittest.TestCase):
- def testInterface(self):
- verifyClass(ISecurityMap, SecurityMap)
+ def setUp(self):
+ self.oldpolicy = setSecurityPolicy(InteractionStub)
+ newInteraction()
+ def tearDown(self):
+ endInteraction()
+ setSecurityPolicy(self.oldpolicy)
+
def _getSecurityMap(self):
return SecurityMap()
def test_addCell(self):
map = self._getSecurityMap()
+ self.assertEqual(getInteraction().invalidated, 0)
map.addCell(0, 0, 'aa')
+ self.assertEqual(getInteraction().invalidated, 1)
self.assertEqual(map._byrow[0][0], 'aa')
self.assertEqual(map._bycol[0][0], 'aa')
map.addCell(1, 0, 'ba')
+ self.assertEqual(getInteraction().invalidated, 2)
self.assertEqual(map._byrow[1][0], 'ba')
self.assertEqual(map._bycol[0][1], 'ba')
map.addCell(5, 3, 'fd')
+ self.assertEqual(getInteraction().invalidated, 3)
self.assertEqual(map._byrow[5][3], 'fd')
self.assertEqual(map._bycol[3][5], 'fd')
@@ -56,18 +72,20 @@
def test_delCell(self):
map = self._getSecurityMap()
- map._byrow[0] = map._empty_mapping()
- map._bycol[1] = map._empty_mapping()
+ self.assertEqual(getInteraction().invalidated, 0)
+ map._byrow[0] = {}
+ map._bycol[1] = {}
map._byrow[0][1] = 'aa'
map._bycol[1][0] = 'aa'
map.delCell(0, 1)
- self.assertEqual(len(map._byrow.get(0)), 0)
- self.assertEqual(len(map._bycol.get(1)), 0)
+ self.assertEqual(getInteraction().invalidated, 1)
+ self.assertEqual(map._byrow.get(0), None)
+ self.assertEqual(map._bycol.get(1), None)
def test_queryCell(self):
map = self._getSecurityMap()
- map._byrow[0] = map._empty_mapping()
- map._bycol[1] = map._empty_mapping()
+ map._byrow[0] = {}
+ map._bycol[1] = {}
map._byrow[0][1] = 'aa'
map._bycol[1][0] = 'aa'
@@ -78,8 +96,8 @@
def test_getCell(self):
map = self._getSecurityMap()
- map._byrow[0] = map._empty_mapping()
- map._bycol[1] = map._empty_mapping()
+ map._byrow[0] = {}
+ map._bycol[1] = {}
map._byrow[0][1] = 'aa'
map._bycol[1][0] = 'aa'
@@ -88,15 +106,15 @@
def test_getRow(self):
map = self._getSecurityMap()
- map._byrow[0] = map._empty_mapping()
+ map._byrow[0] = {}
map._byrow[0][1] = 'ab'
map._byrow[0][2] = 'ac'
- map._byrow[1] = map._empty_mapping()
+ map._byrow[1] = {}
map._byrow[1][1] = 'bb'
- map._bycol[1] = map._empty_mapping()
+ map._bycol[1] = {}
map._bycol[1][0] = 'ab'
map._bycol[1][1] = 'bb'
- map._bycol[2] = map._empty_mapping()
+ map._bycol[2] = {}
map._bycol[2][0] = 'ac'
self.assertEqual(map.getRow(0), [(1, 'ab'), (2, 'ac')])
@@ -105,15 +123,15 @@
def test_getCol(self):
map = self._getSecurityMap()
- map._byrow[0] = map._empty_mapping()
+ map._byrow[0] = {}
map._byrow[0][1] = 'ab'
map._byrow[0][2] = 'ac'
- map._byrow[1] = map._empty_mapping()
+ map._byrow[1] = {}
map._byrow[1][1] = 'bb'
- map._bycol[1] = map._empty_mapping()
+ map._bycol[1] = {}
map._bycol[1][0] = 'ab'
map._bycol[1][1] = 'bb'
- map._bycol[2] = map._empty_mapping()
+ map._bycol[2] = {}
map._bycol[2][0] = 'ac'
self.assertEqual(map.getCol(1), [(0, 'ab'), (1, 'bb')])
@@ -122,15 +140,15 @@
def test_getAllCells(self):
map = self._getSecurityMap()
- map._byrow[0] = map._empty_mapping()
+ map._byrow[0] = {}
map._byrow[0][1] = 'ab'
map._byrow[0][2] = 'ac'
- map._byrow[1] = map._empty_mapping()
+ map._byrow[1] = {}
map._byrow[1][1] = 'bb'
- map._bycol[1] = map._empty_mapping()
+ map._bycol[1] = {}
map._bycol[1][0] = 'ab'
map._bycol[1][1] = 'bb'
- map._bycol[2] = map._empty_mapping()
+ map._bycol[2] = {}
map._bycol[2][0] = 'ac'
self.assertEqual(map.getCol(1), [(0, 'ab'), (1, 'bb')])
@@ -140,9 +158,6 @@
class TestPersistentSecurityMap(TestSecurityMap):
- def testInterface(self):
- verifyClass(ISecurityMap, PersistentSecurityMap)
-
def _getSecurityMap(self):
return PersistentSecurityMap()
Modified: Zope3/trunk/src/zope/app/securitypolicy/tests/test_zopepolicy.py
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/tests/test_zopepolicy.py 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/tests/test_zopepolicy.py 2004-07-21 22:52:23 UTC (rev 26668)
@@ -11,360 +11,61 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Tests the standard zope policy.
+"""Tests the zope policy.
$Id$
"""
-import unittest
-from zope.interface import implements
-from zope.interface.verify import verifyObject
-from zope.app import zapi
+import unittest
+from zope.testing.doctestunit import DocFileSuite
+from zope.app.tests import placelesssetup, ztapi
+from zope.app.annotation.interfaces import IAnnotatable
+from zope.app.annotation.interfaces import IAttributeAnnotatable
+from zope.app.annotation.interfaces import IAnnotations
from zope.app.annotation.attribute import AttributeAnnotations
-from zope.app.annotation.interfaces import IAttributeAnnotatable, IAnnotations
-from zope.app.security.principalregistry import principalRegistry, PrincipalBase
-from zope.app.security.interfaces import IPermission, IAuthenticationService
-from zope.app.security.permission import Permission
-from zope.app.servicenames import Authentication
-from zope.app.site.tests.placefulsetup import PlacefulSetup
-from zope.app.tests import ztapi
-
-from zope.app.securitypolicy.interfaces import IRole
-from zope.app.securitypolicy.interfaces import IRolePermissionManager
+from zope.app.securitypolicy.interfaces import IGrantInfo
from zope.app.securitypolicy.interfaces import IPrincipalRoleManager
-
-from zope.app.securitypolicy.role import Role
-from zope.app.securitypolicy.zopepolicy import permissionsOfPrincipal
-from zope.app.securitypolicy.principalpermission \
- import principalPermissionManager
-from zope.app.securitypolicy.rolepermission import rolePermissionManager
-from zope.app.securitypolicy.principalrole import principalRoleManager
-from zope.app.securitypolicy.principalpermission \
- import AnnotationPrincipalPermissionManager
from zope.app.securitypolicy.interfaces import IPrincipalPermissionManager
+from zope.app.securitypolicy.interfaces import IRolePermissionManager
+from zope.app.securitypolicy.principalpermission \
+ import AnnotationPrincipalPermissionManager
from zope.app.securitypolicy.principalrole \
import AnnotationPrincipalRoleManager
from zope.app.securitypolicy.rolepermission \
- import AnnotationRolePermissionManager
+ import AnnotationRolePermissionManager
+from zope.app.securitypolicy.grantinfo \
+ import AnnotationGrantInfo
+from zope.security.management import endInteraction
+def setUp():
+ placelesssetup.setUp()
+ endInteraction()
+ ztapi.provideAdapter(
+ IAttributeAnnotatable, IAnnotations,
+ AttributeAnnotations)
+ ztapi.provideAdapter(
+ IAnnotatable, IPrincipalPermissionManager,
+ AnnotationPrincipalPermissionManager)
+ ztapi.provideAdapter(
+ IAnnotatable, IPrincipalRoleManager,
+ AnnotationPrincipalRoleManager)
+ ztapi.provideAdapter(
+ IAnnotatable, IRolePermissionManager,
+ AnnotationRolePermissionManager)
+ ztapi.provideAdapter(
+ IAnnotatable, IGrantInfo,
+ AnnotationGrantInfo)
-class RequestStub(object):
- def __init__(self, principal, interaction=None):
- self.principal = principal
- self.interaction = interaction
+def tearDown():
+ placelesssetup.tearDown()
+
-class Interaction(object):
- def __init__(self, user):
- self.participations = [RequestStub(user, self)]
-
-class Unprotected(object):
- pass
-
-class Principal(PrincipalBase):
- pass
-
-
-def defineRole(id, title=None, description=None):
- role = Role(id, title, description)
- ztapi.provideUtility(IRole, role, name=role.id)
- return role
-
-def definePermission(id, title=None, description=None):
- perm = Permission(id, title, description)
- ztapi.provideUtility(IPermission, perm, name=perm.id)
- return perm
-
-
-class Test(PlacefulSetup, unittest.TestCase):
-
- def setUp(self):
- PlacefulSetup.setUp(self)
- services = zapi.getGlobalServices()
-
- services.defineService(Authentication, IAuthenticationService)
- services.provideService(Authentication, principalRegistry)
-
- ztapi.provideAdapter(
- IAttributeAnnotatable, IAnnotations,
- AttributeAnnotations)
-
- # set up some principals
- self.jim = principalRegistry.definePrincipal(
- 'jim', 'Jim', 'Jim Fulton',
- 'jim', '123')
-
- self.tim = principalRegistry.definePrincipal(
- 'tim', 'Tim', 'Tim Peters',
- 'tim', '456')
-
- self.unknown = principalRegistry.defineDefaultPrincipal('unknown',
- 'Unknown', 'Nothing is known about this principal')
-
- # set up some permissions
- self.read = definePermission('read', 'Read', 'Read something').id
-
- self.write = definePermission('write', 'Write', 'Write something').id
-
- self.create = definePermission('create', 'Create',
- 'Create something').id
-
- self.update = definePermission('update', 'Update',
- 'Update something').id
-
- # ... and some roles...
- defineRole("zope.Anonymous", "Everybody",
- "All users have this role implicitly")
-
- self.peon = defineRole('Peon', 'Site Peon').id
-
- self.manager = defineRole('Manager', 'Site Manager').id
-
- self.arole = defineRole('Another', 'Another Role').id
-
- # grant and deny some permissions to a principal
- principalPermissionManager.grantPermissionToPrincipal(
- self.create, self.jim.id)
- principalPermissionManager.denyPermissionToPrincipal(
- self.update, self.jim.id)
-
- # grant and deny some permissions to the roles
- rolePermissionManager.grantPermissionToRole(self.read, self.peon)
-
- rolePermissionManager.grantPermissionToRole(self.read, self.manager)
- rolePermissionManager.grantPermissionToRole(self.write, self.manager)
-
- # ... and assign roles to principals
- principalRoleManager.assignRoleToPrincipal(self.peon, self.jim.id)
- principalRoleManager.assignRoleToPrincipal(self.manager, self.tim.id)
-
- self.interaction = self._makeInteraction()
-
-
- def _makeInteraction(self):
- from zope.app.securitypolicy.zopepolicy import ZopeSecurityPolicy
- return ZopeSecurityPolicy()
-
-
- def __assertPermissions(self, user, expected, object=None):
- permissions = list(permissionsOfPrincipal(user, object))
- permissions.sort()
- self.assertEqual(permissions, expected)
-
- def testImport(self):
- from zope.app.securitypolicy.zopepolicy import ZopeSecurityPolicy
-
- def testInterfaces(self):
- from zope.security.interfaces import ISecurityPolicy
- from zope.app.securitypolicy.zopepolicy import ZopeSecurityPolicy
- verifyObject(ISecurityPolicy, ZopeSecurityPolicy)
-
- def testCreateInteraction(self):
- from zope.security.interfaces import IInteraction
- from zope.app.securitypolicy.zopepolicy import ZopeSecurityPolicy
- i1 = ZopeSecurityPolicy()
- verifyObject(IInteraction, i1)
- self.assertEquals(list(i1.participations), [])
-
- user = object()
- rq = RequestStub(user)
- i2 = ZopeSecurityPolicy(rq)
- verifyObject(IInteraction, i2)
- self.assertEquals(list(i2.participations), [rq])
-
- def testGlobalCheckPermission(self):
- r = RequestStub(self.jim)
- self.interaction.add(r)
- self.failUnless(self.interaction.checkPermission(self.read, None))
- self.interaction.remove(r)
-
- r = RequestStub(self.tim)
- self.interaction.add(r)
- self.failUnless(self.interaction.checkPermission(self.read, None))
- self.failUnless(self.interaction.checkPermission(self.write, None))
- self.interaction.remove(r)
-
- r = RequestStub(self.unknown)
- self.interaction.add(r)
- self.failIf(self.interaction.checkPermission(self.read, None))
- self.failIf(self.interaction.checkPermission(self.write, None))
-
- self.failIf(self.interaction.checkPermission(self.read, None))
-
- self.__assertPermissions(self.jim, ['create', 'read'])
- self.__assertPermissions(self.tim, ['read', 'write'])
- self.__assertPermissions(self.unknown, [])
-
- rolePermissionManager.grantPermissionToRole(
- self.read, 'zope.Anonymous')
-
- self.failUnless(self.interaction.checkPermission(self.read, None))
- self.interaction.remove(r)
-
- self.__assertPermissions(self.unknown, ['read'])
-
- principalPermissionManager.grantPermissionToPrincipal(
- self.write, self.jim.id)
- r = RequestStub(self.jim)
- self.interaction.add(r)
- self.failUnless(self.interaction.checkPermission(self.write, None))
-
- self.__assertPermissions(self.jim, ['create', 'read', 'write'])
-
- def testPlaylessPrincipalRole(self):
- r = RequestStub(self.jim)
- self.interaction.add(r)
- self.failIf(self.interaction.checkPermission(self.write, None))
- principalRoleManager.assignRoleToPrincipal(
- self.manager, self.jim.id)
- self.failUnless(self.interaction.checkPermission(self.write, None))
- principalRoleManager.removeRoleFromPrincipal(
- self.manager, self.jim.id)
- self.failIf(self.interaction.checkPermission(self.write, None))
-
- def testPlayfulPrincipalRole(self):
- ztapi.provideAdapter(
- ITest,
- IPrincipalRoleManager, AnnotationPrincipalRoleManager)
-
- ob1 = TestClass()
- ob2 = TestClass(); ob2.__parent__ = ob1
- ob3 = TestClass(); ob3.__parent__ = ob2
-
- r = RequestStub(self.jim)
- self.interaction.add(r)
- self.failIf(self.interaction.checkPermission(self.write, ob3))
- AnnotationPrincipalRoleManager(ob3).assignRoleToPrincipal(
- self.manager, self.jim.id)
- self.failUnless(self.interaction.checkPermission(self.write, ob3))
- AnnotationPrincipalRoleManager(ob3).removeRoleFromPrincipal(
- self.manager, self.jim.id)
- self.failIf(self.interaction.checkPermission(self.write, ob3))
-
- def testPlayfulRolePermissions(self):
-
- ARPM = AnnotationRolePermissionManager
- ztapi.provideAdapter(ITest,
- IRolePermissionManager, ARPM)
- test = definePermission('test', 'Test', '')
- test = test.id
-
- ob1 = TestClass()
- ob2 = TestClass(); ob2.__parent__ = ob1
- ob3 = TestClass(); ob3.__parent__ = ob2
-
- r = RequestStub(self.tim)
- self.interaction.add(r)
- self.failIf(self.interaction.checkPermission(test, ob3))
- self.__assertPermissions(self.tim, ['read', 'write'], ob3)
-
- ARPM(ob2).grantPermissionToRole(test, self.manager)
- self.failUnless(self.interaction.checkPermission(test, ob3))
- self.__assertPermissions(self.tim, ['read', 'test', 'write'], ob3)
- self.interaction.remove(r)
-
- r = RequestStub(self.jim)
- self.interaction.add(r)
- self.failIf(self.interaction.checkPermission(test, ob3))
- self.__assertPermissions(self.jim, ['create', 'read'], ob3)
-
-
- ARPM(ob3).grantPermissionToRole(test, self.peon)
- self.failUnless(self.interaction.checkPermission(test, ob3))
- self.__assertPermissions(self.jim, ['create', 'read', 'test'], ob3)
-
-
-
- principalPermissionManager.denyPermissionToPrincipal(
- test, self.jim.id)
- self.failIf(self.interaction.checkPermission(test, ob3))
- self.__assertPermissions(self.jim, ['create', 'read'], ob3)
- self.interaction.remove(r)
-
- principalPermissionManager.unsetPermissionForPrincipal(
- test, self.jim.id)
-
- # Make sure multiple conflicting role permissions resolve correctly
- ARPM(ob2).grantPermissionToRole(test, 'zope.Anonymous')
- ARPM(ob2).grantPermissionToRole(test, self.arole)
- ARPM(ob3).denyPermissionToRole(test, self.peon)
-
- new = principalRegistry.definePrincipal('new', 'Newbie',
- 'Newbie User', 'new', '098')
- principalRoleManager.assignRoleToPrincipal(self.arole, new.id)
- r = RequestStub(new)
- self.interaction.add(r)
- self.failUnless(self.interaction.checkPermission(test, ob3))
- self.__assertPermissions(new, ['test'], ob3)
-
- principalRoleManager.assignRoleToPrincipal(self.peon, new.id)
- self.failIf(self.interaction.checkPermission(test, ob3))
- self.__assertPermissions(new, ['read'], ob3)
-
- def testPlayfulPrinciplePermissions(self):
- APPM = AnnotationPrincipalPermissionManager
- ztapi.provideAdapter(ITest,
- IPrincipalPermissionManager, APPM)
-
- ob1 = TestClass()
- ob2 = TestClass(); ob2.__parent__ = ob1
- ob3 = TestClass(); ob3.__parent__ = ob2
-
- test = definePermission('test', 'Test', '').id
-
- r = RequestStub(self.tim)
- self.interaction.add(r)
- self.failIf(self.interaction.checkPermission(test, ob3))
-
- self.__assertPermissions(self.tim, ['read', 'write'], ob3)
-
- APPM(ob2).grantPermissionToPrincipal(test, self.tim.id)
- self.failUnless(self.interaction.checkPermission(test, ob3))
- self.__assertPermissions(self.tim, ['read', 'test', 'write'], ob3)
-
- APPM(ob3).denyPermissionToPrincipal(test, self.tim.id)
- self.failIf(self.interaction.checkPermission(test, ob3))
- self.__assertPermissions(self.tim, ['read', 'write'], ob3)
- self.interaction.remove(r)
-
- r = RequestStub(self.jim)
- self.interaction.add(r)
- APPM(ob1).denyPermissionToPrincipal(test, self.jim.id)
- APPM(ob3).grantPermissionToPrincipal(test, self.jim.id)
- self.failUnless(self.interaction.checkPermission(test, ob3))
- self.__assertPermissions(self.jim, ['create', 'read', 'test'], ob3)
-
-
- APPM(ob3).unsetPermissionForPrincipal(test, self.jim.id)
- self.failIf(self.interaction.checkPermission(test, ob3))
- self.__assertPermissions(self.jim, ['create', 'read'], ob3)
- self.interaction.remove(r)
-
- # make sure placeless principal permissions override placeful ones
- r = RequestStub(self.tim)
- self.interaction.add(r)
- APPM(ob3).grantPermissionToPrincipal(test, self.tim.id)
- principalPermissionManager.denyPermissionToPrincipal(
- test, self.tim.id)
- self.failIf(self.interaction.checkPermission(test, ob3))
-
- self.__assertPermissions(self.tim, ['read', 'write'], ob3)
-
-
-class ITest(IAttributeAnnotatable):
- pass
-
-class TestClass(object):
- implements(ITest)
-
- __parent__ = None
-
- def __init__(self):
- self._roles = { 'test' : {} }
- self._permissions = { 'Manager' : {} , 'Peon' : {} }
-
def test_suite():
- loader=unittest.TestLoader()
- return loader.loadTestsFromTestCase(Test)
+ return unittest.TestSuite((
+ DocFileSuite('zopepolicy.txt',
+ package='zope.app.securitypolicy',
+ setUp=setUp, tearDown=tearDown),
+ ))
-if __name__=='__main__':
- unittest.TextTestRunner().run(test_suite())
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Modified: Zope3/trunk/src/zope/app/securitypolicy/zopepolicy.py
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/zopepolicy.py 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/zopepolicy.py 2004-07-21 22:52:23 UTC (rev 26668)
@@ -15,246 +15,268 @@
$Id$
"""
+
+import zope.interface
+
from zope.security.management import system_user
-import zope.security.simplepolicies
+from zope.security.simplepolicies import ParanoidSecurityPolicy
from zope.security.interfaces import ISecurityPolicy
+from zope.security.proxy import getProxiedObject
-from zope.app.location import LocationIterator
from zope.app.security.settings import Allow, Deny
-from zope.app.securitypolicy.interfaces import \
- IRolePermissionMap, IPrincipalPermissionMap, IPrincipalRoleMap
+
from zope.app.securitypolicy.principalpermission \
import principalPermissionManager
+globalPrincipalPermissionSetting = principalPermissionManager.getSetting
+
from zope.app.securitypolicy.rolepermission import rolePermissionManager
+globalRolesForPermission = rolePermissionManager.getRolesForPermission
+
from zope.app.securitypolicy.principalrole import principalRoleManager
+globalRolesForPrincipal = principalRoleManager.getRolesForPrincipal
-getPermissionsForPrincipal = \
- principalPermissionManager.getPermissionsForPrincipal
-getPermissionsForRole = rolePermissionManager.getPermissionsForRole
-getRolesForPrincipal = principalRoleManager.getRolesForPrincipal
+from zope.app.securitypolicy.interfaces import IRolePermissionMap
+from zope.app.securitypolicy.interfaces import IPrincipalPermissionMap
+from zope.app.securitypolicy.interfaces import IPrincipalRoleMap
+from zope.app.securitypolicy.interfaces import IGrantInfo
-globalContext = object()
+class CacheEntry:
+ pass
+
+class ZopeSecurityPolicy(ParanoidSecurityPolicy):
+ zope.interface.classProvides(ISecurityPolicy)
+ def __init__(self, *args, **kw):
+ ParanoidSecurityPolicy.__init__(self, *args, **kw)
+ self._cache = {}
-class ZopeSecurityPolicy(zope.security.simplepolicies.ParanoidSecurityPolicy):
- zope.interface.classProvides(ISecurityPolicy)
+ def invalidate_cache(self):
+ self._cache = {}
- def checkPermission(self, permission, object):
- # XXX We aren't really handling multiple principals yet
- assert len(self.participations) == 1 # XXX
- user = self.participations[0].principal
+ def cache(self, parent):
+ cache = self._cache.get(id(parent))
+ if cache:
+ cache = cache[0]
+ else:
+ cache = CacheEntry()
+ self._cache[id(parent)] = cache, parent
+ return cache
+
+ def cached_decision(self, parent, principal, permission):
+ cache = self.cache(parent)
+ try:
+ cache_decision = cache.decision
+ except AttributeError:
+ cache_decision = cache.decision = {}
- # mapping from principal to set of roles
- if user is system_user:
- return True
+ cache_decision_prin = cache_decision.get(principal)
+ if not cache_decision_prin:
+ cache_decision_prin = cache_decision[principal] = {}
+
+ try:
+ return cache_decision_prin[permission]
+ except KeyError:
+ pass
+
+ decision = self.cached_prinper(parent, principal, permission)
+ if decision is not None:
+ cache_decision_prin[permission] = decision
+ return decision
- roledict = {'zope.Anonymous': Allow}
- principals = {user.id : roledict}
+ roles = self.cached_roles(parent, permission)
+ if roles:
+ for role in self.cached_principal_roles(parent, principal):
+ if role in roles:
+ cache_decision_prin[permission] = decision = True
+ return decision
- role_permissions = {}
- remove = {}
+ cache_decision_prin[permission] = decision = False
+ return decision
+
+ def cached_prinper(self, parent, principal, permission):
+ cache = self.cache(parent)
+ try:
+ cache_prin = cache.prin
+ except AttributeError:
+ cache_prin = cache.prin = {}
- # Look for placeless grants first.
+ cache_prin_per = cache_prin.get(principal)
+ if not cache_prin_per:
+ cache_prin_per = cache_prin[principal] = {}
- # get placeless principal permissions
- for principal in principals:
- for principal_permission, setting in (
- getPermissionsForPrincipal(principal)):
- if principal_permission == permission:
- if setting is Deny:
- return False
- assert setting is Allow
- remove[principal] = True
+ try:
+ return cache_prin_per[permission]
+ except KeyError:
+ pass
- # Clean out removed principals
- if remove:
- for principal in remove:
- del principals[principal]
- if principals:
- # not done yet
- remove.clear()
- else:
- # we've eliminated all the principals
- return True
+ if parent is None:
+ prinper = globalPrincipalPermissionSetting(
+ permission, principal, None)
+ if prinper is not None:
+ prinper = prinper is Allow
+ cache_prin_per[permission] = prinper
+ return prinper
- # get placeless principal roles
- for principal in principals:
- roles = principals[principal]
- for role, setting in getRolesForPrincipal(principal):
- assert setting in (Allow, Deny)
- if role not in roles:
- roles[role] = setting
+ prinper = IPrincipalPermissionMap(parent, None)
+ if prinper is not None:
+ prinper = prinper.getSetting(permission, principal, None)
+ if prinper is not None:
+ prinper = prinper is Allow
+ cache_prin_per[permission] = prinper
+ return prinper
- for perm, role, setting in (
- rolePermissionManager.getRolesAndPermissions()):
- assert setting in (Allow, Deny)
- if role not in role_permissions:
- role_permissions[role] = {perm: setting}
- else:
- if perm not in role_permissions[role]:
- role_permissions[role][perm] = setting
+ parent = getProxiedObject(getattr(parent, '__parent__', None))
+ prinper = self.cached_prinper(parent, principal, permission)
+ cache_prin_per[permission] = prinper
+ return prinper
+
+ def cached_roles(self, parent, permission):
+ cache = self.cache(parent)
+ try:
+ cache_roles = cache.roles
+ except AttributeError:
+ cache_roles = cache.roles = {}
+ try:
+ return cache_roles[permission]
+ except KeyError:
+ pass
+
+ if parent is None:
+ roles = dict(
+ [(role, 1)
+ for (role, setting) in globalRolesForPermission(permission)
+ if setting is Allow
+ ]
+ )
+ cache_roles[permission] = roles
+ return roles
- # Get principal permissions based on roles
- for principal in principals:
- roles = principals[principal]
- for role, role_setting in roles.items():
- if role_setting is Deny:
- return False
- if role in role_permissions:
- if permission in role_permissions[role]:
- setting = role_permissions[role][permission]
- if setting is Deny:
- return False
- remove[principal] = True
+ roles = self.cached_roles(
+ getProxiedObject(getattr(parent, '__parent__', None)),
+ permission)
+ roleper = IRolePermissionMap(parent, None)
+ if roleper:
+ roles = roles.copy()
+ for role, setting in roleper.getRolesForPermission(permission):
+ if setting is Allow:
+ roles[role] = 1
+ elif role in roles:
+ del roles[role]
+ cache_roles[permission] = roles
+ return roles
- # Clean out removed principals
- if remove:
- for principal in remove:
- del principals[principal]
- if principals:
- # not done yet
- remove.clear()
- else:
- # we've eliminated all the principals
- return True
+ def cached_principal_roles(self, parent, principal):
+ cache = self.cache(parent)
+ try:
+ cache_principal_roles = cache.principal_roles
+ except AttributeError:
+ cache_principal_roles = cache.principal_roles = {}
+ try:
+ return cache_principal_roles[principal]
+ except KeyError:
+ pass
- # Look for placeful grants
- for place in LocationIterator(object):
+ if parent is None:
+ roles = dict(
+ [(role, 1)
+ for (role, setting) in globalRolesForPrincipal(principal)
+ if setting is Allow
+ ]
+ )
+ roles['zope.Anonymous'] = 1 # Everybody has Anonymous
+ cache_principal_roles[principal] = roles
+ return roles
+
+ roles = self.cached_principal_roles(
+ getProxiedObject(getattr(parent, '__parent__', None)),
+ principal)
+ prinrole = IPrincipalRoleMap(parent, None)
+ if prinrole:
+ roles = roles.copy()
+ for role, setting in prinrole.getRolesForPrincipal(principal):
+ if setting is Allow:
+ roles[role] = 1
+ elif role in roles:
+ del roles[role]
- # Copy specific principal permissions
- prinper = IPrincipalPermissionMap(place, None)
- if prinper is not None:
- for principal in principals:
- for principal_permission, setting in (
- prinper.getPermissionsForPrincipal(principal)):
- if principal_permission == permission:
- if setting is Deny:
- return False
+ cache_principal_roles[principal] = roles
+ return roles
+
- assert setting is Allow
- remove[principal] = True
+ def checkPermission(self, permission, object):
+ principals = {}
+ for participation in self.participations:
+ principal = participation.principal
+ if principal is system_user:
+ continue # always allow system_user
+ principals[principal.id] = 1
- # Clean out removed principals
- if remove:
- for principal in remove:
- del principals[principal]
- if principals:
- # not done yet
- remove.clear()
- else:
- # we've eliminated all the principals
- return True
+ if not principals:
+ return True
- # Collect principal roles
- prinrole = IPrincipalRoleMap(place, None)
- if prinrole is not None:
- for principal in principals:
- roles = principals[principal]
- for role, setting in (
- prinrole.getRolesForPrincipal(principal)):
- assert setting in (Allow, Deny)
- if role not in roles:
- roles[role] = setting
+ object = getProxiedObject(object)
+ parent = getProxiedObject(getattr(object, '__parent__', None))
- # Collect role permissions
- roleper = IRolePermissionMap(place, None)
- if roleper is not None:
- for perm, role, setting in roleper.getRolesAndPermissions():
- assert setting in (Allow, Deny)
- if role not in role_permissions:
- role_permissions[role] = {perm: setting}
- else:
- if perm not in role_permissions[role]:
- role_permissions[role][perm] = setting
-
- # Get principal permissions based on roles
+ grant_info = IGrantInfo(object, None)
+ if not grant_info:
+ # No local grants; just use cached decision for parent
for principal in principals:
- roles = principals[principal]
- for role, role_setting in roles.items():
- if role_setting is Deny:
- return False
- if role in role_permissions:
- if permission in role_permissions[role]:
- setting = role_permissions[role][permission]
- if setting is Deny:
- return False
- remove[principal] = True
+ if not self.cached_decision(parent, principal, permission):
+ return False
+ return True
- # Clean out removed principals
- if remove:
- for principal in remove:
+ # We need to combine local and parent info
+
+ # First, look for principal grants
+ for principal in principals.keys():
+ setting = grant_info.principalPermissionGrant(
+ principal, permission)
+ if setting is Deny:
+ return False
+ elif setting is Allow: # setting could be None
+ del principals[principal]
+ if not principals:
+ return True
+ continue
+
+ decision = self.cached_prinper(parent, principal, permission)
+ if decision is not None:
+ if decision:
del principals[principal]
- if principals:
- # not done yet
- remove.clear()
+ if not principals:
+ return True
else:
- # we've eliminated all the principals
- return True
+ return decision # False
- return False # deny by default
+ roles = self.cached_roles(parent, permission)
+ local_roles = grant_info.getRolesForPermission(permission)
+ if local_roles:
+ roles = roles.copy()
+ for role, setting in local_roles:
+ if setting is Allow:
+ roles[role] = 1
+ elif role in roles:
+ del roles[role]
+ for principal in principals.keys():
+ proles = self.cached_principal_roles(parent, principal).copy()
+ for role, setting in grant_info.getRolesForPrincipal(principal):
+ if setting is Allow:
+ if role in roles:
+ del principals[principal]
+ if not principals:
+ return True
+ break
+ elif role in proles:
+ del proles[role]
+ else:
+ for role in proles:
+ if role in roles:
+ del principals[principal]
+ if not principals:
+ return True
+ break
+
+ return False
-def permissionsOfPrincipal(principal, object):
- permissions = {}
-
- roles = {'zope.Anonymous': Allow}
- principalid = principal.id
-
- # Make two passes.
-
- # First, collect what we know about the principal:
-
-
- # get placeless principal permissions
- for permission, setting in getPermissionsForPrincipal(principalid):
- if permission not in permissions:
- permissions[permission] = setting
-
- # get placeless principal roles
- for role, setting in getRolesForPrincipal(principalid):
- if role not in roles:
- roles[role] = setting
-
- # get placeful principal permissions and roles
- for place in LocationIterator(object):
-
- # Copy specific principal permissions
- prinper = IPrincipalPermissionMap(place, None)
- if prinper is not None:
- for permission, setting in prinper.getPermissionsForPrincipal(
- principalid):
- if permission not in permissions:
- permissions[permission] = setting
-
- # Collect principal roles
- prinrole = IPrincipalRoleMap(place, None)
- if prinrole is not None:
- for role, setting in prinrole.getRolesForPrincipal(principalid):
- if role not in roles:
- roles[role] = setting
-
- # Second, update permissions using principal
-
- for perm, role, setting in (
- rolePermissionManager.getRolesAndPermissions()):
- if role in roles and perm not in permissions:
- permissions[perm] = setting
-
- for place in LocationIterator(object):
-
- # Collect role permissions
- roleper = IRolePermissionMap(place, None)
- if roleper is not None:
- for perm, role, setting in roleper.getRolesAndPermissions():
- if role in roles and perm not in permissions:
- permissions[perm] = setting
-
-
-
- result = [permission
- for permission in permissions
- if permissions[permission] is Allow]
-
- return result
-
Added: Zope3/trunk/src/zope/app/securitypolicy/zopepolicy.txt
===================================================================
--- Zope3/trunk/src/zope/app/securitypolicy/zopepolicy.txt 2004-07-21 22:51:35 UTC (rev 26667)
+++ Zope3/trunk/src/zope/app/securitypolicy/zopepolicy.txt 2004-07-21 22:52:23 UTC (rev 26668)
@@ -0,0 +1,480 @@
+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.app.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`
+attribute:
+
+ >>> class Principal:
+ ... pass
+ >>> principal = Principal()
+ >>> principal.id = 'bob'
+
+Roles and permissions are also represented by objects, however, for
+the purposes of the scurity 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 participataing 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
+
+We make grants and denials on objects by adaping them to various
+granting interfaces:
+
+ >>> 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 out 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 verses 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 prinicipal-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
+whos `__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
+prinicipal-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
+it's grants from it's 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:
+
+ >>> del ob3.__parent__
+
+it will have whatever grants were made globally:
+
+ >>> 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)
+ False
+ >>> interaction.checkPermission('P4G', ob3)
+ False
+
+ >>> prinroleG.assignRoleToPrincipal('R1G', "bob", False)
+ >>> interaction.checkPermission('P3G', ob3)
+ True
+
+We'll get the same result if we have a non-annotatble 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
+
+Cleanup
+-------
+
+We clean up the changes we made:
+
+ >>> zope.security.management.endInteraction()
+ >>> ignore = zope.security.management.setSecurityPolicy(oldpolicy)
Property changes on: Zope3/trunk/src/zope/app/securitypolicy/zopepolicy.txt
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Zope3-Checkins
mailing list