[Zope3-checkins] CVS: Zope3/src/zope/security - simpleinteraction.py:1.1.2.1 checker.py:1.43.18.2 interfaces.py:1.8.2.2 management.py:1.5.2.1 readme.txt:1.5.12.2 simplepolicies.py:1.6.2.1 context.py:NONE manager.py:NONE

Marius Gedminas marius at pov.lt
Mon Mar 8 13:44:15 EST 2004


Update of /cvs-repository/Zope3/src/zope/security
In directory cvs.zope.org:/tmp/cvs-serv14991/src/zope/security

Modified Files:
      Tag: mgedmin-events2-branch
	checker.py interfaces.py management.py readme.txt 
	simplepolicies.py 
Added Files:
      Tag: mgedmin-events2-branch
	simpleinteraction.py 
Removed Files:
      Tag: mgedmin-events2-branch
	context.py manager.py 
Log Message:
Replaced security managers and security contexts with interactions.  There is
at most one active interaction per thread, accessible with getInteraction().
Code that used getSecurityManager to get the authenticated principal should now
use the current interaction.  Note that the interaction contains an iterable of
principals instead of just one principal.  Code that used a security manager to
implement user logins should now use newInteraction/ endInteraction pair.  Code
that used a security manager to check whether the authenticated user has a
permission to do something should now ask the security policy directly (there's
a new global function getSecurityPolicy).

Interactions are tied with security policies: ISecurityPolicy has a method
used to create a new interaction for a given request.  This is not necessarily
the best idea, perhaps a global hook (setInteractionFactory) would be better.

Things not done yet:
 - Not all places in the code are ready to cope with more than one principal.
 - The README in zope.security and the sandbox security example need to be
   updated.
 - There was an idea of using a notification method in IInteraction that would
   let it customize the handling of local authentication during traversal.
   It could be e.g. afterLocalAuthentication(old_principal, new_principal, site)
   Currently the ZopePublication code just does
     interaction.remove(old_principal)
     interaction.add(new_principal)
   when request.user is changed during traversal.
 - The interaction API could be polished a bit (perhaps the request argument
   to newInteraction should be optional, perhaps there should be an alternative
   principals argument to newInteraction, perhaps endInteraction should not
   raise an exception when it is called outside of an active interaction).
 - It is not clearly cut when security checks should use the global interaction
   and when they should use the interaction of the security proxy.  Perhaps
   use the global one if interaction stored in the proxy is None?
 - It is not defined explicitly where the interaction argument can safely be
   None (as an argument to ProxyFactory, as an argument to security checkers,
   etc.).
 - Some places that construct security proxies pass None to ProxyFactory.
   Perhaps they should use the current interaction instead.  Or maybe not.





=== Added File Zope3/src/zope/security/simpleinteraction.py ===
##############################################################################
#
# 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.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.
#
##############################################################################
""" Define Zope\'s default interaction class

$Id: simpleinteraction.py,v 1.1.2.1 2004/03/08 18:43:44 mgedmin Exp $
"""

import sets

from zope.interface import implements
from zope.security.interfaces import IInteraction

__metaclass__ = type


class Interaction:
    implements(IInteraction)

    def __init__(self):
        self.principals = []

    def add(self, principal):
        self.principals.append(principal)

    def remove(self, principal):
        self.principals.remove(principal)


def createInteraction(request):
    """A helper for implementing ISecurityPolicy.createInteraction"""
    interaction = Interaction()
    if request is not None:
        interaction.add(request.user)
    return interaction



=== Zope3/src/zope/security/checker.py 1.43.18.1 => 1.43.18.2 ===
--- Zope3/src/zope/security/checker.py:1.43.18.1	Mon Feb 23 14:18:06 2004
+++ Zope3/src/zope/security/checker.py	Mon Mar  8 13:43:44 2004
@@ -36,7 +36,7 @@
 from zope.interface.declarations import Declaration
 from zope.security.interfaces import IChecker, INameBasedChecker
 from zope.security.interfaces import ISecurityProxyFactory
-from zope.security.management import getSecurityManager
+from zope.security.management import getSecurityPolicy
 from zope.security._proxy import _Proxy as Proxy, getChecker, getInteraction
 from zope.exceptions import Unauthorized, ForbiddenAttribute, DuplicationError
 
@@ -148,8 +148,8 @@
         if permission is not None:
             if permission is CheckerPublic:
                 return # Public
-            manager = getSecurityManager()
-            if manager.checkPermission(permission, object):
+            policy = getSecurityPolicy()
+            if policy.checkPermission(permission, object, interaction):
                 return
             else:
                 __traceback_supplement__ = (TracebackSupplement, object)
@@ -164,8 +164,8 @@
         if permission is not None:
             if permission is CheckerPublic:
                 return # Public
-            manager = getSecurityManager()
-            if manager.checkPermission(permission, object):
+            policy = getSecurityPolicy()
+            if policy.checkPermission(permission, object, interaction):
                 return
             else:
                 __traceback_supplement__ = (TracebackSupplement, object)
@@ -308,8 +308,8 @@
         if permission is not None:
             if permission is CheckerPublic:
                 return # Public
-            manager = getSecurityManager()
-            if manager.checkPermission(permission, object):
+            policy = getSecurityPolicy()
+            if policy.checkPermission(permission, object, interaction):
                 return
             else:
                 __traceback_supplement__ = (TracebackSupplement, object)
@@ -325,8 +325,8 @@
         if permission is not None:
             if permission is CheckerPublic:
                 return # Public
-            manager = getSecurityManager()
-            if manager.checkPermission(permission, object):
+            policy = getSecurityPolicy()
+            if policy.checkPermission(permission, object, interaction):
                 return
             else:
                 __traceback_supplement__ = (TracebackSupplement, object)
@@ -342,8 +342,8 @@
         if permission is not None:
             if permission is CheckerPublic:
                 return # Public
-            manager = getSecurityManager()
-            if manager.checkPermission(permission, object):
+            policy = getSecurityPolicy()
+            if policy.checkPermission(permission, object, interaction):
                 return
             else:
                 __traceback_supplement__ = (TracebackSupplement, object)


=== Zope3/src/zope/security/interfaces.py 1.8.2.1 => 1.8.2.2 ===
--- Zope3/src/zope/security/interfaces.py:1.8.2.1	Mon Feb 23 14:18:06 2004
+++ Zope3/src/zope/security/interfaces.py	Mon Mar  8 13:43:44 2004
@@ -17,33 +17,12 @@
 """
 from zope.interface import Interface, Attribute
 
-class ISecurityManagementSetup(Interface):
-    """Methods to manage the security manager.
-
-    Infrastructure (including tests, etc.) calls these things to
-    tweak the security manager.
-    """
-
-    def newSecurityManager(user):
-        """Install a new SecurityManager, using user.
-
-        Return the old SecurityManager, if any, or None.
-        """
-
-    def replaceSecurityManager(old_manager):
-        """Replace the SecurityManager with old_manager.
-
-        old_manager must implement ISecurityManager.
-        """
-
-    def noSecurityManager():
-        """Clear any existing SecurityManager."""
 
 class ISecurityManagement(Interface):
     """Public security management API."""
 
-    def getSecurityManager():
-        """Get a SecurityManager (create if needed)."""
+    def getSecurityPolicy():
+        """Get the system default security policy."""
 
     def setSecurityPolicy(aSecurityPolicy):
         """Set the system default security policy.
@@ -52,6 +31,7 @@
         should never, for example, be called during a web request.
         """
 
+
 class ISecurityProxyFactory(Interface):
 
     def __call__(object, checker=None, interaction=None):
@@ -66,56 +46,6 @@
         XXX maybe interaction should be a required argument
         """
 
-# XXX This interface has too much Zope application dependence. This
-# needs to be refactored somehow.
-
-class ISecurityManager(Interface):
-    """
-        A security manager provides methods for checking access and managing
-        executable context and policies.
-    """
-
-    def getPrincipal():
-        """Return the authenticated principal.
-
-        This is equivalent to something like::
-        REQUEST['AUTHENTICATED_USER']
-        but is a bit cleaner, especially if 'REQUEST' isn't handy.
-
-        An IPrincipal object wrapped in a context of its
-        AuthenticationService is returned.
-        """
-
-    def checkPermission(permission, object):
-        """Return whether security context allows permission on object.
-
-        Arguments:
-        permission -- A permission name
-        object -- The object being accessed according to the permission
-        """
-
-    def pushExecutable(anExecutableObject):
-        """
-            Push an ExecutableObject onto the manager's stack, and
-            activate its custom security policy, if any.
-        """
-
-    def popExecutable(anExecutableObject):
-        """
-            Pop the topmost ExecutableObject from the stack, deactivating
-            any custom security policy it might have installed.
-        """
-
-    def calledByExecutable():
-        """
-            Return a boolean indicating whether the current request has
-            invoked any IExecutableObjects.
-
-            This can be used to determine if an object was called
-            (more or less) directly from a URL, or if it was called by
-            through-the-web provided code.
-        """
-
 
 class IChecker(Interface):
     """Security-proxy plugin objects that implement low-level checks
@@ -169,31 +99,25 @@
 
 class ISecurityPolicy(Interface):
 
-    def checkPermission(permission, object, context):
+    def createInteraction(request):
+        """Creates a new interaction for a given request.
+
+        XXX perhaps this should be a separate interface IInteractionFactory,
+            and the factory registered by calling
+            ISecurityManagement.global setInteractionFactory(factory).
+        """
+
+    def checkPermission(permission, object, interaction):
         """Return whether security context allows permission on object.
 
         Arguments:
         permission -- A permission name
         object -- The object being accessed according to the permission
-        context -- A SecurityContext, which provides access to information
-            such as the context stack and AUTHENTICATED_USER.
+        interaction -- An interaction, which provides access to information
+            such as authenticated principals.
         """
 
 
-class ISecurityContext(Interface):
-    """Capture transient request-specific security information."""
-
-    Attribute('stack',
-              'A stack of elements, each either be an ExecutableObject'
-              'or a tuple consisting of an ExecutableObject and a'
-              'custom SecurityPolicy.'
-            )
-
-    Attribute('user',
-              'The AUTHENTICATED_USER for the request.'
-              )
-
-
 class IInteraction(Interface):
     """A representation of an interaction between some actors and the system.
     """
@@ -205,4 +129,21 @@
 
     def remove(principal):
         """Remove a participant."""
+
+
+class IInteractionManagement(Interface):
+    """Interaction management API."""
+
+    def newInteraction(request):
+        """Start a new interaction."""
+
+    def getInteraction():
+        """Return the current interaction.
+
+        Returns None if called outside newInteraction/endInteraction pair.
+        XXX should it raise an exception instead?
+        """
+
+    def endInteraction():
+        """Finish the current interaction."""
 


=== Zope3/src/zope/security/management.py 1.5 => 1.5.2.1 ===
--- Zope3/src/zope/security/management.py:1.5	Fri Feb 20 15:42:12 2004
+++ Zope3/src/zope/security/management.py	Mon Mar  8 13:43:44 2004
@@ -11,75 +11,41 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Default 'ISecurityManagement' implementation
+"""Default 'ISecurityManagement' and 'IInteractionManagement' implementation
 
 $Id$
 """
+
 # Special system user that has all permissions
-# zope.security.manager needs it
+# zope.security.simplepolicies needs it
 system_user = object()
 
 from zope.interface import moduleProvides
 from zope.security.interfaces import ISecurityManagement
-from zope.security.interfaces import ISecurityManagementSetup
-from zope.security.manager import SecurityManager
-from zope.security.manager import setSecurityPolicy as _setSecurityPolicy
-from zope.security.context import SecurityContext
-
-moduleProvides(ISecurityManagement, ISecurityManagementSetup)
-
-try:
-    import thread
-except:
-    get_ident = lambda: 0
-else:
-    get_ident = thread.get_ident
+from zope.security.interfaces import IInteractionManagement
+from zope.security.simplepolicies import ParanoidSecurityPolicy
+from zope.testing.cleanup import addCleanUp
+from zope.thread import thread_globals
 
-_managers = {}
+moduleProvides(ISecurityManagement, IInteractionManagement)
 
-from zope.testing.cleanup import addCleanUp
-addCleanUp(_managers.clear)
 
-#
-#   ISecurityManagementSetup implementation
-#
-def newSecurityManager(user):
-    """Install a new SecurityManager, using user.
+_defaultPolicy = ParanoidSecurityPolicy()
 
-    Return the old SecurityManager, if any, or None.
-    """
-    return replaceSecurityManager(SecurityManager(SecurityContext(user)))
+def _clear():
+    global _defaultPolicy
+    _defaultPolicy = ParanoidSecurityPolicy()
 
-def replaceSecurityManager(old_manager):
-    """Replace the SecurityManager with 'old_manager', which must
-    implement ISecurityManager.
-    """
+addCleanUp(_clear)
 
-    thread_id = get_ident()
-    old = _managers.get(thread_id, None)
-    _managers[thread_id] = old_manager
-    return old
-
-def noSecurityManager():
-    """Clear any existing SecurityManager."""
-    try:
-        del _managers[get_ident()]
-    except KeyError:
-        pass
 
 #
 #   ISecurityManagement implementation
 #
-def getSecurityManager():
-    """Get a SecurityManager (create if needed)."""
-    thread_id = get_ident()
-    manager = _managers.get(thread_id, None)
-
-    if manager is None:
-        newSecurityManager(None)
-        manager = _managers.get(thread_id, None)
 
-    return manager
+def getSecurityPolicy():
+    """Get the system default security policy."""
+    return _defaultPolicy
 
 def setSecurityPolicy(aSecurityPolicy):
     """Set the system default security policy, and return the previous
@@ -88,4 +54,39 @@
     This method should only be called by system startup code.
     It should never, for example, be called during a web request.
     """
-    return _setSecurityPolicy(aSecurityPolicy)
+    global _defaultPolicy
+
+    last, _defaultPolicy = _defaultPolicy, aSecurityPolicy
+
+    return last
+
+
+#
+#   IInteractionManagement implementation
+#
+
+def getInteraction(_thread=None):
+    """Get the current interaction."""
+    return thread_globals(_thread).interaction
+
+def newInteraction(request, _thread=None, _policy=None):
+    """Start a new interaction."""
+    if getInteraction(_thread) is not None:
+        raise AssertionError("newInteraction called"
+                             " while another interaction is active")
+    interaction = _defaultPolicy.createInteraction(request)
+    thread_globals(_thread).interaction = interaction
+
+def endInteraction(_thread=None):
+    """End the current interaction."""
+    if getInteraction(_thread=_thread) is None:
+        raise AssertionError("endInteraction called"
+                             " without an active interaction")
+    thread_globals(_thread).interaction = None
+
+
+def _cleanUp():
+    thread_globals().interaction = None
+
+addCleanUp(_cleanUp)
+


=== Zope3/src/zope/security/readme.txt 1.5.12.1 => 1.5.12.2 ===
--- Zope3/src/zope/security/readme.txt:1.5.12.1	Mon Feb 23 14:18:06 2004
+++ Zope3/src/zope/security/readme.txt	Mon Mar  8 13:43:44 2004
@@ -28,10 +28,13 @@
     objects.  Attribute names are mapped onto permission names when
     checking access and the implementation of the security check is
     defined by the security policy, which receives the object, the
-    permission name, and a context.
+    permission name, and an interaction.
 
-    Security contexts are containers of transient information such as
-    the current principal and the context stack.
+    Interactions are containers of transient information such as the
+    list of authenticated principals.
+
+    XXX this needs some rewrite since security contexts were replaced
+        with interactions
 
     To explain the concept and usage of the context stack, a little
     background into the design influences of the default Zope policy
@@ -84,20 +87,21 @@
         Provides accessors for setting up security manager and global
         security policy.
 
-      Security Context
+      Interaction
 
-        Stores transient information on the current principal and the
-        context stack.
+        Stores transient information on the current principal etc.
 
       Security Manager
 
         Manages security context (execution stack) and delegates
         permission checks to security policy.
 
+	XXX contexts are going away
+
       Security Policy
 
         Provides a single method that accepts the object, the
-        permission, and the context of the access being checked and is
+        permission, and the interaction of the access being checked and is
         used to implement the application logic for the security
         framework.
 
@@ -162,8 +166,7 @@
         proxy wrappers to automatically check security.
 
       - inserting hooks into the original simulation to register the
-        agents as the active principal within a security manager's
-        context....
+        agents as the active principal within the interaction
 
     Defining Permission Model
 
@@ -228,9 +231,12 @@
 
           __implements__ = ISecurityPolicy
 
-        def checkPermission(self, permission, object, context):
+        # XXX createInteraction
+
+        def checkPermission(self, permission, object, interaction):
 
-            token = context.user.getAuthenticationToken()
+            user = interaction.principals[0] # XXX
+            token = user.getAuthenticationToken()
             home = object.getHome()
             db = getattr(SimulationSecurityDatabase, home.getId(), None)
 
@@ -263,7 +269,7 @@
       methods) is implicitly guarded by our security policy.
 
       The second step is that we want to associate the active agent
-      with the security context so the security policy will know which
+      with the interaction so the security policy will know which
       agent's authentication token to validate against.
 
       The third step is to set our security policy as the default


=== Zope3/src/zope/security/simplepolicies.py 1.6 => 1.6.2.1 ===
--- Zope3/src/zope/security/simplepolicies.py:1.6	Fri Feb 20 15:42:12 2004
+++ Zope3/src/zope/security/simplepolicies.py	Mon Mar  8 13:43:44 2004
@@ -16,28 +16,36 @@
 $Id$
 """
 
+from zope.interface import implements
 from zope.security.interfaces import ISecurityPolicy
 from zope.security.management import system_user
+from zope.security.simpleinteraction import createInteraction \
+                                            as _createInteraction
 import zope.security.checker
-from zope.interface import implements
 
 class ParanoidSecurityPolicy:
     """Deny all access."""
     implements(ISecurityPolicy)
 
-    def checkPermission(self, permission, object, context):
+    createInteraction = staticmethod(_createInteraction)
+
+    def checkPermission(self, permission, object, interaction):
         if permission is zope.security.checker.CheckerPublic:
             return True
-        if (context.user is system_user   # no user
-            and not context.stack  # no untrusted code
-            ):
+
+        users = list(interaction.principals)
+        if len(users) == 1 and users[0] is system_user:
             return True # Nobody not to trust!
 
         return False
 
+
 class PermissiveSecurityPolicy:
     """Allow all access."""
     implements(ISecurityPolicy)
 
-    def checkPermission(self, permission, object, context):
+    createInteraction = staticmethod(_createInteraction)
+
+    def checkPermission(self, permission, object, interaction):
         return True
+

=== Removed File Zope3/src/zope/security/context.py ===

=== Removed File Zope3/src/zope/security/manager.py ===




More information about the Zope3-Checkins mailing list