[Zope3-checkins] SVN: Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/ Merged from trunk:

Jim Fulton jim at zope.com
Fri Jul 23 14:42:15 EDT 2004


Log message for revision 26714:
  Merged from trunk:
  
    r26668 | jim | 2004-07-21 18:52:23 -0400 (Wed, 21 Jul 2004) | 17 lines
  
  - 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/branches/ZopeX3-3.0/src/zope/app/securitypolicy/browser/configure.zcml
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/browser/rolepermissionview.py
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/configure.zcml
  A   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/grantinfo.py
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/interfaces.py
  D   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/permissionroles.py
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/principalpermission.py
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/principalrole.py
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/rolepermission.py
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/securitymap.py
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/tests/test_securitymap.py
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/tests/test_zopepolicy.py
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/zopepolicy.py
  A   Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/zopepolicy.txt


-=-
Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/browser/configure.zcml
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/browser/configure.zcml	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/browser/configure.zcml	2004-07-23 18:42:15 UTC (rev 26714)
@@ -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/branches/ZopeX3-3.0/src/zope/app/securitypolicy/browser/rolepermissionview.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/browser/rolepermissionview.py	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/browser/rolepermissionview.py	2004-07-23 18:42:15 UTC (rev 26714)
@@ -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:
 
@@ -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/branches/ZopeX3-3.0/src/zope/app/securitypolicy/configure.zcml
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/configure.zcml	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/configure.zcml	2004-07-23 18:42:15 UTC (rev 26714)
@@ -3,29 +3,56 @@
     i18n_domain="zope"
     >
 
-  <content class=".permissionroles.PermissionRoles">
-    <require permission="zope.Security"
-             attributes="roles rolesInfo id title description" />
-  </content>
-
-  <content class=".rolepermission.RolePermissions">
-    <require permission="zope.Security"
-             attributes="permissions permissionsInfo id title description" />
-  </content>
-
   <adapter factory=".rolepermission.AnnotationRolePermissionManager"
            provides=".interfaces.IRolePermissionManager"
-           for="zope.app.annotation.interfaces.IAnnotatable" />
+           for="zope.app.annotation.interfaces.IAnnotatable"
+           trusted="true" 
+           />
 
+  <class class=".rolepermission.AnnotationRolePermissionManager">
+    <require 
+        permission="zope.Security"
+        attributes="grantPermissionToRole denyPermissionToRole
+                    unsetPermissionFromRole" 
+        />
+    <allow interface=".interfaces.IRolePermissionMap" />
+  </class>       
+
   <adapter factory=".principalrole.AnnotationPrincipalRoleManager"
            provides=".interfaces.IPrincipalRoleManager"
-           for="zope.app.annotation.interfaces.IAnnotatable" />
+           for="zope.app.annotation.interfaces.IAnnotatable" 
+           trusted="true" 
+           />
 
+  <class class=".principalrole.AnnotationPrincipalRoleManager">
+    <require 
+        permission="zope.Security"
+        attributes="assignRoleToPrincipal removeRoleFromPrincipal
+                    unsetRoleForPrincipal" 
+        />
+    <allow interface=".interfaces.IPrincipalRoleMap" />
+  </class>
+
   <adapter factory=".principalpermission.AnnotationPrincipalPermissionManager"
            provides=".interfaces.IPrincipalPermissionManager"
-           for="zope.app.annotation.interfaces.IAnnotatable" />
+           for="zope.app.annotation.interfaces.IAnnotatable" 
+           trusted="true"
+           />
 
+  <class class=".principalpermission.AnnotationPrincipalPermissionManager">
+    <require 
+        permission="zope.Security"
+        attributes="grantPermissionToRole denyPermissionToRole
+                    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" />

Copied: Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/grantinfo.py (from rev 26668, Zope3/trunk/src/zope/app/securitypolicy/grantinfo.py)


Property changes on: Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/grantinfo.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/interfaces.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/interfaces.py	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/interfaces.py	2004-07-23 18:42:15 UTC (rev 26714)
@@ -18,28 +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 "
-
-    # XXX queryCell / getCell ?
-    def getCell(rowentry, colentry, default=None):
-        " return the value of a cell by row, entry "
-
-    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."""
 
@@ -220,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/branches/ZopeX3-3.0/src/zope/app/securitypolicy/permissionroles.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/permissionroles.py	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/permissionroles.py	2004-07-23 18:42:15 UTC (rev 26714)
@@ -1,57 +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.
-#
-##############################################################################
-"""
-$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/branches/ZopeX3-3.0/src/zope/app/securitypolicy/principalpermission.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/principalpermission.py	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/principalpermission.py	2004-07-23 18:42:15 UTC (rev 26714)
@@ -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 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:
+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.getCell(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.getCell(permission_id, principal_id, default=Unset)
+        return self.queryCell(permission_id, principal_id, default)
 
     def getPrincipalsAndPermissions(self):
         ''' See the interface IPrincipalPermissionManager '''

Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/principalrole.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/principalrole.py	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/principalrole.py	2004-07-23 18:42:15 UTC (rev 26714)
@@ -16,95 +16,46 @@
 __metaclass__ = type
 
 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:
+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.getCell(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/branches/ZopeX3-3.0/src/zope/app/securitypolicy/rolepermission.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/rolepermission.py	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/rolepermission.py	2004-07-23 18:42:15 UTC (rev 26714)
@@ -16,7 +16,6 @@
 """
 from zope.interface import implements
 
-from zope.app.annotation.interfaces import IAnnotations
 
 from zope.app.security.settings import Allow, Deny, Unset
 from zope.app.security.permission import allPermissions
@@ -25,135 +24,35 @@
 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.getCell(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/branches/ZopeX3-3.0/src/zope/app/securitypolicy/securitymap.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/securitymap.py	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/securitymap.py	2004-07-23 18:42:15 UTC (rev 26714)
@@ -14,14 +14,11 @@
 """ Generic two-dimensional array type """
 
 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()
 
@@ -29,73 +26,133 @@
         self._byrow = {}
         self._bycol = {}
 
-    def _empty_mapping(self):
-        return {}
+    def __nonzero__(self):
+        return bool(self._byrow)
 
     def addCell(self, rowentry, colentry, value):
         # 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):
         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]
 
-    def getCell(self, rowentry, colentry, default=None):
-        " return the value of a cell by row, entry "
+            self._invalidated_interaction_cache()
+
+            return True
+
+        return False
+
+    def queryCell(self, rowentry, colentry, default=None):
         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):
+        marker = object()
+        cell = self.queryCell(rowentry, colentry, marker)
+        if cell is marker:
+            raise KeyError('Not a valid row and column pair.')
+        return cell
+
     def getRow(self, rowentry):
-        " return a list of (colentry, value) tuples from a row "
         row = self._byrow.get(rowentry)
         if row:
             return row.items()
-        else: return []
+        else:
+            return []
 
     def getCol(self, colentry):
-        " return a list of (rowentry, value) tuples from a col "
         col = self._bycol.get(colentry)
         if col:
             return col.items()
-        else: return []
+        else:
+            return []
 
     def getAllCells(self):
-        " return a list of (rowentry, colentry, value) "
-        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/branches/ZopeX3-3.0/src/zope/app/securitypolicy/tests/test_securitymap.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/tests/test_securitymap.py	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/tests/test_securitymap.py	2004-07-23 18:42:15 UTC (rev 26714)
@@ -11,30 +11,156 @@
 # FOR A PARTICULAR PURPOSE.
 #
 #############################################################################
-"""
+"""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()
 
-    # XXX Test the map. Grrrrr.
+    def tearDown(self):
+        endInteraction()
+        setSecurityPolicy(self.oldpolicy)
 
-class TestPersistentSecurityMap(TestSecurityMap):
+    def _getSecurityMap(self):
+        return SecurityMap()
 
-    def testInterface(self):
-        verifyClass(ISecurityMap, PersistentSecurityMap)
+    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')
 
-    # XXX test persistence...
+        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')
 
+    def test_addCell_noninteger(self):
+        map = self._getSecurityMap()
+        map.addCell(0.3, 0.4, 'entry')
+        self.assertEqual(map._byrow[0.3][0.4], 'entry')
+        self.assertEqual(map._bycol[0.4][0.3], 'entry')
+
+        marker = object()
+        map.addCell('a', 'b', marker)
+        self.assertEqual(map._byrow['a']['b'], marker)
+        self.assertEqual(map._bycol['b']['a'], marker)
+        
+    def test_delCell(self):
+        map = self._getSecurityMap()
+        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(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._bycol[1] = {}
+        map._byrow[0][1] = 'aa'
+        map._bycol[1][0] = 'aa'
+
+        marker = object()
+        self.assertEqual(map.queryCell(0, 1), 'aa')
+        self.assertEqual(map.queryCell(1, 0), None)
+        self.assertEqual(map.queryCell(1, 0, marker), marker)
+
+    def test_getCell(self):
+        map = self._getSecurityMap()
+        map._byrow[0] = {}
+        map._bycol[1] = {}
+        map._byrow[0][1] = 'aa'
+        map._bycol[1][0] = 'aa'
+
+        self.assertEqual(map.getCell(0, 1), 'aa')
+        self.assertRaises(KeyError, map.getCell, 1, 0)
+
+    def test_getRow(self):
+        map = self._getSecurityMap()
+        map._byrow[0] = {}
+        map._byrow[0][1] = 'ab'
+        map._byrow[0][2] = 'ac'
+        map._byrow[1] = {}
+        map._byrow[1][1] = 'bb'
+        map._bycol[1] = {}
+        map._bycol[1][0] = 'ab'
+        map._bycol[1][1] = 'bb'
+        map._bycol[2] = {}
+        map._bycol[2][0] = 'ac'
+
+        self.assertEqual(map.getRow(0), [(1, 'ab'), (2, 'ac')])
+        self.assertEqual(map.getRow(1), [(1, 'bb')])
+        self.assertEqual(map.getRow(2), [])
+
+    def test_getCol(self):
+        map = self._getSecurityMap()
+        map._byrow[0] = {}
+        map._byrow[0][1] = 'ab'
+        map._byrow[0][2] = 'ac'
+        map._byrow[1] = {}
+        map._byrow[1][1] = 'bb'
+        map._bycol[1] = {}
+        map._bycol[1][0] = 'ab'
+        map._bycol[1][1] = 'bb'
+        map._bycol[2] = {}
+        map._bycol[2][0] = 'ac'
+
+        self.assertEqual(map.getCol(1), [(0, 'ab'), (1, 'bb')])
+        self.assertEqual(map.getCol(2), [(0, 'ac')])
+        self.assertEqual(map.getCol(0), [])
+
+    def test_getAllCells(self):
+        map = self._getSecurityMap()
+        map._byrow[0] = {}
+        map._byrow[0][1] = 'ab'
+        map._byrow[0][2] = 'ac'
+        map._byrow[1] = {}
+        map._byrow[1][1] = 'bb'
+        map._bycol[1] = {}
+        map._bycol[1][0] = 'ab'
+        map._bycol[1][1] = 'bb'
+        map._bycol[2] = {}
+        map._bycol[2][0] = 'ac'
+
+        self.assertEqual(map.getCol(1), [(0, 'ab'), (1, 'bb')])
+        self.assertEqual(map.getCol(2), [(0, 'ac')])
+        self.assertEqual(map.getCol(0), [])
+
+
+class TestPersistentSecurityMap(TestSecurityMap):
+
+    def _getSecurityMap(self):
+        return PersistentSecurityMap()
+
 def test_suite():
     return unittest.TestSuite((
         unittest.makeSuite(TestSecurityMap),

Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/tests/test_zopepolicy.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/tests/test_zopepolicy.py	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/tests/test_zopepolicy.py	2004-07-23 18:42:15 UTC (rev 26714)
@@ -1,4 +1,4 @@
-##############################################################################
+#############################################################################
 #
 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
 # All Rights Reserved.
@@ -11,364 +11,60 @@
 # 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:
-    def __init__(self, principal, interaction=None):
-        self.principal = principal
-        self.interaction = interaction
+def tearDown():
+    placelesssetup.tearDown()
 
-class Interaction:
-    def __init__(self, user):
-        self.participations = [RequestStub(user, self)]
-
-class Unprotected:
-    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 testCheckerPublic(self):
-        from zope.security.checker import CheckerPublic
-        self.failUnless(self.interaction.checkPermission(CheckerPublic, None))
-
-    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:
-    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/branches/ZopeX3-3.0/src/zope/app/securitypolicy/zopepolicy.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/zopepolicy.py	2004-07-23 18:31:57 UTC (rev 26713)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/zopepolicy.py	2004-07-23 18:42:15 UTC (rev 26714)
@@ -15,249 +15,268 @@
 
 $Id$
 """
-from zope.security.checker import CheckerPublic
+
+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):
-        if permission is CheckerPublic:
-            return True
-        # 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
-

Copied: Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/zopepolicy.txt (from rev 26668, Zope3/trunk/src/zope/app/securitypolicy/zopepolicy.txt)


Property changes on: Zope3/branches/ZopeX3-3.0/src/zope/app/securitypolicy/zopepolicy.txt
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the Zope3-Checkins mailing list