From 2466f6eaa2246598dc6cb3c962364773eb4cc64a Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 16 Jul 2008 10:32:08 +0000 Subject: Add security. --- repoze/bfg/tests/test_router.py | 111 +++++++++++ repoze/bfg/tests/test_security.py | 362 +++++++++++++++++++++++++++++++++++ repoze/bfg/tests/test_wsgiadapter.py | 52 ----- repoze/bfg/tests/test_zcml.py | 38 +++- 4 files changed, 507 insertions(+), 56 deletions(-) create mode 100644 repoze/bfg/tests/test_security.py (limited to 'repoze/bfg/tests') diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py index 8bb579054..148b72427 100644 --- a/repoze/bfg/tests/test_router.py +++ b/repoze/bfg/tests/test_router.py @@ -21,12 +21,24 @@ class RouterTests(unittest.TestCase, PlacelessSetup): from repoze.bfg.interfaces import IViewFactory gsm.registerAdapter(app, for_, IViewFactory, name) + def _registerPermission(self, permission, name, *for_): + import zope.component + gsm = zope.component.getGlobalSiteManager() + from repoze.bfg.interfaces import IViewPermission + gsm.registerAdapter(permission, for_, IViewPermission, name) + def _registerWSGIFactory(self, app, name, *for_): import zope.component gsm = zope.component.getGlobalSiteManager() from repoze.bfg.interfaces import IWSGIApplicationFactory gsm.registerAdapter(app, for_, IWSGIApplicationFactory, name) + def _registerSecurityPolicy(self, secpol): + import zope.component + gsm = zope.component.getGlobalSiteManager() + from repoze.bfg.interfaces import ISecurityPolicy + gsm.registerUtility(secpol, ISecurityPolicy) + def _getTargetClass(self): from repoze.bfg.router import Router return Router @@ -155,6 +167,88 @@ class RouterTests(unittest.TestCase, PlacelessSetup): self.failUnless('404' in result[0]) self.assertEqual(start_response.status, '404 Not Found') + def test_call_view_registered_security_policy_permission_none(self): + rootpolicy = make_rootpolicy(None) + from zope.interface import Interface + from zope.interface import directlyProvides + class IContext(Interface): + pass + from repoze.bfg.interfaces import IRequest + context = DummyContext() + directlyProvides(context, IContext) + traversalfactory = make_traversal_factory(context, '', ['']) + response = DummyResponse() + viewfactory = make_view_factory(response) + wsgifactory = make_wsgi_factory('200 OK', (), ['Hello world']) + environ = self._makeEnviron() + self._registerTraverserFactory(traversalfactory, '', None, None) + self._registerViewFactory(viewfactory, '', IContext, IRequest) + self._registerWSGIFactory(wsgifactory, '', None, None, None) + secpol = DummySecurityPolicy() + self._registerSecurityPolicy(secpol) + app_context = make_appcontext() + router = self._makeOne(rootpolicy, None) + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(start_response.status, '200 OK') + + def test_call_view_registered_security_policy_permission_succeeds(self): + rootpolicy = make_rootpolicy(None) + from zope.interface import Interface + from zope.interface import directlyProvides + class IContext(Interface): + pass + from repoze.bfg.interfaces import IRequest + context = DummyContext() + directlyProvides(context, IContext) + traversalfactory = make_traversal_factory(context, '', ['']) + response = DummyResponse() + viewfactory = make_view_factory(response) + wsgifactory = make_wsgi_factory('200 OK', (), ['Hello world']) + secpol = DummySecurityPolicy() + permissionfactory = make_permission_factory(True) + environ = self._makeEnviron() + self._registerTraverserFactory(traversalfactory, '', None, None) + self._registerViewFactory(viewfactory, '', IContext, IRequest) + self._registerWSGIFactory(wsgifactory, '', None, None, None) + self._registerSecurityPolicy(secpol) + self._registerPermission(permissionfactory, '', IContext, IRequest) + app_context = make_appcontext() + router = self._makeOne(rootpolicy, None) + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(start_response.status, '200 OK') + self.assertEqual(permissionfactory.checked_with, secpol) + + def test_call_view_registered_security_policy_permission_failss(self): + rootpolicy = make_rootpolicy(None) + from zope.interface import Interface + from zope.interface import directlyProvides + class IContext(Interface): + pass + from repoze.bfg.interfaces import IRequest + context = DummyContext() + directlyProvides(context, IContext) + traversalfactory = make_traversal_factory(context, '', ['']) + response = DummyResponse() + viewfactory = make_view_factory(response) + wsgifactory = make_wsgi_factory('200 OK', (), ['Hello world']) + secpol = DummySecurityPolicy() + permissionfactory = make_permission_factory(False) + environ = self._makeEnviron() + self._registerTraverserFactory(traversalfactory, '', None, None) + self._registerViewFactory(viewfactory, '', IContext, IRequest) + self._registerWSGIFactory(wsgifactory, '', None, None, None) + self._registerSecurityPolicy(secpol) + self._registerPermission(permissionfactory, '', IContext, IRequest) + app_context = make_appcontext() + router = self._makeOne(rootpolicy, None) + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(start_response.status, '401 Unauthorized') + self.failUnless('permission' in result[0]) + self.assertEqual(permissionfactory.checked_with, secpol) + class MakeAppTests(unittest.TestCase, PlacelessSetup): def setUp(self): PlacelessSetup.setUp(self) @@ -213,6 +307,20 @@ def make_traversal_factory(context, name, subpath): return context, name, subpath return DummyTraversalFactory +def make_permission_factory(result): + class DummyPermissionFactory: + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self, secpol): + self.__class__.checked_with = secpol + return result + + def __repr__(self): + return 'permission' + return DummyPermissionFactory + def make_rootpolicy(root): def rootpolicy(environ): return root @@ -237,3 +345,6 @@ class DummyResponse: headerlist = () app_iter = () +class DummySecurityPolicy: + pass + diff --git a/repoze/bfg/tests/test_security.py b/repoze/bfg/tests/test_security.py new file mode 100644 index 000000000..8a4c624e9 --- /dev/null +++ b/repoze/bfg/tests/test_security.py @@ -0,0 +1,362 @@ +import unittest + +from zope.component.testing import PlacelessSetup + +class TestACLAuthorizer(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.security import ACLAuthorizer + return ACLAuthorizer + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_permits_no_acl_raises(self): + context = DummyContext() + logger = DummyLogger() + authorizer = self._makeOne(context, logger) + from repoze.bfg.interfaces import NoAuthorizationInformation + self.assertRaises(NoAuthorizationInformation, + authorizer.permits, (), None) + + def test_permits_deny_implicit_empty_acl(self): + context = DummyContext() + logger = DummyLogger() + context.__acl__ = [] + authorizer = self._makeOne(context, logger) + result = authorizer.permits((), None) + self.assertEqual(result, False) + self.assertEqual(result.ace, None) + + def test_permits_deny_no_principals_implicit(self): + context = DummyContext() + logger = DummyLogger() + from repoze.bfg.security import Allow + from repoze.bfg.security import Everyone + acl = [(Allow, Everyone, 'view')] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + result = authorizer.permits(None) + self.assertEqual(result, False) + self.assertEqual(result.ace, None) + + def test_permits_deny_oneacl_implicit(self): + context = DummyContext() + logger = DummyLogger() + from repoze.bfg.security import Allow + acl = [(Allow, 'somebody', 'view')] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + result = authorizer.permits('view', 'somebodyelse') + self.assertEqual(result, False) + self.assertEqual(result.ace, None) + + def test_permits_deny_twoacl_implicit(self): + context = DummyContext() + logger = DummyLogger() + from repoze.bfg.security import Allow + acl = [(Allow, 'somebody', 'view'), (Allow, 'somebody', 'write')] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + result = authorizer.permits('view', 'somebodyelse') + self.assertEqual(result, False) + self.assertEqual(result.ace, None) + + def test_permits_deny_oneacl_explcit(self): + context = DummyContext() + logger = DummyLogger() + from repoze.bfg.security import Deny + ace = (Deny, 'somebody', 'view') + acl = [ace] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + result = authorizer.permits('view', 'somebody') + self.assertEqual(result, False) + self.assertEqual(result.ace, ace) + + def test_permits_deny_oneacl_multiperm_explcit(self): + context = DummyContext() + logger = DummyLogger() + acl = [] + from repoze.bfg.security import Deny + from repoze.bfg.security import Allow + deny = (Deny, 'somebody', ('view', 'read')) + allow = (Allow, 'somebody', 'view') + acl = [deny, allow] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + result = authorizer.permits('view', 'somebody') + self.assertEqual(result, False) + self.assertEqual(result.ace, deny) + + def test_permits_deny_twoacl_explicit(self): + context = DummyContext() + logger = DummyLogger() + acl = [] + from repoze.bfg.security import Deny + from repoze.bfg.security import Allow + allow = (Allow, 'somebody', 'read') + deny = (Deny, 'somebody', 'view') + acl = [allow, deny] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + result = authorizer.permits('view', 'somebody') + self.assertEqual(result, False) + self.assertEqual(result.ace, deny) + + def test_permits_allow_twoacl_explicit(self): + context = DummyContext() + logger = DummyLogger() + from repoze.bfg.security import Deny + from repoze.bfg.security import Allow + allow = (Allow, 'somebody', 'read') + deny = (Deny, 'somebody', 'view') + acl = [allow, deny] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + result = authorizer.permits('read', 'somebody') + self.assertEqual(result, True) + self.assertEqual(result.ace, allow) + + def test_permits_nested_principals_list_allow(self): + context = DummyContext() + logger = DummyLogger() + acl = [] + from repoze.bfg.security import Allow + ace = (Allow, 'larry', 'read') + acl = [ace] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + principals = (['fred', ['jim', ['bob', 'larry']]]) + result = authorizer.permits('read', *principals) + self.assertEqual(result, True) + self.assertEqual(result.ace, ace) + + def test_permits_nested_principals_list_deny_explicit(self): + context = DummyContext() + logger = DummyLogger() + from repoze.bfg.security import Deny + ace = (Deny, 'larry', 'read') + acl = [ace] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + principals = (['fred', ['jim', ['bob', 'larry']]]) + result = authorizer.permits('read', *principals) + self.assertEqual(result, False) + self.assertEqual(result.ace, ace) + + def test_permits_nested_principals_list_deny_implicit(self): + context = DummyContext() + logger = DummyLogger() + from repoze.bfg.security import Allow + ace = (Allow, 'somebodyelse', 'read') + acl = [ace] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + principals = (['fred', ['jim', ['bob', 'larry']]]) + result = authorizer.permits('read', *principals) + self.assertEqual(result, False) + + def test_logging_deny_implicit(self): + context = DummyContext() + logger = DummyLogger() + from repoze.bfg.security import Allow + ace = (Allow, 'somebodyelse', 'read') + acl = [ace] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + principals = ['fred'] + result = authorizer.permits('read', *principals) + self.assertEqual(len(logger.messages), 1) + + def test_logging_deny_explicit(self): + context = DummyContext() + logger = DummyLogger() + from repoze.bfg.security import Deny + ace = (Deny, 'somebodyelse', 'read') + acl = [ace] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + principals = ['somebodyelse'] + result = authorizer.permits('read', *principals) + self.assertEqual(len(logger.messages), 1) + + def test_logging_allow(self): + context = DummyContext() + logger = DummyLogger() + from repoze.bfg.security import Allow + ace = (Allow, 'somebodyelse', 'read') + acl = [ace] + context.__acl__ = acl + authorizer = self._makeOne(context, logger) + principals = ['somebodyelse'] + result = authorizer.permits('read', *principals) + self.assertEqual(len(logger.messages), 1) + +class RemoteUserACLSecurityPolicy(unittest.TestCase, PlacelessSetup): + def _getTargetClass(self): + from repoze.bfg.security import RemoteUserACLSecurityPolicy + return RemoteUserACLSecurityPolicy + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def setUp(self): + PlacelessSetup.setUp(self) + + def tearDown(self): + PlacelessSetup.tearDown(self) + + def test_permits_no_remote_user_no_acl_info_on_context(self): + context = DummyContext() + request = DummyRequest({}) + logger = DummyLogger() + policy = self._makeOne(logger) + authorizer_factory = make_authorizer_factory(None) + policy.authorizer_factory = authorizer_factory + result = policy.permits(context, request, 'view') + self.assertEqual(result, False) + from repoze.bfg.security import Everyone + self.assertEqual(authorizer_factory.principals, (Everyone,)) + self.assertEqual(authorizer_factory.permission, 'view') + self.assertEqual(authorizer_factory.context, context) + + def test_permits_no_remote_user_acl_info_on_context(self): + context = DummyContext() + context.__acl__ = [] + request = DummyRequest({}) + logger = DummyLogger() + policy = self._makeOne(logger) + authorizer_factory = make_authorizer_factory(None) + policy.authorizer_factory = authorizer_factory + result = policy.permits(context, request, 'view') + self.assertEqual(result, False) + from repoze.bfg.security import Everyone + self.assertEqual(authorizer_factory.principals, (Everyone,)) + self.assertEqual(authorizer_factory.permission, 'view') + self.assertEqual(authorizer_factory.context, context) + + def test_permits_no_remote_user_withparents_root_has_acl_info(self): + context = DummyContext() + context.__name__ = None + context.__parent__ = None + context2 = DummyContext() + context2.__name__ = 'context2' + context2.__parent__ = context + context.__acl__ = [] + request = DummyRequest({}) + logger = DummyLogger() + policy = self._makeOne(logger) + authorizer_factory = make_authorizer_factory(None) + policy.authorizer_factory = authorizer_factory + result = policy.permits(context, request, 'view') + self.assertEqual(result, False) + from repoze.bfg.security import Everyone + self.assertEqual(authorizer_factory.principals, (Everyone,)) + self.assertEqual(authorizer_factory.permission, 'view') + self.assertEqual(authorizer_factory.context, context) + + def test_permits_no_remote_user_withparents_root_allows_everyone(self): + context = DummyContext() + context.__name__ = None + context.__parent__ = None + context2 = DummyContext() + context2.__name__ = 'context2' + context2.__parent__ = context + request = DummyRequest({}) + logger = DummyLogger() + policy = self._makeOne(logger) + authorizer_factory = make_authorizer_factory(context) + policy.authorizer_factory = authorizer_factory + result = policy.permits(context, request, 'view') + self.assertEqual(result, True) + from repoze.bfg.security import Everyone + self.assertEqual(authorizer_factory.principals, (Everyone,)) + self.assertEqual(authorizer_factory.permission, 'view') + self.assertEqual(authorizer_factory.context, context) + + +class TestViewPermission(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.security import ViewPermission + return ViewPermission + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_call(self): + context = DummyContext() + request = DummyRequest({}) + secpol = DummySecurityPolicy(True) + permission = self._makeOne(context, request, 'repoze.view') + result = permission(secpol) + self.assertEqual(result, True) + self.assertEqual(secpol.checked, (context, request, 'repoze.view')) + +class TestViewPermissionFactory(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.security import ViewPermissionFactory + return ViewPermissionFactory + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_call(self): + context = DummyContext() + request = DummyRequest({}) + factory = self._makeOne('repoze.view') + result = factory(context, request) + self.assertEqual(result.permission_name, 'repoze.view') + self.assertEqual(result.context, context) + self.assertEqual(result.request, request) + +class DummyContext: + pass + +class DummyRequest: + def __init__(self, environ): + self.environ = environ + +class DummySecurityPolicy: + def __init__(self, result): + self.result = result + + def permits(self, *args): + self.checked = args + return self.result + +class DummyLogger: + def __init__(self): + self.messages = [] + def debug(self, msg): + self.messages.append(msg) + +class make_authorizer_factory: + def __init__(self, expected_context, intermediates_raise=False): + self.expected_context = expected_context + self.intermediates_raise = intermediates_raise + + def __call__(self, context, logger): + authorizer = self + class Authorizer: + def permits(self, permission, *principals): + authorizer.permission = permission + authorizer.principals = principals + authorizer.context = context + result = authorizer.expected_context == context + if not result and authorizer.intermediates_raise: + from repoze.bfg.interfaces import NoAuthorizationInformation + raise NoAuthorizationInformation() + return result + return Authorizer() + + + + + + + + diff --git a/repoze/bfg/tests/test_wsgiadapter.py b/repoze/bfg/tests/test_wsgiadapter.py index 500c7b138..217d43427 100644 --- a/repoze/bfg/tests/test_wsgiadapter.py +++ b/repoze/bfg/tests/test_wsgiadapter.py @@ -91,58 +91,6 @@ class NaiveWSGIAdapterTests(unittest.TestCase, PlacelessSetup): start_response = DummyStartResponse() self.assertRaises(ValueError, adapter, environ, start_response) - def test_view_fails_security_policy(self): - import zope.component - gsm = zope.component.getGlobalSiteManager() - from repoze.bfg.wsgiadapter import IViewSecurityPolicy - def failed(context, request): - def view(): - response = DummyResponse() - response.app_iter = ['failed'] - response.status = '401 Unauthorized' - response.headerlist = () - return response - return view - gsm.registerAdapter(failed, (None, None), IViewSecurityPolicy) - request = DummyRequest() - response = DummyResponse() - response.app_iter = ['Hello world'] - def view(request): - response.request = request - return response - context = DummyContext() - adapter = self._makeOne(context, request, view) - environ = {} - start_response = DummyStartResponse() - result = adapter(environ, start_response) - self.assertEqual(result, ['failed']) - self.assertEqual(start_response.headers, ()) - self.assertEqual(start_response.status, '401 Unauthorized') - - def test_view_passes_security_policy(self): - import zope.component - gsm = zope.component.getGlobalSiteManager() - from repoze.bfg.wsgiadapter import IViewSecurityPolicy - def failed(context, request): - def view(): - return None - return view - gsm.registerAdapter(failed, (None, None), IViewSecurityPolicy) - request = DummyRequest() - response = DummyResponse() - response.app_iter = ['Hello world'] - def view(request): - response.request = request - return response - context = DummyContext() - adapter = self._makeOne(context, request, view) - environ = {} - start_response = DummyStartResponse() - result = adapter(environ, start_response) - self.assertEqual(result, ['Hello world']) - self.assertEqual(start_response.headers, ()) - self.assertEqual(start_response.status, '200 OK') - self.assertEqual(response.request, request) class DummyContext: pass diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py index db826b687..a2602b9c1 100644 --- a/repoze/bfg/tests/test_zcml.py +++ b/repoze/bfg/tests/test_zcml.py @@ -36,10 +36,12 @@ class TestViewDirective(unittest.TestCase, PlacelessSetup): from repoze.bfg.interfaces import IView from repoze.bfg.interfaces import IRequest from repoze.bfg.interfaces import IViewFactory + from repoze.bfg.interfaces import IViewPermission + from repoze.bfg.security import ViewPermissionFactory from zope.component.zcml import handler from zope.component.interface import provideInterface - self.assertEqual(len(actions), 3) + self.assertEqual(len(actions), 4) regutil_discriminator = ('utility', IView, context.path('minimal.pt')) regutil = actions[0] @@ -57,7 +59,20 @@ class TestViewDirective(unittest.TestCase, PlacelessSetup): self.assertEqual(provide['args'][0], '') self.assertEqual(provide['args'][1], IFoo) - regadapt = actions[2] + permission = actions[2] + permission_discriminator = ('permission', IFoo, '', IRequest, + IViewPermission) + self.assertEqual(permission['discriminator'], permission_discriminator) + self.assertEqual(permission['callable'], handler) + self.assertEqual(permission['args'][0], 'registerAdapter') + self.failUnless(isinstance(permission['args'][1],ViewPermissionFactory)) + self.assertEqual(permission['args'][1].permission_name, 'repoze.view') + self.assertEqual(permission['args'][2], (IFoo, IRequest)) + self.assertEqual(permission['args'][3], IViewPermission) + self.assertEqual(permission['args'][4], '') + self.assertEqual(permission['args'][5], None) + + regadapt = actions[3] regadapt_discriminator = ('view', IFoo, '', IRequest, IViewFactory) self.assertEqual(regadapt['discriminator'], regadapt_discriminator) self.assertEqual(regadapt['callable'], handler) @@ -78,18 +93,33 @@ class TestViewDirective(unittest.TestCase, PlacelessSetup): actions = context.actions from repoze.bfg.interfaces import IRequest from repoze.bfg.interfaces import IViewFactory + from repoze.bfg.interfaces import IViewPermission + from repoze.bfg.security import ViewPermissionFactory from zope.component.zcml import handler from zope.component.interface import provideInterface - self.assertEqual(len(actions), 2) + self.assertEqual(len(actions), 3) provide = actions[0] self.assertEqual(provide['discriminator'], None) self.assertEqual(provide['callable'], provideInterface) self.assertEqual(provide['args'][0], '') self.assertEqual(provide['args'][1], IFoo) + + permission = actions[1] + permission_discriminator = ('permission', IFoo, '', IRequest, + IViewPermission) + self.assertEqual(permission['discriminator'], permission_discriminator) + self.assertEqual(permission['callable'], handler) + self.assertEqual(permission['args'][0], 'registerAdapter') + self.failUnless(isinstance(permission['args'][1],ViewPermissionFactory)) + self.assertEqual(permission['args'][1].permission_name, 'repoze.view') + self.assertEqual(permission['args'][2], (IFoo, IRequest)) + self.assertEqual(permission['args'][3], IViewPermission) + self.assertEqual(permission['args'][4], '') + self.assertEqual(permission['args'][5], None) - regadapt = actions[1] + regadapt = actions[2] regadapt_discriminator = ('view', IFoo, '', IRequest, IViewFactory) self.assertEqual(regadapt['discriminator'], regadapt_discriminator) self.assertEqual(regadapt['callable'], handler) -- cgit v1.2.3