[Checkins]
SVN: PluggableAuthService/branches/tseaver-pluggable_allowed/
First pass at making PropertiedUser.allowed pluggable (no
local roles plugin yet).
Tres Seaver
tseaver at palladion.com
Thu Aug 3 17:27:52 EDT 2006
Log message for revision 69344:
First pass at making PropertiedUser.allowed pluggable (no local roles plugin yet).
Changed:
U PluggableAuthService/branches/tseaver-pluggable_allowed/PluggableAuthService.py
U PluggableAuthService/branches/tseaver-pluggable_allowed/PropertiedUser.py
U PluggableAuthService/branches/tseaver-pluggable_allowed/interfaces/plugins.py
A PluggableAuthService/branches/tseaver-pluggable_allowed/plugins/UserAllowedPolicies.py
A PluggableAuthService/branches/tseaver-pluggable_allowed/plugins/tests/test_UserAllowedPolicies.py
U PluggableAuthService/branches/tseaver-pluggable_allowed/tests/conformance.py
U PluggableAuthService/branches/tseaver-pluggable_allowed/tests/test_PropertiedUser.py
-=-
Modified: PluggableAuthService/branches/tseaver-pluggable_allowed/PluggableAuthService.py
===================================================================
--- PluggableAuthService/branches/tseaver-pluggable_allowed/PluggableAuthService.py 2006-08-03 18:35:05 UTC (rev 69343)
+++ PluggableAuthService/branches/tseaver-pluggable_allowed/PluggableAuthService.py 2006-08-03 21:27:52 UTC (rev 69344)
@@ -79,6 +79,7 @@
from interfaces.plugins import IRoleAssignerPlugin
from interfaces.plugins import IChallengeProtocolChooser
from interfaces.plugins import IRequestTypeSniffer
+from interfaces.plugins import IUserAllowedPolicyPlugin
from permissions import SearchPrincipals
@@ -1243,6 +1244,11 @@
, 'request_type_sniffer'
, "Request Type Sniffer plugins detect the type of an incoming request."
)
+ , ( IUserAllowedPolicyPlugin
+ , 'IUserAllowedPolicyPlugin'
+ , 'user_allowed_policy'
+ , "Apply a policy for granting access to a user given the required roles."
+ )
)
def addPluggableAuthService( dispatcher
Modified: PluggableAuthService/branches/tseaver-pluggable_allowed/PropertiedUser.py
===================================================================
--- PluggableAuthService/branches/tseaver-pluggable_allowed/PropertiedUser.py 2006-08-03 18:35:05 UTC (rev 69343)
+++ PluggableAuthService/branches/tseaver-pluggable_allowed/PropertiedUser.py 2006-08-03 21:27:52 UTC (rev 69344)
@@ -22,6 +22,7 @@
from AccessControl.PermissionRole import _what_not_even_god_should_do
from interfaces.authservice import IPropertiedUser
+from interfaces.plugins import IUserAllowedPolicyPlugin
from UserPropertySheet import UserPropertySheet
from utils import classImplements
@@ -149,9 +150,29 @@
o Ripped off from AccessControl.User.BasicUser, which provides
no other extension mechanism. :(
"""
+ plugins = aq_parent( self )._getOb( 'plugins' )
+
+ # This isn't really a policy -- everything in Zope expects that
+ # 'roles = ()' means "VERBOTEN".
if object_roles is _what_not_even_god_should_do:
return 0
+ policies = plugins.listPlugins( IUserAllowedPolicyPlugin )
+
+ if len(policies) == 0:
+ return self._default_allow_policies( object, object_roles )
+
+ for policy_id, policy in policies:
+ result = policy.isUserAllowed( self, object, object_roles )
+ if result is None:
+ continue
+ return result
+
+ return None
+
+ def _default_allow_policies( self, object, object_roles ):
+ """ These policies reflect those from the traditional Zope acl_users.
+ """
# Short-circuit the common case of anonymous access.
if object_roles is None or 'Anonymous' in object_roles:
return 1
Modified: PluggableAuthService/branches/tseaver-pluggable_allowed/interfaces/plugins.py
===================================================================
--- PluggableAuthService/branches/tseaver-pluggable_allowed/interfaces/plugins.py 2006-08-03 18:35:05 UTC (rev 69343)
+++ PluggableAuthService/branches/tseaver-pluggable_allowed/interfaces/plugins.py 2006-08-03 21:27:52 UTC (rev 69344)
@@ -432,7 +432,8 @@
class IRequestTypeSniffer( Interface ):
- """ Given a request, detects the request type for later use by other plugins.
+ """ Given a request, detects the request type for later use by other
+ plugins.
"""
def sniffRequestType( request ):
""" Return a interface identifying what kind the request is.
@@ -455,8 +456,15 @@
o Once the protocol is decided, all challenge plugins for that
protocol will be executed.
"""
-#
-# XXX: Do we need a LocalRoleAlgorithm plugin type? E.g., base_cms
-# has two different algorithms, based on whether or not the
-# context object implements IPlacelessSecurity.
-#
+
+class IUserAllowedPolicyPlugin( Interface ):
+
+ """ Pluggable policy for testing user access given required roles.
+ """
+ def isUserAllowed( user, object, object_roles ):
+
+ """ Return a boolean whether the user is allowed to access the object.
+
+ o Return None to indicate that this plugin cannot determine the
+ answer either way.
+ """
Added: PluggableAuthService/branches/tseaver-pluggable_allowed/plugins/UserAllowedPolicies.py
===================================================================
--- PluggableAuthService/branches/tseaver-pluggable_allowed/plugins/UserAllowedPolicies.py 2006-08-03 18:35:05 UTC (rev 69343)
+++ PluggableAuthService/branches/tseaver-pluggable_allowed/plugins/UserAllowedPolicies.py 2006-08-03 21:27:52 UTC (rev 69344)
@@ -0,0 +1,81 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+""" Policies for plugging into 'PropertiedUser.allowed()'
+
+$Id$
+"""
+from AccessControl.SecurityInfo import ClassSecurityInfo
+from App.class_init import default__class_init__ as InitializeClass
+
+from Products.PluggableAuthService.interfaces.plugins \
+ import IUserAllowedPolicyPlugin
+from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
+from Products.PluggableAuthService.utils import classImplements
+
+class ShortcutAllowPolicy( BasePlugin ):
+
+ """ Plugin for traditional "shortcut" allow policy.
+ """
+ meta_type = 'Shortcut Allow Policy'
+
+ security = ClassSecurityInfo()
+
+ security.declarePrivate( 'isUserAllowed' )
+ def isUserAllowed( self, user, object, object_roles ):
+ """ See IUserAllowedPolicyPlugin.
+ """
+ # Short-circuit the common case of anonymous access.
+ if object_roles is None or 'Anonymous' in object_roles:
+ return True
+
+ # Provide short-cut access if object is protected by 'Authenticated'
+ # role and user is not nobody
+ if 'Authenticated' in object_roles and (
+ user.getUserName() != 'Anonymous User'):
+ return True
+
+ return None # pass
+
+classImplements( ShortcutAllowPolicy
+ , IUserAllowedPolicyPlugin
+ )
+
+InitializeClass( ShortcutAllowPolicy )
+
+class GlobalRolesAllowPolicy( BasePlugin ):
+
+ """ Plugin for traditional allow policy using global roles.
+ """
+ meta_type = 'Global Roles Allow Policy'
+
+ security = ClassSecurityInfo()
+
+ security.declarePrivate( 'isUserAllowed' )
+ def isUserAllowed( self, user, object, object_roles ):
+ """ See IUserAllowedPolicyPlugin.
+ """
+ user_roles = user.getRoles()
+
+ for role in object_roles:
+ if role in user_roles:
+ return user._check_context(object)
+
+ return None # pass
+
+classImplements( GlobalRolesAllowPolicy
+ , IUserAllowedPolicyPlugin
+ )
+
+InitializeClass( GlobalRolesAllowPolicy )
Property changes on: PluggableAuthService/branches/tseaver-pluggable_allowed/plugins/UserAllowedPolicies.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: PluggableAuthService/branches/tseaver-pluggable_allowed/plugins/tests/test_UserAllowedPolicies.py
===================================================================
--- PluggableAuthService/branches/tseaver-pluggable_allowed/plugins/tests/test_UserAllowedPolicies.py 2006-08-03 18:35:05 UTC (rev 69343)
+++ PluggableAuthService/branches/tseaver-pluggable_allowed/plugins/tests/test_UserAllowedPolicies.py 2006-08-03 21:27:52 UTC (rev 69344)
@@ -0,0 +1,138 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+""" Unit tests for UserAllowedPolicies
+
+$Id$
+"""
+import unittest
+
+from Products.PluggableAuthService.tests.conformance \
+ import IUserAllowedPolicyPlugin_conformance
+
+class PolicyTestBase( unittest.TestCase
+ , IUserAllowedPolicyPlugin_conformance
+ ):
+
+ # must supply '_getTargetClass'
+
+ def _makeOne( self, *args, **kw ):
+ return self._getTargetClass()( *args, **kw )
+
+class ShortcutAllowPolicyTests( PolicyTestBase ):
+
+ def _getTargetClass( self ):
+ from Products.PluggableAuthService.plugins.UserAllowedPolicies \
+ import ShortcutAllowPolicy
+ return ShortcutAllowPolicy
+
+ def test_pass_wo_anonymous_or_authenticated_in_roles( self ):
+ plugin = self._makeOne()
+ user = DummyUser()
+ obj = object()
+ obj_roles = ( 'Role A', )
+ allowed = plugin.isUserAllowed( user, obj, obj_roles )
+ self.assertEqual(allowed, None)
+
+ def test_allows_for_public( self ):
+ plugin = self._makeOne()
+ user = DummyUser()
+ obj = object()
+ obj_roles = None # classic Zope "public"
+ allowed = plugin.isUserAllowed( user, obj, obj_roles )
+ self.failUnless(allowed)
+
+ def test_allows_for_Anonymous_in_roles( self ):
+ plugin = self._makeOne()
+ user = DummyUser()
+ obj = object()
+ obj_roles = ( 'Anonymous', )
+ allowed = plugin.isUserAllowed( user, obj, obj_roles )
+ self.failUnless(allowed)
+
+ def test_allows_for_Authenticated_in_roles_not_anonymous( self ):
+ plugin = self._makeOne()
+ user = DummyUser()
+ obj = object()
+ obj_roles = ( 'Authenticated', )
+ allowed = plugin.isUserAllowed( user, obj, obj_roles )
+ self.failUnless(allowed)
+
+ def test_pass_for_Authenticated_in_roles_and_anonymous( self ):
+ plugin = self._makeOne()
+ user = DummyUser( 'Anonymous User' )
+ obj = object()
+ obj_roles = ( 'Authenticated', )
+ allowed = plugin.isUserAllowed( user, obj, obj_roles )
+ self.assertEqual(allowed, None)
+
+class GlobalRolesAllowPolicyTests( PolicyTestBase ):
+
+ def _getTargetClass( self ):
+ from Products.PluggableAuthService.plugins.UserAllowedPolicies \
+ import GlobalRolesAllowPolicy
+ return GlobalRolesAllowPolicy
+
+ def test_pass_for_no_match_in_roles( self ):
+ plugin = self._makeOne()
+ user = DummyUser()
+ obj = object()
+ obj_roles = ( 'Role A', )
+ allowed = plugin.isUserAllowed( user, obj, obj_roles )
+ self.assertEqual(allowed, None)
+
+ def test_allow_for_match_in_roles_and_in_context( self ):
+ plugin = self._makeOne()
+ user = DummyUser( roles=( 'Role B', ), in_context=True )
+ obj = object()
+ obj_roles = ( 'Role A', 'Role B', 'Role C' )
+ allowed = plugin.isUserAllowed( user, obj, obj_roles )
+ self.failUnless(allowed)
+
+ def test_disallow_for_match_in_roles( self ):
+ plugin = self._makeOne()
+ user = DummyUser( roles=( 'Role B', ), in_context=False )
+ obj = object()
+ obj_roles = ( 'Role A', 'Role B', 'Role C' )
+ allowed = plugin.isUserAllowed( user, obj, obj_roles )
+ self.failIf(allowed)
+
+class DummyUser:
+ def __init__( self, name='Dummy User', roles=(), in_context=True
+ , shared_roles=None ):
+ self.name = name
+ self.roles = roles
+ self.in_context = in_context
+ self.shared_roles = shared_roles
+
+ def getUserName( self ):
+ return self.name
+
+ def getRoles( self ):
+ return self.roles
+
+ def _check_context( self, object ):
+ return self.in_context
+
+ def _shared_roles( self, object ):
+ return self.shared_roles
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite( ShortcutAllowPolicyTests ),
+ unittest.makeSuite( GlobalRolesAllowPolicyTests ),
+ ))
+
+if __name__ == "__main__":
+ unittest.main(defaultTest='test_suite')
Property changes on: PluggableAuthService/branches/tseaver-pluggable_allowed/plugins/tests/test_UserAllowedPolicies.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Modified: PluggableAuthService/branches/tseaver-pluggable_allowed/tests/conformance.py
===================================================================
--- PluggableAuthService/branches/tseaver-pluggable_allowed/tests/conformance.py 2006-08-03 18:35:05 UTC (rev 69343)
+++ PluggableAuthService/branches/tseaver-pluggable_allowed/tests/conformance.py 2006-08-03 21:27:52 UTC (rev 69344)
@@ -174,6 +174,15 @@
verifyClass( IRequestTypeSniffer, self._getTargetClass() )
+class IUserAllowedPolicyPlugin_conformance:
+
+ def test_UserAllowedPolicyPlugin_conformance( self ):
+
+ from Products.PluggableAuthService.interfaces.plugins \
+ import IUserAllowedPolicyPlugin
+
+ verifyClass( IUserAllowedPolicyPlugin, self._getTargetClass() )
+
class IUserFolder_conformance:
def test_conformance_IUserFolder( self ):
Modified: PluggableAuthService/branches/tseaver-pluggable_allowed/tests/test_PropertiedUser.py
===================================================================
--- PluggableAuthService/branches/tseaver-pluggable_allowed/tests/test_PropertiedUser.py 2006-08-03 18:35:05 UTC (rev 69343)
+++ PluggableAuthService/branches/tseaver-pluggable_allowed/tests/test_PropertiedUser.py 2006-08-03 21:27:52 UTC (rev 69344)
@@ -31,6 +31,25 @@
self.__ac_local_roles__ = local_roles
+class FauxPluginRegistry( Implicit ):
+
+ def __init__( self, plugins=() ):
+ self.plugins = plugins
+
+ def listPlugins( self, iface ):
+ return [ ( 'plugin_%02d' % i, self.plugins[ i ] )
+ for i in range( len( self.plugins ) ) ]
+
+class FauxPAS( Implicit ):
+
+ def __init__( self, plugins ):
+
+ self.plugins = FauxPluginRegistry( plugins )
+
+ def _getOb( self, id ):
+
+ return getattr( self, id )
+
class PropertiedUserTests( unittest.TestCase
, IBasicUser_conformance
, IPropertiedUser_conformance
@@ -47,6 +66,9 @@
return self._getTargetClass()( id, login, *args, **kw )
+ def _makePAS( self, *plugins ):
+ return FauxPAS( plugins )
+
def test_empty( self ):
user = self._makeOne( 'empty' )
@@ -191,71 +213,81 @@
self.assertEqual( len( local_roles ), 1 )
self.failUnless( 'Manager' in local_roles )
- def test_allowed_not_even_god_should( self ):
+ def test_allowed_not_even_god_should_do_no_plugins( self ):
from AccessControl.PermissionRole import _what_not_even_god_should_do
- user = self._makeOne()
+ pas = self._makePAS()
+ user = self._makeOne().__of__(pas)
self.failIf( user.allowed( None, _what_not_even_god_should_do ) )
- def test_allowed_anonymous( self ):
+ def test_allowed_anonymous_no_plugins( self ):
- user = self._makeOne()
+ pas = self._makePAS()
+ user = self._makeOne().__of__(pas)
self.failUnless( user.allowed( None, ('Anonymous',) ) )
- def test_allowed_authenticated( self ):
+ def test_allowed_authenticated_no_plugins( self ):
- user = self._makeOne()
+ pas = self._makePAS()
+ user = self._makeOne().__of__(pas)
self.failUnless( user.allowed( None, ('Authenticated',) ) )
- def test_allowed_authenticated_required_but_anonymous( self ):
+ def test_allowed_authenticated_required_but_anonymous_no_plugins( self ):
- user = self._makeOne('Anonymous User')
+ pas = self._makePAS()
+ user = self._makeOne('Anonymous User').__of__(pas)
self.failIf( user.allowed( None, ('Authenticated',) ) )
- def test_allowed_global_roles_ok( self ):
+ def test_allowed_global_roles_ok_no_plugins( self ):
- user = self._makeOne()
+ pas = self._makePAS()
+ user = self._makeOne().__of__(pas)
user._addRoles( ( 'Role 1', 'Role 2' ) )
self.failUnless( user.allowed( None, ( 'Role 1', ) ) )
- def test_allowed_global_roles_not_ok( self ):
+ def test_allowed_global_roles_not_ok_no_plugins( self ):
- user = self._makeOne()
+ pas = self._makePAS()
+ user = self._makeOne().__of__(pas)
user._addRoles( ( 'Role 1', 'Role 2' ) )
self.failIf( user.allowed( None, ( 'Role 3', ) ) )
- def test_allowed_local_roles_on_user_ok( self ):
+ def test_allowed_local_roles_on_user_ok_no_plugins( self ):
- user = self._makeOne( 'user' )
+ pas = self._makePAS()
+ user = self._makeOne( 'user' ).__of__(pas)
object = FauxProtected( { 'user' : ( 'Role 1', ) } )
self.failUnless( user.allowed( object, ( 'Role 1', ) ) )
- def test_allowed_local_roles_on_user_not_ok( self ):
+ def test_allowed_local_roles_on_user_not_ok_no_plugins( self ):
- user = self._makeOne( 'user' )
+ pas = self._makePAS()
+ user = self._makeOne( 'user' ).__of__(pas)
object = FauxProtected( { 'user' : ( 'Role 1', ) } )
self.failIf( user.allowed( object, ( 'Role 2', ) ) )
- def test_allowed_local_roles_on_group_ok( self ):
+ def test_allowed_local_roles_on_group_ok_no_plugins( self ):
- user = self._makeOne( 'user' )
+ pas = self._makePAS()
+ user = self._makeOne( 'user' ).__of__(pas)
user._addGroups( ( 'Group 1', 'Group 2' ) )
object = FauxProtected( { 'Group 1' : ( 'Role 1', ) } )
self.failUnless( user.allowed( object, ( 'Role 1', ) ) )
- def test_allowed_acquisition( self ):
+ def test_allowed_acquisition_no_plugins( self ):
groups = ( 'Group A', 'Group B' )
- user = self._makeOne()
+ pas = self._makePAS()
+ user = self._makeOne().__of__(pas)
user._addGroups( groups )
faux_container = FauxProtected( { 'Group A' : ( 'Manager', ) } )
@@ -264,12 +296,13 @@
self.failUnless( user.allowed( faux_contained, ( 'Manager', ) ) )
- def test_allowed_weslayan( self ):
+ def test_allowed_weslayan_no_plugins( self ):
# Test "methodish" checks.
groups = ( 'Group A', 'Group B' )
- user = self._makeOne()
+ pas = self._makePAS()
+ user = self._makeOne().__of__(pas)
user._addGroups( groups )
faux_self = FauxProtected( { 'Group A' : ( 'Manager', ) } )
@@ -278,7 +311,28 @@
self.failUnless( user.allowed( faux_method, ( 'Manager', ) ) )
+ def test_allowed_not_even_god_should_do_even_with_plugins( self ):
+ from AccessControl.PermissionRole import _what_not_even_god_should_do
+ pas = self._makePAS( allowAnything )
+ user = self._makeOne().__of__(pas)
+
+ self.failIf( user.allowed( None, _what_not_even_god_should_do ) )
+
+ def test_allowed_global_roles_not_ok_with_allowAnything_plugin( self ):
+
+ pas = self._makePAS( allowAnything )
+ user = self._makeOne().__of__(pas)
+ user._addRoles( ( 'Role 1', 'Role 2' ) )
+
+ self.failUnless( user.allowed( None, ( 'Role 3', ) ) )
+
+class AllowAnything:
+ def isUserAllowed( self, user, object, object_rules ):
+ return True
+
+allowAnything = AllowAnything()
+
if __name__ == "__main__":
unittest.main()
More information about the Checkins
mailing list