[Zope-Checkins] CVS: Zope3/lib/python/Zope/App/Security - ISecurityManager.py:1.1.2.6.6.1 ISecurityPolicy.py:1.1.2.3.6.1 SecurityManagement.py:1.1.2.5.6.1 SecurityManager.py:1.1.2.8.6.1 SimpleSecurityPolicies.py:1.1.2.4.6.1 ZopeSecurityPolicy.py:1.1.2.24.4.1 protectClass.py:1.1.2.12.6.1 publicClass.py:1.1.2.8.6.1 security.zcml:1.1.2.4.2.1

Jim Fulton jim@zope.com
Fri, 26 Apr 2002 14:23:18 -0400


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

Modified Files:
      Tag: SecurityProxy-branch
	ISecurityManager.py ISecurityPolicy.py SecurityManagement.py 
	SecurityManager.py SimpleSecurityPolicies.py 
	ZopeSecurityPolicy.py protectClass.py publicClass.py 
	security.zcml 
Log Message:
Changed security code to use security proxies and name-based
security. This has pretty far-reaching implications:

- You now protect names/operations, *not* values. This means it's as
  easy yo protect data attributes that have simple values as it is to
  protect methods.

- There is no longer a __permissions__ attribute. :)

- There is no longer a validate method in either security managers or
  policies. 

- No more need to have a special compiler for restricted code.
  In exchange, lots of objects are proxies and code sometimes needs to
  be prepared to remove proxies.

In addition:

- Basic objects (None, strings, numbers, etc.) are not wrapped in
  context wrappers.

- There is a test that fails unless Python 2.3 is used.



=== Zope3/lib/python/Zope/App/Security/ISecurityManager.py 1.1.2.6 => 1.1.2.6.6.1 ===
         executable context and policies.
     """
+
     def getPrincipal():
         """
             Return the authenticated principal. 
@@ -27,25 +28,6 @@
             REQUEST['AUTHENTICATED_USER']
 
             but is a bit cleaner, especially if 'REQUEST' isn't handy.
-        """
-
-    def validate( name, value ):
-        """
-            Validate access, raising Unauthorized if not allowed..
-
-            Arguments:
-
-            name -- The attribute name used to access the value
-
-            value -- The value retrieved though the access.
-        """
-
-    def validateValue( value ):
-        """
-            Validate access, raising Unauthorized if not allowed..
-            
-            This is a shortcut for the common case of
-            validating a value without providing access information.
         """
 
     def checkPermission( permission, object ):


=== Zope3/lib/python/Zope/App/Security/ISecurityPolicy.py 1.1.2.3 => 1.1.2.3.6.1 ===
 
 class ISecurityPolicy( Interface ):
-        
-    def validate( name
-                , value
-                , context
-                ):
-        """
-            Validate access, raising Unauthorized if not allowed.
-
-            Arguments:
-
-            name -- The name used to access the value
-
-            value -- The value returned by the access
-
-            context -- must implement ISecurityContext; access to information
-                       such as the context stack and AUTHENTICATED_USER.
-        """
 
     def checkPermission( permission
                        , object


=== Zope3/lib/python/Zope/App/Security/SecurityManagement.py 1.1.2.5 => 1.1.2.5.6.1 ===
 _managers={}
 
+from Zope.Testing.CleanUp import addCleanUp
+addCleanUp(_managers.clear)
+
+
 #
 #   ISecurityManagementSetup implementation
 #


=== Zope3/lib/python/Zope/App/Security/SecurityManager.py 1.1.2.8 => 1.1.2.8.6.1 ===
 _defaultPolicy = ParanoidSecurityPolicy()
 
+def _clear():
+    global _defaultPolicy
+    _defaultPolicy = ParanoidSecurityPolicy()
+
+from Zope.Testing.CleanUp import addCleanUp
+addCleanUp(_clear)
+
+
 def setSecurityPolicy( aSecurityPolicy ):
     """
         Set the system default security policy. 
@@ -69,31 +77,6 @@
             but is a bit cleaner, especially if 'REQUEST' isn't handy.
         """
         return self._context.user
-
-    def validate( self, name, value ):
-        """
-            Validate access.
-
-            Arguments:
-
-            name -- The attribute name used to access the value
-
-            value -- The value retrieved though the access.
-
-            A boolean value is returned indicating whether the value is
-            accessible. An Unauthorized exception may be raised in some
-            cases.
-        """
-        return self._getPolicy().validate( name, value, self._context )
-
-    def validateValue( self, value ):
-        """
-            Validate access. This is a shortcut for the common case of
-            validating a value without providing access information.
-
-            Raise Unauthorized if access not allowed.
-        """
-        return self._getPolicy().validate( None, value, self._context )
 
     def checkPermission( self, permission, object ):
         """


=== Zope3/lib/python/Zope/App/Security/SimpleSecurityPolicies.py 1.1.2.4 => 1.1.2.4.6.1 ===
     """
     __implements__ = ISecurityPolicy
-        
-    def validate( self, name, value, context ):
-        raise Unauthorized(name=name, value=value)
 
     def checkPermission( sel, permission, object, context ):
+        if (context.user is None   # no user
+            and not context.stack  # no untrusted code
+            ):
+            return 1 # Nobody not to trust!
+        
         return 0
 
 class PermissiveSecurityPolicy:
@@ -33,30 +35,7 @@
         Allow all access
     """
     __implements__ = ISecurityPolicy
-        
-    def validate( self, name, value, context ):
-        pass
 
     def checkPermission( self, permission, object, context ):
-        return 1
-
-class NameBasedSecurityPolicy:
-    """ Allow access based on traversed name.
-
-    The constructor takes a callable object, which is passed the name of the
-    item accessed, or None. This method should either pass or raise
-    Zope.Exceptions.Unauthorized.
-
-    """
-
-    __implements__ = ISecurityPolicy
-
-    def __init__(self, nameChecker):
-        self._nameChecker = nameChecker
-
-    def validate(self, name, value, context):
-        self._nameChecker(name)
-
-    def checkPermission(self, permission, object, context):
         return 1
 


=== Zope3/lib/python/Zope/App/Security/ZopeSecurityPolicy.py 1.1.2.24 => 1.1.2.24.4.1 ===
 
 from Zope.ComponentArchitecture import getAdapter
-from Zope.ContextWrapper.ContainmentIterator import ContainmentIterator
-
+from Zope.Proxy.ContextWrapper import ContainmentIterator
 from Zope.Exceptions import Unauthorized, Forbidden
-
 from Zope.App.Security.IRolePermissionManager import IRolePermissionManager
 from Zope.App.Security.IPrincipalPermissionManager \
     import IPrincipalPermissionManager
@@ -39,9 +37,6 @@
 
 from types import StringType, StringTypes, TupleType, ListType, IntType, MethodType, NoneType
 
-# XXX: hack alert
-from Zope.ContextWrapper import getbaseobject
-
 getPermissionsForPrincipal = \
                 principalPermissionManager.getPermissionsForPrincipal
 getPermissionsForRole      = rolePermissionManager.getPermissionsForRole
@@ -75,48 +70,6 @@
         self._ownerous=ownerous
         self._authenticated=authenticated
 
-    #
-    #   ISecurityPolicy implementation.
-    #
-    def validate( self
-                , name
-                , value
-                , context
-                ):
-
-        try: permission=value.__permission__
-        except AttributeError:
-            # XXX: allow some simple types to get ZMI working
-            #      is this the right thing to do?
-            #      Also, respect the attribute
-            #      __allow_access_to_unprotected_subobjects__
-            #      as used in TALES iterators and other places
-            #      Also, respect __aatus__ for accessing methods of objects
-            #      (un)protected by it.
-            #      This is still a hack, and still incomplete, and just here
-            #      to get the ZMI working.
-            
-            
-            unwrapped_value = getbaseobject(value)
-            if (isinstance(unwrapped_value, (ListType, TupleType, StringTypes, IntType, NoneType))
-                or 
-                getattr(value,'__allow_access_to_unprotected_subobjects__',0)):
-                permission = 'Zope.Public'
-            elif (isinstance(unwrapped_value, MethodType) and 
-                  getattr(value.im_self,
-                          '__allow_access_to_unprotected_subobjects__',
-                          0)):
-                permission = 'Zope.Public'
-            else:
-                raise Forbidden(name, value, 'No permission set')
-
-        if permission == 'Zope.Public': # XXX need unit test for this
-            return
-
-        if self.checkPermission(permission, value, context):
-            return
-        raise Unauthorized(permission, name, value)
-
     def checkPermission( self, permission, object, context ):
         # XXX We aren't really handling multiple principals yet
 
@@ -191,51 +144,6 @@
                 return 1 # Allow on global role
 
         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
-
-    #
-    #   Helper methods
-    #
-    def _allowName( self, name ):
-        """
-            Is 'name' ever allowed to be retrieved under this policy?
-        """
-        return name and isinstance(name, StringType) and name.strip()
-
-    def _findPermission( self, value ):
-        """
-            Find the permission which guards the accessed object.
-        """
-        return getattr( value, '__permission__', None )
-    
-    def _listRolesFor( self, permission, object ):
-        """
-            Crawl the context of 'object' and return an accumulated
-            list of all roles which have 'permission'.
-
-            Note that we don't (yet) use 'object' to search for
-            placeful permission-role bindings.
-        """
-        role_set = {}
-
-        # Add "global" roles for permission here
-        for role in getRolesForPermission( permission ):
-            role_set[ role ] = 1
-
-        roles = role_set.keys()
-        roles.sort()
-
-        return tuple( roles )
 
 zopeSecurityPolicy=ZopeSecurityPolicy()
 


=== Zope3/lib/python/Zope/App/Security/protectClass.py 1.1.2.12 => 1.1.2.12.6.1 ===
 # 
 ##############################################################################
-"""Create the protection declarations object.
-
-Protection declarations are assert as part of the configuration process to map
-permissions to objects, ie to specific classes, class instances, and methods.
-\(Method permissions apply to both the class and class instance manifestations
-of the methods.)
-
-When the declarations are expressed as a simple (ie, empty) tag, they are
-applied to both the class and the instances of the class.  When the tags have
-subtags, then the instances are not inherently affected - the subtag directive
-"instances" can then be used.  [XXX this needs to be fleshed out.]
-
-Invalid protection declarations raise ProtectionDeclarationException
-instances."""
+"""Make assertions about permissions needed to access class instances attributes
+"""
 
 from Interface.Method import Method
 from Exceptions import UndefinedPermissionError
@@ -33,25 +21,25 @@
 from Zope.Configuration.ConfigurationDirectiveInterfaces \
      import INonEmptyDirective
 
+from Zope.Security.Checker import defineChecker, getCheckerForInstancesOf
+from Zope.Security.Checker import Checker, CheckerPublic
+
 class ProtectionDeclarationException(Exception):
     """Security-protection-specific exceptions."""
     pass
 
 
-
 class protectClass:
 
     __class_implements__ = INonEmptyDirective    
     
     def __init__(self, _context, name, permission_id=None, interface=None,
-                 methods=None):
+                 names=None):
         self.__class = _context.resolve(name)
         self.__name = name
         self.__permission_id = permission_id
         self.__context = _context
-        self.__r = self.protect(_context, permission_id, interface, methods)
-        # So subsequent simple-declaration-style self() calls process instances
-        self.__empty = 1
+        self.__r = self.protect(_context, permission_id, interface, names)
 
     # ._getPermission() is handy for subclassing with different permission
     # policy, eg publicClass.
@@ -68,12 +56,12 @@
             return permission_id
 
     def protect(self, _context, permission_id=None, interface=None,
-                methods=None):
+                names=None):
         "Protect a specific aspect"
 
         self.__empty = 0
 
-        if not (interface or methods):
+        if not (interface or names):
             return []
         permission_id = self._getPermission(permission_id)
 
@@ -81,52 +69,31 @@
 
         if interface:
             self.__protectByInterface(interface, permission_id, r)
-        if methods:
-            self.__protectMethods(methods, permission_id, r)
-
-        return r
+        if names:
+            self.__protectNames(names, permission_id, r)
 
-    def instances(self, _context, permission_id=None):
-        "Protect instances of the class, as opposed to methods"
-        self.__empty = 0
-
-        permission_id = self._getPermission(permission_id)
-        r=[]
-        self.__instances(permission_id, r)
         return r
 
-    def __instances(self, permission_id, r):
-        "Protect instances of the class, as opposed to methods"
-        permission_id = self._getPermission(permission_id)
-        r.append((
-            ('protectInstances', self.__class),
-            protectInstancesOfClass, (self.__class, permission_id,)))
-
-    def __protectMethod(self, method, permission_id, r):
-        "Set a permission on a particular method."
+    def __protectName(self, name, permission_id, r):
+        "Set a permission on a particular name."
         r.append((
-            ('protectMethod', self.__class, method),
-            protectMethod, (self.__class, method, permission_id)))
-
-    def __protectMethods(self, methods, permission_id, r):
-        "Set a permission on a bunch of methods."
-        for method in methods.split(","):
-            self.__protectMethod(method.strip(), permission_id, r)
+            ('protectName', self.__class, name),
+            protectName, (self.__class, name, permission_id)))
 
+    def __protectNames(self, names, permission_id, r):
+        "Set a permission on a bunch of names."
+        for name in names.split(","):
+            self.__protectName(name.strip(), permission_id, r)
 
     def __protectByInterface(self, interface, permission_id, r):
-        "Set a permission on methods in an interface."
+        "Set a permission on names in an interface."
         interface = self.__context.resolve(interface)
         for n, d in interface.namesAndDescriptions(1):
-            if isinstance(d, Method):
-                self.__protectMethod(n, permission_id, r)
+            self.__protectName(n, permission_id, r)
 
     def __call__(self):
         "Handle empty/simple declaration."
-        r = self.__r
-        if self.__empty:
-            self.__instances(self.__permission_id, r)
-        return r
+        return self.__r
 
 def _checkPermission(permission_id):
     """Check to make sure that the permission is valid.
@@ -135,58 +102,17 @@
     if not permissionRegistry.definedPermission(permission_id):
         raise UndefinedPermissionError(permission_id)
 
-
-def protectInstancesOfClass(class_, permission_id):
-    _checkPermission(permission_id)
-    class_.__permission__ = permission_id
-
-def protectMethod(class_, method, permission_id):
-    "Set a permission on a particular method."
-    _checkPermission(permission_id)
-
-    m = getattr(class_, method)
-
-    d = class_.__dict__
-    if not d.has_key(method):
-        # Hm, we inherit the method. Dang, we need to insert a stub
-
-        # Make sure we have new style class:
-        if not issubclass(class_, object):
-            raise TypeError(
-                "Can only protected inherited methods of new "
-                "style classes", class_)
-
-        f = Protected(class_, method, permission_id)
-        setattr(class_, method, f)
-        return
-        
-    try:
-        setattr(m, "__permission__", permission_id)
-    except (AttributeError, TypeError):
-        if hasattr(m, "im_func"):
-            setattr(m.im_func, "__permission__", permission_id)
-        else:
-            raise ProtectionDeclarationException(
-                "Couldn't assign permission to method %s of class %s"
-                % (m, class_.__name__))
-
-# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Hack!!!!!!!!!!
-
-class Protected(object):
-
-    def __init__(self, *args):
-        self.__args = args # class_, method, permission_id
-
-    def __get__(self, instance, type=None):
-        return ProtectedMethod(self.__args, instance)
-
+def protectName(class_, name, permission_id):
+    "Set a permission on a particular name."
+    checker = getCheckerForInstancesOf(class_)
+    if checker is None:
+        checker = Checker({}.get)
+        defineChecker(class_, checker)
+
+    # OK, so it's a hack.
+    names = checker.getPermission_func().__self__
+    if permission_id == 'Zope.Public':
+        # Translate public permission to CheckerPublic
+        permission_id = CheckerPublic
     
-class ProtectedMethod(object):
-
-    def __init__(self, args, instance):
-        class_, method, self.__permission__ = args
-        m = getattr(super(class_, instance), method)
-        self.__m = self.__call__ = m
-
-    def __call__(self, *args, **kw):
-        return self.__m(*args, **kw)
+    names[name] = permission_id


=== Zope3/lib/python/Zope/App/Security/publicClass.py 1.1.2.8 => 1.1.2.8.6.1 ===
 
     def __init__(self, _context, name, permission_id=None, interface=None,
-                 methods=None):
+                 names=None):
         self._getPermission(permission_id) # Prohibit explicit permission!
         protectClass.__init__(self, _context, name,
                               permission_id=PublicPermission,
-                              interface=interface, methods=methods)
+                              interface=interface, names=names)
 
     def _getPermission(self, permission_id=None):
         if permission_id not in [None, PublicPermission]:


=== Zope3/lib/python/Zope/App/Security/security.zcml 1.1.2.4 => 1.1.2.4.2.1 ===
 <security:protectClass name="Zope.App.Security.RolePermissionView."
    permission_id="Zope.Security"
-   methods="index, roles, permissions, permissionRoles, action,
+   names="index, roles, permissions, permissionRoles, action,
    manage_permissionForm, update_permission,
    manage_roleForm, update_role, permissionForID" />
 
 <security:protectClass
    name="Zope.App.Security.RolePermissionView.PermissionRoles."
    permission_id="Zope.Security"
-   methods="roles, rolesInfo"
+   names="roles, rolesInfo"
    interface="Zope.App.Security.IRegisteredObject." />
 
 
@@ -55,7 +55,7 @@
   
 <security:protectClass name="Zope.App.Security.PrincipalPermissionView."
    permission_id="Zope.Security"
-   methods="index, get_principal, unsetPermissions, denyPermissions,
+   names="index, get_principal, unsetPermissions, denyPermissions,
    grantPermissions, getUnsetPermissionsForPrincipal,
    getPermissionsForPrincipal" />