[Zope-Checkins] CVS: Zope3/lib/python/Zope/App/Security - AttributePrincipalPermissionManager.py:1.1.2.1 AttributePrincipalRoleManager.py:1.1.2.1 IAttributePrincipalPermissionManageable.py:1.1.2.1 IPrincipalPermissionManageable.py:1.1.2.1 IPrincipalRoleManager.py:1.1.2.4 IPrincipalRoleMap.py:1.1.2.3 PrincipalPermissionManager.py:1.1.2.7 PrincipalRoleManager.py:1.1.2.4 ZopeSecurityPolicy.py:1.1.2.11

Casey Duncan casey_duncan@yahoo.com
Sat, 9 Feb 2002 13:44:50 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/App/Security
In directory cvs.zope.org:/tmp/cvs-serv17794

Modified Files:
      Tag: Zope-3x-branch
	IPrincipalRoleManager.py IPrincipalRoleMap.py 
	PrincipalPermissionManager.py PrincipalRoleManager.py 
	ZopeSecurityPolicy.py 
Added Files:
      Tag: Zope-3x-branch
	AttributePrincipalPermissionManager.py 
	AttributePrincipalRoleManager.py 
	IAttributePrincipalPermissionManageable.py 
	IPrincipalPermissionManageable.py 
Log Message:
New Security mechanism. We now have Principals, Permissions, and Roles. 
You can explicitly Allow and Deny Permissions and Assign and Remove 
roles. You can also Unset roles or permissions, which means "find it 
from some other path" (e.g. context). This is equivalent to Zope2's 
"Acquire Permissions".

Principal/Permission works playfully, Principal/Roles work playfully, 
we need to update the RolePermissions code to support Allow/Deny
(Permissions).

There's not enough test cases for the SecurityPolicy yet, as we need
the RolePermission stuff updated first.


=== Added File Zope3/lib/python/Zope/App/Security/AttributePrincipalPermissionManager.py ===
# AttributePrincipalPermissionManager.py
#
# Copyright (c) 2001 Zope Coporation and Contributors.  All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 1.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.

"""Mappings between principals and permissions, stored in an object locally."""

from Zope.App.Security.IPrincipalPermissionManager \
     import IPrincipalPermissionManager
from Zope.App.Security.LocalSecurityMap import LocalSecurityMap
from Zope.App.Security.Settings import Allow, Deny, Unset

class AttributePrincipalPermissionManager:
    """Mappings between principals and permissions."""

    __implements__ = IPrincipalPermissionManager

    def __init__(self, context):
        self._context = context

    def grantPermissionToPrincipal( self, permission, principal ):
        ''' See the interface IPrincipalPermissionManager '''
        pp = self._getPrincipalPermissions(create=1)
        pp.addCell( permission, principal, Allow )
        self._context._p_changed = 1

    def denyPermissionToPrincipal( self, permission, principal ):
        ''' See the interface IPrincipalPermissionManager '''
        pp = self._getPrincipalPermissions(create=1)
        pp.addCell( permission, principal, Deny )
        self._context._p_changed = 1

    def unsetPermissionForPrincipal( self, permission, principal ):
        ''' See the interface IPrincipalPermissionManager '''
        pp = self._getPrincipalPermissions()
        # Only unset if there is a security map, otherwise, we're done
        if pp:
            pp.delCell( permission, principal )
            self._context._p_changed = 1

    def getPrincipalsForPermission( self, permission ):
        ''' See the interface IPrincipalPermissionManager '''
        pp = self._getPrincipalPermissions()
        if pp: 
            return pp.getRow( permission )
        return []

    def getPermissionsForPrincipal( self, principal ):
        ''' See the interface IPrincipalPermissionManager '''
        pp = self._getPrincipalPermissions()
        if pp: 
            return pp.getCol( principal )
        return []

    def getSetting( self, permission, principal ):
        ''' See the interface IPrincipalPermissionManager '''
        pp = self._getPrincipalPermissions()
        if pp: 
            return pp.getCell( permission, principal, default=Unset )
        return []

    # Implementation helpers

    def _getPrincipalPermissions(self, create=0):
        """ Get the principal permission map stored in the context, optionally
            creating one if necessary """
        try:
            return self._context.__principal_permissions__
        except AttributeError:
            if create:
                pp = self._context.__principal_permissions__ = \
                    LocalSecurityMap()
                return pp
        return None


=== Added File Zope3/lib/python/Zope/App/Security/AttributePrincipalRoleManager.py ===
# AttributePrincipalRoleManager.py
#
# Copyright (c) 2001 Zope Coporation and Contributors.  All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 1.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.

"""Mappings between principals and roles, stored in an object locally."""

from Zope.App.Security.IPrincipalRoleManager \
     import IPrincipalRoleManager
from Zope.App.Security.LocalSecurityMap import LocalSecurityMap
from Zope.App.Security.Settings import Assign, Remove, Unset

class AttributePrincipalRoleManager:
    """Mappings between principals and roles."""

    __implements__ = IPrincipalRoleManager

    def __init__(self, context):
        self._context = context

    def assignRoleToPrincipal( self, role, principal ):
        ''' See the interface IPrincipalRoleManager '''
        pp = self._getPrincipalRoles(create=1)
        pp.addCell( role, principal, Assign )
        self._context._p_changed = 1

    def removeRoleFromPrincipal( self, role, principal ):
        ''' See the interface IPrincipalRoleManager '''
        pp = self._getPrincipalRoles(create=1)
        pp.addCell( role, principal, Remove )
        self._context._p_changed = 1

    def unsetRoleForPrincipal( self, role, principal ):
        ''' See the interface IPrincipalRoleManager '''
        pp = self._getPrincipalRoles()
        # Only unset if there is a security map, otherwise, we're done
        if pp:
            pp.delCell( role, principal )
            self._context._p_changed = 1

    def getPrincipalsForRole( self, role ):
        ''' See the interface IPrincipalRoleManager '''
        pp = self._getPrincipalRoles()
        if pp: 
            return pp.getRow( role )
        return []

    def getRolesForPrincipal( self, principal ):
        ''' See the interface IPrincipalRoleManager '''
        pp = self._getPrincipalRoles()
        if pp: 
            return pp.getCol( principal )
        return []

    def getSetting( self, role, principal ):
        ''' See the interface IPrincipalRoleManager '''
        pp = self._getPrincipalRoles()
        if pp: 
            return pp.getCell( role, principal, default=Unset )
        return Unset

    # Implementation helpers

    def _getPrincipalRoles(self, create=0):
        """ Get the principal role map stored in the context, optionally
            creating one if necessary """
        try:
            return self._context.__principal_roles__
        except AttributeError:
            if create:
                pp = self._context.__principal_roles__ = \
                    LocalSecurityMap()
                return pp
        return None


=== Added File Zope3/lib/python/Zope/App/Security/IAttributePrincipalPermissionManageable.py ===
##############################################################################
#
# Copyright (c) 2001 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
# 
##############################################################################
"""

Revision information: $Id: IAttributePrincipalPermissionManageable.py,v 1.1.2.1 2002/02/09 18:44:49 caseman Exp $
"""

from Persistence.IPersistent import IPersistent

class IAttributePrincipalPermissionManageable(IPersistent):

    """The object reserves the attribute __principal_permissions__ for use
    by implementations of IPrincipalPermissionManager"""



=== Added File Zope3/lib/python/Zope/App/Security/IPrincipalPermissionManageable.py ===
# IPrincipalPermissionManager.py
#
# Copyright (c) 2001 Zope Coporation and Contributors.  All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 1.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.

"""Management interface for mappings between principals and permissions."""

from Zope.App.Security.IPrincipalPermissionMap import IPrincipalPermissionMap


class IPrincipalPermissionManager(IPrincipalPermissionMap):
    """Management interface for mappings between principals and permissions."""

    def grantPermissionToPrincipal(permission, principal):
        """Assert that the permission is allowed for the principal.

        permission must be an IPermission
        principal must be an IPrincipal
        """

    def denyPermissionToPrincipal(permission, principal):
        """Assert that the permission is denied to the principal.

        permission must be an IPermission
        principal must be an IPrincipal
        """

    def unsetPermissionForPrincipal(permission, principal):
        """Remove the permission (either denied or allowed) from the
        principal.

        permission must be an IPermission
        principal must be an IPrincipal
        """
        
    def getSetting(permission, principal):
        """Get the setting (Allow/Deny/Unset) for a given permission
        and principal.

        permission must be an IPermission
        principal must be an IPrincipal
        """


=== Zope3/lib/python/Zope/App/Security/IPrincipalRoleManager.py 1.1.2.3 => 1.1.2.4 ===
         principal must be an IPrincipal
         """
+
+    def removeRoleFromPrincipal(role, principal):
+        """ remove a role from the principal """
+
+    def unsetRoleForPrincipal(role, principal):
+        """ unset the role for the principal 
+
+        role must be an IRole
+        principal must be an IPrincipal
+        """ 


=== Zope3/lib/python/Zope/App/Security/IPrincipalRoleMap.py 1.1.2.2 => 1.1.2.3 ===
 
     def getPrincipalsForRole(role):
-        """Return the list of (principal, setting) who have been assigned or removed from a role.
+        """Return the list of (principal, setting) who have been assigned or 
+        removed from a role.
 
         role must be an IRole.  If no principals have been assigned this role,
         then the empty list is returned.
         """
 
     def getRolesForPrincipal(principal):
-        """Return the list of (role, setting) assigned or removed from this principal.
+        """Return the list of (role, setting) assigned or removed from 
+        this principal.
 
         principal must be an IPrincipal.  If no roles have been assigned to
         this principal, then the empty list is returned.


=== Zope3/lib/python/Zope/App/Security/PrincipalPermissionManager.py 1.1.2.6 => 1.1.2.7 ===
 from Zope.App.Security.IPrincipalPermissionManager \
      import IPrincipalPermissionManager
-from Zope.App.Security.SecurityMap import SecurityMap
+from Zope.App.Security.LocalSecurityMap import LocalSecurityMap
+from Zope.App.Security.Settings import Allow, Deny, Unset
 
 
-class PrincipalPermissionManager(SecurityMap):
+class PrincipalPermissionManager(LocalSecurityMap):
     """Mappings between principals and permissions."""
 
     __implements__ = IPrincipalPermissionManager
 
-    """Bind the permission to the principal.
-
-    permission must be an IPermission
-    principal must be an IPrincipal
-    """
     def grantPermissionToPrincipal( self, permission, principal ):
-        self.addCell( permission, principal )
+        ''' See the interface IPrincipalPermissionManager '''
+        self.addCell( permission, principal, Allow )
 
-    """Return the list of principals for the given permission.
+    def denyPermissionToPrincipal( self, permission, principal ):
+        ''' See the interface IPrincipalPermissionManager '''
+        self.addCell( permission, principal, Deny )
+
+    def unsetPermissionForPrincipal( self, permission, principal ):
+        ''' See the interface IPrincipalPermissionManager '''
+        self.delCell( permission, principal )
 
-    permission must be an IPermission.  If no principals have been granted
-    this permission, then the empty list is returned.
-    """
     def getPrincipalsForPermission( self, permission ):
-        return self.getColumnsForRow( permission )
-
-    """Return the list of permissions for the given principal.
+        ''' See the interface IPrincipalPermissionManager '''
+        return self.getRow( permission )
 
-    principal must be an IPrincipal.  If no permissions have been granted
-    to this principal, then the empty list is returned.
-    """
     def getPermissionsForPrincipal( self, principal ):
-        return self.getRowsForColumn( principal )
+        ''' See the interface IPrincipalPermissionManager '''
+        return self.getCol( principal )
 
+    def getSetting( self, permission, principal ):
+        ''' See the interface IPrincipalPermissionManager '''
+        return self.getCell( permission, principal, default=Unset )
 
 # Permissions are our rows, and principals are our columns
 principalPermissionManager = PrincipalPermissionManager()


=== Zope3/lib/python/Zope/App/Security/PrincipalRoleManager.py 1.1.2.3 => 1.1.2.4 ===
 """Mappings between principals and roles."""
 
-from Zope.App.Security.SecurityMap import SecurityMap
+from Zope.App.Security.LocalSecurityMap import LocalSecurityMap
+from Zope.App.Security.Settings import Assign, Remove, Unset
+from Zope.App.Security.IPrincipalRoleManager import IPrincipalRoleManager
+from Zope.App.Security.IPrincipalRoleMap import IPrincipalRoleMap
 
 
-class PrincipalRoleManager(SecurityMap):
-    """Mappings between principals roles."""
 
-    """Assign the role to the principal.
+class PrincipalRoleManager(LocalSecurityMap):
+    """Mappings between principals and roles."""
+
+    __implements__ = ( IPrincipalRoleManager, IPrincipalRoleMap )
 
-    role must be an IRole
-    principal must be an IPrincipal
-    """
     def assignRoleToPrincipal( self, role, principal ):
-        self.addCell( role, principal )
+        ''' See the interface IPrincipalRoleManager '''
+        self.addCell( role, principal, Assign )
 
-    """Return the list of principals assigned the given role.
+    def removeRoleFromPrincipal( self, role, principal ):
+        ''' See the interface IPrincipalRoleManager '''
+        self.addCell( role, principal, Remove )
+
+    def unsetRoleForPrincipal( self, role, principal ):
+        ''' See the interface IPrincipalRoleManager '''
+        self.delCell( role, principal )
 
-    role must be an IRole.  If no principals have been assigned
-    this role, then the empty list is returned.
-    """
     def getPrincipalsForRole( self, role ):
-        return self.getColumnsForRow( role )
-
-    """Return the list of roles assigned to the given principal.
+        ''' See the interface IPrincipalRoleMap '''
+        return self.getRow( role )
 
-    principal must be an IPrincipal.  If no roles have been assigned
-    to this principal, then the empty list is returned.
-    """
     def getRolesForPrincipal( self, principal ):
-        return self.getRowsForColumn( principal )
+        ''' See the interface IPrincipalRoleMap '''
+        return self.getCol( principal )
+
+    def getSetting( self, role, principal ):
+        ''' See the interface IPrincipalRoleMap '''
+        return self.getCell( role, principal, default=Unset )
 
 
 # Roles are our rows, and principals are our columns


=== Zope3/lib/python/Zope/App/Security/ZopeSecurityPolicy.py 1.1.2.10 => 1.1.2.11 ===
 
 from Zope.App.Security.IRolePermissionManager import IRolePermissionManager
+from Zope.App.Security.IPrincipalPermissionManager \
+    import IPrincipalPermissionManager
+from Zope.App.Security.IPrincipalRoleManager \
+    import IPrincipalRoleManager
+from Zope.App.Security.IRolePermissionManager import IRolePermissionManager
 from Zope.App.Security.PermissionRegistry import permissionRegistry 
 from Zope.App.Security.PrincipalRegistry import principalRegistry 
 from Zope.App.Security.RoleRegistry import roleRegistry
@@ -30,6 +35,7 @@
      import principalPermissionManager 
 from Zope.App.Security.RolePermissionManager import rolePermissionManager 
 from Zope.App.Security.PrincipalRoleManager import principalRoleManager
+from Zope.App.Security.Settings import Allow, Deny, Assign, Remove
 
 getPermissionsForPrincipal = principalPermissionManager.getPermissionsForPrincipal
 getPermissionsForRole      = rolePermissionManager.getPermissionsForRole
@@ -87,24 +93,45 @@
         
         principals = { context.user : 1 }
         roles      = {}
+        seen_allowed = 0
+        all_roles = self._listAllRoles(object, context)
 
+        # XXX We aren't really handling multiple principals below
         for c in ContainmentIterator(object):
+            ppm = getAdapter(c, IPrincipalPermissionManager, None)
+            if ppm is not None: 
+                for principal in principals.keys():
+                    setting = ppm.getSetting(permission, principal)
+                    if setting is Allow:
+                        seen_allowed = 1
+                    elif setting is Deny:
+                        return 0 # Explicit deny on principal
+                if seen_allowed:
+                    return 1 # If I'm allowed here... forget the rest.
+
             rpm = getAdapter(c, IRolePermissionManager, None)
             if rpm is not None:
-                for role in rpm.getRolesForPermission(permission):
-                    roles[role] = 1
-
-        for p in principals.keys():
-            if permission in getPermissionsForPrincipal(p):
-                del principals[p]
-            else:
-                for r in getRolesForPrincipal(p):
-                    if permission in getPermissionsForRole(r):
-                        del principals[p]
-                    if r in roles:
-                        return 1
+                for role in all_roles:
+                    # XXX: As yet, role permission managers have no concept of
+                    # deny, refactor when this gets implemented 
+                    if permission in rpm.getPermissionsForRole(role):
+                        seen_allowed = 1
+                if seen_allowed:
+                    return 1 # I'm allowed by a role on the principal
+
+        return 0 # Deny by default
+
+#        for p in principals.keys():
+#            if permission in getPermissionsForPrincipal(p):
+#                del principals[p]
+#            else:
+#                for r in getRolesForPrincipal(p):
+#                    if permission in getPermissionsForRole(r):
+#                        del principals[p]
+#                    if r in roles:
+#                        return 1
                 
-        return not principals
+#        return not principals
 
     #
     #   Helper methods
@@ -139,6 +166,29 @@
         roles.sort()
 
         return tuple( roles )
+
+    def _listAllRoles( self, object, context ):
+        """
+            Walk the containment hierarchy of object and accumulate the roles 
+            assigned to the current user in the context.
+        """
+        principals = [context.user]
+        roles = {}
+
+        for c in ContainmentIterator(object):
+            prm = getAdapter(c, IPrincipalRoleManager, None)
+            if prm is not None:
+                for principal in principals:
+                    for role, setting in prm.getRolesForPrincipal(principal):
+                        if not roles.has_key(role):
+                            roles[role] = setting
+
+        result = []
+        for role, setting in roles.items():
+            if setting is Assign:
+                result.append(role)
+
+        return result
 
 zopeSecurityPolicy=ZopeSecurityPolicy()