diff options
| author | Michael Merickel <michael@merickel.org> | 2019-09-30 22:23:02 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-09-30 22:23:02 -0500 |
| commit | 849463d3c2f5ad2c89b3d10a2abce63e4892082d (patch) | |
| tree | 5bc507d427d8d2000c59ad7837cc03099decf1b5 /tests | |
| parent | ada0a977d9190520c21ffaf9500860db2f3a1b3e (diff) | |
| parent | cdb26610782176955cd8cfb0b3c3e242ca819f74 (diff) | |
| download | pyramid-849463d3c2f5ad2c89b3d10a2abce63e4892082d.tar.gz pyramid-849463d3c2f5ad2c89b3d10a2abce63e4892082d.tar.bz2 pyramid-849463d3c2f5ad2c89b3d10a2abce63e4892082d.zip | |
Merge pull request #3465 from luhn/security-policy
Security policy implementation
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/pkgs/defpermbugapp/__init__.py | 4 | ||||
| -rw-r--r-- | tests/pkgs/forbiddenapp/__init__.py | 4 | ||||
| -rw-r--r-- | tests/pkgs/legacysecurityapp/__init__.py | 37 | ||||
| -rw-r--r-- | tests/pkgs/securityapp/__init__.py | 41 | ||||
| -rw-r--r-- | tests/pkgs/staticpermapp/__init__.py | 4 | ||||
| -rw-r--r-- | tests/test_authentication.py | 50 | ||||
| -rw-r--r-- | tests/test_authorization.py | 261 | ||||
| -rw-r--r-- | tests/test_config/test_init.py | 9 | ||||
| -rw-r--r-- | tests/test_config/test_security.py | 32 | ||||
| -rw-r--r-- | tests/test_config/test_testing.py | 28 | ||||
| -rw-r--r-- | tests/test_config/test_views.py | 29 | ||||
| -rw-r--r-- | tests/test_integration.py | 50 | ||||
| -rw-r--r-- | tests/test_predicates.py | 22 | ||||
| -rw-r--r-- | tests/test_request.py | 4 | ||||
| -rw-r--r-- | tests/test_security.py | 175 | ||||
| -rw-r--r-- | tests/test_testing.py | 45 | ||||
| -rw-r--r-- | tests/test_viewderivers.py | 134 |
17 files changed, 677 insertions, 252 deletions
diff --git a/tests/pkgs/defpermbugapp/__init__.py b/tests/pkgs/defpermbugapp/__init__.py index 81897e86a..af78404ae 100644 --- a/tests/pkgs/defpermbugapp/__init__.py +++ b/tests/pkgs/defpermbugapp/__init__.py @@ -25,6 +25,6 @@ def includeme(config): authn_policy = AuthTktAuthenticationPolicy('seekt1t', hashalg='sha512') authz_policy = ACLAuthorizationPolicy() config.scan('tests.pkgs.defpermbugapp') - config._set_authentication_policy(authn_policy) - config._set_authorization_policy(authz_policy) + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(authz_policy) config.set_default_permission('private') diff --git a/tests/pkgs/forbiddenapp/__init__.py b/tests/pkgs/forbiddenapp/__init__.py index 31ea4dd52..79670dd32 100644 --- a/tests/pkgs/forbiddenapp/__init__.py +++ b/tests/pkgs/forbiddenapp/__init__.py @@ -22,7 +22,7 @@ def includeme(config): authn_policy = AuthTktAuthenticationPolicy('seekr1t', hashalg='sha512') authz_policy = ACLAuthorizationPolicy() - config._set_authentication_policy(authn_policy) - config._set_authorization_policy(authz_policy) + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(authz_policy) config.add_view(x_view, name='x', permission='private') config.add_view(forbidden_view, context=HTTPForbidden) diff --git a/tests/pkgs/legacysecurityapp/__init__.py b/tests/pkgs/legacysecurityapp/__init__.py new file mode 100644 index 000000000..12fb6104e --- /dev/null +++ b/tests/pkgs/legacysecurityapp/__init__.py @@ -0,0 +1,37 @@ +from pyramid.response import Response +from pyramid.authentication import RemoteUserAuthenticationPolicy +from pyramid.security import Allowed, Denied + + +class AuthorizationPolicy: + def permits(self, context, principals, permission): + if 'bob' in principals and permission == 'foo': + return Allowed('') + else: + return Denied('') + + def principals_allowed_by_permission(self, context, permission): + raise NotImplementedError() # pragma: no cover + + +def public(context, request): + return Response('Hello') + + +def private(context, request): + return Response('Secret') + + +def inaccessible(context, request): + raise AssertionError() # pragma: no cover + + +def includeme(config): + config.set_authentication_policy(RemoteUserAuthenticationPolicy()) + config.set_authorization_policy(AuthorizationPolicy()) + config.add_route('public', '/public') + config.add_view(public, route_name='public') + config.add_route('private', '/private') + config.add_view(private, route_name='private', permission='foo') + config.add_route('inaccessible', '/inaccessible') + config.add_view(inaccessible, route_name='inaccessible', permission='bar') diff --git a/tests/pkgs/securityapp/__init__.py b/tests/pkgs/securityapp/__init__.py new file mode 100644 index 000000000..6ddba585b --- /dev/null +++ b/tests/pkgs/securityapp/__init__.py @@ -0,0 +1,41 @@ +from pyramid.response import Response +from pyramid.security import Allowed, Denied + + +class SecurityPolicy: + def identify(self, request): + return request.environ.get('REMOTE_USER') + + def permits(self, request, context, identity, permission): + if identity and permission == 'foo': + return Allowed('') + else: + return Denied('') + + def remember(self, request, userid, **kw): + raise NotImplementedError() # pragma: no cover + + def forget(self, request): + raise NotImplementedError() # pragma: no cover + + +def public(context, request): + return Response('Hello') + + +def private(context, request): + return Response('Secret') + + +def inaccessible(context, request): + raise AssertionError() # pragma: no cover + + +def includeme(config): + config.set_security_policy(SecurityPolicy()) + config.add_route('public', '/public') + config.add_view(public, route_name='public') + config.add_route('private', '/private') + config.add_view(private, route_name='private', permission='foo') + config.add_route('inaccessible', '/inaccessible') + config.add_view(inaccessible, route_name='inaccessible', permission='bar') diff --git a/tests/pkgs/staticpermapp/__init__.py b/tests/pkgs/staticpermapp/__init__.py index ffc87d39a..a12eac2d3 100644 --- a/tests/pkgs/staticpermapp/__init__.py +++ b/tests/pkgs/staticpermapp/__init__.py @@ -18,8 +18,8 @@ def includeme(config): authn_policy = RemoteUserAuthenticationPolicy() authz_policy = ACLAuthorizationPolicy() - config._set_authentication_policy(authn_policy) - config._set_authorization_policy(authz_policy) + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(authz_policy) config.add_static_view('allowed', 'tests:fixtures/static/') config.add_static_view( 'protected', 'tests:fixtures/static/', permission='view' diff --git a/tests/test_authentication.py b/tests/test_authentication.py index 8671eba05..710e87423 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -1693,6 +1693,56 @@ class TestSessionAuthenticationPolicy(unittest.TestCase): self.assertEqual(result, []) +class TestSessionAuthenticationHelper(unittest.TestCase): + def _makeRequest(self, session=None): + from types import SimpleNamespace + + if session is None: + session = dict() + return SimpleNamespace(session=session) + + def _makeOne(self, prefix=''): + from pyramid.authentication import SessionAuthenticationHelper + + return SessionAuthenticationHelper(prefix=prefix) + + def test_identify(self): + request = self._makeRequest({'userid': 'fred'}) + helper = self._makeOne() + self.assertEqual(helper.identify(request), 'fred') + + def test_identify_with_prefix(self): + request = self._makeRequest({'foo.userid': 'fred'}) + helper = self._makeOne(prefix='foo.') + self.assertEqual(helper.identify(request), 'fred') + + def test_identify_none(self): + request = self._makeRequest() + helper = self._makeOne() + self.assertEqual(helper.identify(request), None) + + def test_remember(self): + request = self._makeRequest() + helper = self._makeOne() + result = helper.remember(request, 'fred') + self.assertEqual(request.session.get('userid'), 'fred') + self.assertEqual(result, []) + + def test_forget(self): + request = self._makeRequest({'userid': 'fred'}) + helper = self._makeOne() + result = helper.forget(request) + self.assertEqual(request.session.get('userid'), None) + self.assertEqual(result, []) + + def test_forget_no_identity(self): + request = self._makeRequest() + helper = self._makeOne() + result = helper.forget(request) + self.assertEqual(request.session.get('userid'), None) + self.assertEqual(result, []) + + class TestBasicAuthAuthenticationPolicy(unittest.TestCase): def _getTargetClass(self): from pyramid.authentication import BasicAuthAuthenticationPolicy as cls diff --git a/tests/test_authorization.py b/tests/test_authorization.py index efb84b203..399b3da60 100644 --- a/tests/test_authorization.py +++ b/tests/test_authorization.py @@ -272,6 +272,267 @@ class TestACLAuthorizationPolicy(unittest.TestCase): self.assertTrue(result) +class TestACLHelper(unittest.TestCase): + def test_no_acl(self): + from pyramid.authorization import ACLHelper + + context = DummyContext() + helper = ACLHelper() + result = helper.permits(context, ['foo'], 'permission') + self.assertEqual(result, False) + self.assertEqual(result.ace, '<default deny>') + self.assertEqual( + result.acl, '<No ACL found on any object in resource lineage>' + ) + self.assertEqual(result.permission, 'permission') + self.assertEqual(result.principals, ['foo']) + self.assertEqual(result.context, context) + + def test_acl(self): + from pyramid.authorization import ACLHelper + from pyramid.security import Deny + from pyramid.security import Allow + from pyramid.security import Everyone + from pyramid.security import Authenticated + from pyramid.security import ALL_PERMISSIONS + from pyramid.security import DENY_ALL + + helper = ACLHelper() + root = DummyContext() + community = DummyContext(__name__='community', __parent__=root) + blog = DummyContext(__name__='blog', __parent__=community) + root.__acl__ = [(Allow, Authenticated, VIEW)] + community.__acl__ = [ + (Allow, 'fred', ALL_PERMISSIONS), + (Allow, 'wilma', VIEW), + DENY_ALL, + ] + blog.__acl__ = [ + (Allow, 'barney', MEMBER_PERMS), + (Allow, 'wilma', VIEW), + ] + + result = helper.permits( + blog, [Everyone, Authenticated, 'wilma'], 'view' + ) + self.assertEqual(result, True) + self.assertEqual(result.context, blog) + self.assertEqual(result.ace, (Allow, 'wilma', VIEW)) + self.assertEqual(result.acl, blog.__acl__) + + result = helper.permits( + blog, [Everyone, Authenticated, 'wilma'], 'delete' + ) + self.assertEqual(result, False) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = helper.permits( + blog, [Everyone, Authenticated, 'fred'], 'view' + ) + self.assertEqual(result, True) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS)) + result = helper.permits( + blog, [Everyone, Authenticated, 'fred'], 'doesntevenexistyet' + ) + self.assertEqual(result, True) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = helper.permits( + blog, [Everyone, Authenticated, 'barney'], 'view' + ) + self.assertEqual(result, True) + self.assertEqual(result.context, blog) + self.assertEqual(result.ace, (Allow, 'barney', MEMBER_PERMS)) + result = helper.permits( + blog, [Everyone, Authenticated, 'barney'], 'administer' + ) + self.assertEqual(result, False) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = helper.permits( + root, [Everyone, Authenticated, 'someguy'], 'view' + ) + self.assertEqual(result, True) + self.assertEqual(result.context, root) + self.assertEqual(result.ace, (Allow, Authenticated, VIEW)) + result = helper.permits( + blog, [Everyone, Authenticated, 'someguy'], 'view' + ) + self.assertEqual(result, False) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = helper.permits(root, [Everyone], 'view') + self.assertEqual(result, False) + self.assertEqual(result.context, root) + self.assertEqual(result.ace, '<default deny>') + self.assertEqual(result.acl, root.__acl__) + + context = DummyContext() + result = helper.permits(context, [Everyone], 'view') + self.assertEqual(result, False) + self.assertEqual(result.ace, '<default deny>') + self.assertEqual( + result.acl, '<No ACL found on any object in resource lineage>' + ) + + def test_string_permissions_in_acl(self): + from pyramid.authorization import ACLHelper + from pyramid.security import Allow + + helper = ACLHelper() + root = DummyContext() + root.__acl__ = [(Allow, 'wilma', 'view_stuff')] + + result = helper.permits(root, ['wilma'], 'view') + # would be True if matching against 'view_stuff' instead of against + # ['view_stuff'] + self.assertEqual(result, False) + + def test_callable_acl(self): + from pyramid.authorization import ACLHelper + from pyramid.security import Allow + + helper = ACLHelper() + context = DummyContext() + fn = lambda self: [(Allow, 'bob', 'read')] + context.__acl__ = fn.__get__(context, context.__class__) + result = helper.permits(context, ['bob'], 'read') + self.assertTrue(result) + + def test_principals_allowed_by_permission_direct(self): + from pyramid.authorization import ACLHelper + from pyramid.security import Allow + from pyramid.security import DENY_ALL + + helper = ACLHelper() + context = DummyContext() + acl = [ + (Allow, 'chrism', ('read', 'write')), + DENY_ALL, + (Allow, 'other', 'read'), + ] + context.__acl__ = acl + result = sorted( + helper.principals_allowed_by_permission(context, 'read') + ) + self.assertEqual(result, ['chrism']) + + def test_principals_allowed_by_permission_callable_acl(self): + from pyramid.authorization import ACLHelper + from pyramid.security import Allow + from pyramid.security import DENY_ALL + + helper = ACLHelper() + context = DummyContext() + acl = lambda: [ + (Allow, 'chrism', ('read', 'write')), + DENY_ALL, + (Allow, 'other', 'read'), + ] + context.__acl__ = acl + result = sorted( + helper.principals_allowed_by_permission(context, 'read') + ) + self.assertEqual(result, ['chrism']) + + def test_principals_allowed_by_permission_string_permission(self): + from pyramid.authorization import ACLHelper + from pyramid.security import Allow + + helper = ACLHelper() + context = DummyContext() + acl = [(Allow, 'chrism', 'read_it')] + context.__acl__ = acl + result = helper.principals_allowed_by_permission(context, 'read') + # would be ['chrism'] if 'read' were compared against 'read_it' instead + # of against ['read_it'] + self.assertEqual(list(result), []) + + def test_principals_allowed_by_permission(self): + from pyramid.authorization import ACLHelper + from pyramid.security import Allow + from pyramid.security import Deny + from pyramid.security import DENY_ALL + from pyramid.security import ALL_PERMISSIONS + + helper = ACLHelper() + root = DummyContext(__name__='', __parent__=None) + community = DummyContext(__name__='community', __parent__=root) + blog = DummyContext(__name__='blog', __parent__=community) + root.__acl__ = [ + (Allow, 'chrism', ('read', 'write')), + (Allow, 'other', ('read',)), + (Allow, 'jim', ALL_PERMISSIONS), + ] + community.__acl__ = [ + (Deny, 'flooz', 'read'), + (Allow, 'flooz', 'read'), + (Allow, 'mork', 'read'), + (Deny, 'jim', 'read'), + (Allow, 'someguy', 'manage'), + ] + blog.__acl__ = [(Allow, 'fred', 'read'), DENY_ALL] + + result = sorted(helper.principals_allowed_by_permission(blog, 'read')) + self.assertEqual(result, ['fred']) + result = sorted( + helper.principals_allowed_by_permission(community, 'read') + ) + self.assertEqual(result, ['chrism', 'mork', 'other']) + result = sorted( + helper.principals_allowed_by_permission(community, 'read') + ) + result = sorted(helper.principals_allowed_by_permission(root, 'read')) + self.assertEqual(result, ['chrism', 'jim', 'other']) + + def test_principals_allowed_by_permission_no_acls(self): + from pyramid.authorization import ACLHelper + + helper = ACLHelper() + context = DummyContext() + result = sorted( + helper.principals_allowed_by_permission(context, 'read') + ) + self.assertEqual(result, []) + + def test_principals_allowed_by_permission_deny_not_permission_in_acl(self): + from pyramid.authorization import ACLHelper + from pyramid.security import Deny + from pyramid.security import Everyone + + helper = ACLHelper() + context = DummyContext() + acl = [(Deny, Everyone, 'write')] + context.__acl__ = acl + result = sorted( + helper.principals_allowed_by_permission(context, 'read') + ) + self.assertEqual(result, []) + + def test_principals_allowed_by_permission_deny_permission_in_acl(self): + from pyramid.authorization import ACLHelper + from pyramid.security import Deny + from pyramid.security import Everyone + + helper = ACLHelper() + context = DummyContext() + acl = [(Deny, Everyone, 'read')] + context.__acl__ = acl + result = sorted( + helper.principals_allowed_by_permission(context, 'read') + ) + self.assertEqual(result, []) + + class DummyContext: def __init__(self, *arg, **kw): self.__dict__.update(kw) diff --git a/tests/test_config/test_init.py b/tests/test_config/test_init.py index ce2b042ec..661654ef0 100644 --- a/tests/test_config/test_init.py +++ b/tests/test_config/test_init.py @@ -205,6 +205,15 @@ class ConfiguratorTests(unittest.TestCase): result = config.registry.getUtility(IDebugLogger) self.assertEqual(logger, result) + def test_ctor_security_policy(self): + from pyramid.interfaces import ISecurityPolicy + + policy = object() + config = self._makeOne(security_policy=policy) + config.commit() + result = config.registry.getUtility(ISecurityPolicy) + self.assertEqual(policy, result) + def test_ctor_authentication_policy(self): from pyramid.interfaces import IAuthenticationPolicy diff --git a/tests/test_config/test_security.py b/tests/test_config/test_security.py index 6257960b8..0ae199239 100644 --- a/tests/test_config/test_security.py +++ b/tests/test_config/test_security.py @@ -11,6 +11,28 @@ class ConfiguratorSecurityMethodsTests(unittest.TestCase): config = Configurator(*arg, **kw) return config + def test_set_security_policy(self): + from pyramid.interfaces import ISecurityPolicy + + config = self._makeOne() + policy = object() + config.set_security_policy(policy) + config.commit() + self.assertEqual(config.registry.getUtility(ISecurityPolicy), policy) + + def test_set_authentication_policy_with_security_policy(self): + from pyramid.interfaces import IAuthorizationPolicy + from pyramid.interfaces import ISecurityPolicy + + config = self._makeOne() + security_policy = object() + authn_policy = object() + authz_policy = object() + config.registry.registerUtility(security_policy, ISecurityPolicy) + config.registry.registerUtility(authz_policy, IAuthorizationPolicy) + config.set_authentication_policy(authn_policy) + self.assertRaises(ConfigurationError, config.commit) + def test_set_authentication_policy_no_authz_policy(self): config = self._makeOne() policy = object() @@ -27,6 +49,8 @@ class ConfiguratorSecurityMethodsTests(unittest.TestCase): def test_set_authentication_policy_with_authz_policy(self): from pyramid.interfaces import IAuthenticationPolicy from pyramid.interfaces import IAuthorizationPolicy + from pyramid.interfaces import ISecurityPolicy + from pyramid.security import LegacySecurityPolicy config = self._makeOne() authn_policy = object() @@ -37,10 +61,15 @@ class ConfiguratorSecurityMethodsTests(unittest.TestCase): self.assertEqual( config.registry.getUtility(IAuthenticationPolicy), authn_policy ) + self.assertIsInstance( + config.registry.getUtility(ISecurityPolicy), LegacySecurityPolicy + ) def test_set_authentication_policy_with_authz_policy_autocommit(self): from pyramid.interfaces import IAuthenticationPolicy from pyramid.interfaces import IAuthorizationPolicy + from pyramid.interfaces import ISecurityPolicy + from pyramid.security import LegacySecurityPolicy config = self._makeOne(autocommit=True) authn_policy = object() @@ -51,6 +80,9 @@ class ConfiguratorSecurityMethodsTests(unittest.TestCase): self.assertEqual( config.registry.getUtility(IAuthenticationPolicy), authn_policy ) + self.assertIsInstance( + config.registry.getUtility(ISecurityPolicy), LegacySecurityPolicy + ) def test_set_authorization_policy_no_authn_policy(self): config = self._makeOne() diff --git a/tests/test_config/test_testing.py b/tests/test_config/test_testing.py index 0fb73d268..500aedeae 100644 --- a/tests/test_config/test_testing.py +++ b/tests/test_config/test_testing.py @@ -1,7 +1,7 @@ import unittest from zope.interface import implementer -from pyramid.security import AuthenticationAPIMixin, AuthorizationAPIMixin +from pyramid.security import SecurityAPIMixin, AuthenticationAPIMixin from pyramid.util import text_ from . import IDummy @@ -17,28 +17,20 @@ class TestingConfiguratorMixinTests(unittest.TestCase): from pyramid.testing import DummySecurityPolicy config = self._makeOne(autocommit=True) - config.testing_securitypolicy( - 'user', ('group1', 'group2'), permissive=False - ) - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy + config.testing_securitypolicy('user', permissive=False) + from pyramid.interfaces import ISecurityPolicy - ut = config.registry.getUtility(IAuthenticationPolicy) - self.assertTrue(isinstance(ut, DummySecurityPolicy)) - ut = config.registry.getUtility(IAuthorizationPolicy) - self.assertEqual(ut.userid, 'user') - self.assertEqual(ut.groupids, ('group1', 'group2')) - self.assertEqual(ut.permissive, False) + policy = config.registry.getUtility(ISecurityPolicy) + self.assertTrue(isinstance(policy, DummySecurityPolicy)) + self.assertEqual(policy.identity, 'user') + self.assertEqual(policy.permissive, False) def test_testing_securitypolicy_remember_result(self): from pyramid.security import remember config = self._makeOne(autocommit=True) pol = config.testing_securitypolicy( - 'user', - ('group1', 'group2'), - permissive=False, - remember_result=True, + 'user', permissive=False, remember_result=True ) request = DummyRequest() request.registry = config.registry @@ -51,7 +43,7 @@ class TestingConfiguratorMixinTests(unittest.TestCase): config = self._makeOne(autocommit=True) pol = config.testing_securitypolicy( - 'user', ('group1', 'group2'), permissive=False, forget_result=True + 'user', permissive=False, forget_result=True ) request = DummyRequest() request.registry = config.registry @@ -232,7 +224,7 @@ class DummyEvent: pass -class DummyRequest(AuthenticationAPIMixin, AuthorizationAPIMixin): +class DummyRequest(SecurityAPIMixin, AuthenticationAPIMixin): def __init__(self, environ=None): if environ is None: environ = {} diff --git a/tests/test_config/test_views.py b/tests/test_config/test_views.py index 685b81a0f..28b7a9fb1 100644 --- a/tests/test_config/test_views.py +++ b/tests/test_config/test_views.py @@ -2059,22 +2059,19 @@ class TestViewsConfigurationMixin(unittest.TestCase): outerself = self class DummyPolicy(object): - def effective_principals(self, r): + def identify(self, r): outerself.assertEqual(r, request) - return ['abc'] + return 123 - def permits(self, context, principals, permission): + def permits(self, r, context, identity, permission): + outerself.assertEqual(r, request) outerself.assertEqual(context, None) - outerself.assertEqual(principals, ['abc']) + outerself.assertEqual(identity, 123) outerself.assertEqual(permission, 'view') return True policy = DummyPolicy() - config = self._makeOne( - authorization_policy=policy, - authentication_policy=policy, - autocommit=True, - ) + config = self._makeOne(security_policy=policy, autocommit=True) config.add_view(view=view1, permission='view', renderer=null_renderer) view = self._getViewCallable(config) request = self._makeRequest(config) @@ -2087,22 +2084,20 @@ class TestViewsConfigurationMixin(unittest.TestCase): outerself = self class DummyPolicy(object): - def effective_principals(self, r): + def identify(self, r): outerself.assertEqual(r, request) - return ['abc'] + return 123 - def permits(self, context, principals, permission): + def permits(self, r, context, identity, permission): + outerself.assertEqual(r, request) outerself.assertEqual(context, None) - outerself.assertEqual(principals, ['abc']) + outerself.assertEqual(identity, 123) outerself.assertEqual(permission, 'view') return True policy = DummyPolicy() config = self._makeOne( - authorization_policy=policy, - authentication_policy=policy, - default_permission='view', - autocommit=True, + security_policy=policy, default_permission='view', autocommit=True ) config.add_view(view=view1, renderer=null_renderer) view = self._getViewCallable(config) diff --git a/tests/test_integration.py b/tests/test_integration.py index e6dccbb5b..331542d7d 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -521,6 +521,48 @@ class TestExceptionViewsApp(IntegrationBase, unittest.TestCase): self.assertTrue(b'caught' in res.body) +class TestSecurityApp(IntegrationBase, unittest.TestCase): + package = 'tests.pkgs.securityapp' + + def test_public(self): + res = self.testapp.get('/public', status=200) + self.assertEqual(res.body, b'Hello') + + def test_private_denied(self): + self.testapp.get('/private', status=403) + + def test_private_allowed(self): + self.testapp.extra_environ = {'REMOTE_USER': 'bob'} + res = self.testapp.get('/private', status=200) + self.assertEqual(res.body, b'Secret') + + def test_inaccessible(self): + self.testapp.get('/inaccessible', status=403) + self.testapp.extra_environ = {'REMOTE_USER': 'bob'} + self.testapp.get('/inaccessible', status=403) + + +class TestLegacySecurityApp(IntegrationBase, unittest.TestCase): + package = 'tests.pkgs.legacysecurityapp' + + def test_public(self): + res = self.testapp.get('/public', status=200) + self.assertEqual(res.body, b'Hello') + + def test_private_denied(self): + self.testapp.get('/private', status=403) + + def test_private_allowed(self): + self.testapp.extra_environ = {'REMOTE_USER': 'bob'} + res = self.testapp.get('/private', status=200) + self.assertEqual(res.body, b'Secret') + + def test_inaccessible(self): + self.testapp.get('/inaccessible', status=403) + self.testapp.extra_environ = {'REMOTE_USER': 'bob'} + self.testapp.get('/inaccessible', status=403) + + class TestConflictApp(unittest.TestCase): package = 'tests.pkgs.conflictapp' @@ -581,10 +623,12 @@ class TestConflictApp(unittest.TestCase): def test_overridden_authorization_policy(self): config = self._makeConfig() config.include(self.package) - from pyramid.testing import DummySecurityPolicy - config.set_authorization_policy(DummySecurityPolicy('fred')) - config.set_authentication_policy(DummySecurityPolicy(permissive=True)) + class DummySecurityPolicy: + def permits(self, context, principals, permission): + return True + + config.set_authorization_policy(DummySecurityPolicy()) app = config.make_wsgi_app() self.testapp = TestApp(app) res = self.testapp.get('/protected', status=200) diff --git a/tests/test_predicates.py b/tests/test_predicates.py index a99651a8f..60e36047e 100644 --- a/tests/test_predicates.py +++ b/tests/test_predicates.py @@ -502,6 +502,22 @@ class Test_EffectivePrincipalsPredicate(unittest.TestCase): return EffectivePrincipalsPredicate(val, config) + def _testing_authn_policy(self, userid, groupids=tuple()): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.security import Everyone, Authenticated + + class DummyPolicy: + def effective_principals(self, request): + p = [Everyone] + if userid: + p.append(Authenticated) + p.append(userid) + p.extend(groupids) + return p + + registry = self.config.registry + registry.registerUtility(DummyPolicy(), IAuthenticationPolicy) + def test_text(self): inst = self._makeOne(('verna', 'fred'), None) self.assertEqual( @@ -526,7 +542,7 @@ class Test_EffectivePrincipalsPredicate(unittest.TestCase): def test_it_call_authentication_policy_provides_superset(self): request = testing.DummyRequest() - self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi')) + self._testing_authn_policy('fred', groupids=('verna', 'bambi')) inst = self._makeOne(('verna', 'fred'), None) context = Dummy() self.assertTrue(inst(context, request)) @@ -535,14 +551,14 @@ class Test_EffectivePrincipalsPredicate(unittest.TestCase): from pyramid.security import Authenticated request = testing.DummyRequest() - self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi')) + self._testing_authn_policy('fred', groupids=('verna', 'bambi')) inst = self._makeOne(Authenticated, None) context = Dummy() self.assertTrue(inst(context, request)) def test_it_call_authentication_policy_doesnt_provide_superset(self): request = testing.DummyRequest() - self.config.testing_securitypolicy('fred') + self._testing_authn_policy('fred') inst = self._makeOne(('verna', 'fred'), None) context = Dummy() self.assertFalse(inst(context, request)) diff --git a/tests/test_request.py b/tests/test_request.py index 484d86e01..1a10a8509 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -1,7 +1,7 @@ import unittest from pyramid import testing -from pyramid.security import AuthenticationAPIMixin, AuthorizationAPIMixin +from pyramid.security import SecurityAPIMixin, AuthenticationAPIMixin from pyramid.util import text_, bytes_ @@ -54,7 +54,7 @@ class TestRequest(unittest.TestCase): self.assertEqual(cls.ResponseClass, Response) def test_implements_security_apis(self): - apis = (AuthenticationAPIMixin, AuthorizationAPIMixin) + apis = (SecurityAPIMixin, AuthenticationAPIMixin) r = self._makeOne() self.assertTrue(isinstance(r, apis)) diff --git a/tests/test_security.py b/tests/test_security.py index 8b8028f61..2a8847f3b 100644 --- a/tests/test_security.py +++ b/tests/test_security.py @@ -187,32 +187,22 @@ class TestRemember(unittest.TestCase): return remember(*arg, **kwarg) - def test_no_authentication_policy(self): + def test_no_security_policy(self): request = _makeRequest() result = self._callFUT(request, 'me') self.assertEqual(result, []) - def test_with_authentication_policy(self): + def test_with_security_policy(self): request = _makeRequest() registry = request.registry - _registerAuthenticationPolicy(registry, 'yo') - result = self._callFUT(request, 'me') - self.assertEqual(result, [('X-Pyramid-Test', 'me')]) - - def test_with_authentication_policy_no_reg_on_request(self): - from pyramid.threadlocal import get_current_registry - - registry = get_current_registry() - request = _makeRequest() - del request.registry - _registerAuthenticationPolicy(registry, 'yo') + _registerSecurityPolicy(registry, 'yo') result = self._callFUT(request, 'me') self.assertEqual(result, [('X-Pyramid-Test', 'me')]) def test_with_missing_arg(self): request = _makeRequest() registry = request.registry - _registerAuthenticationPolicy(registry, 'yo') + _registerSecurityPolicy(registry, 'yo') self.assertRaises(TypeError, lambda: self._callFUT(request)) @@ -228,24 +218,14 @@ class TestForget(unittest.TestCase): return forget(*arg) - def test_no_authentication_policy(self): + def test_no_security_policy(self): request = _makeRequest() result = self._callFUT(request) self.assertEqual(result, []) - def test_with_authentication_policy(self): + def test_with_security_policy(self): request = _makeRequest() - _registerAuthenticationPolicy(request.registry, 'yo') - result = self._callFUT(request) - self.assertEqual(result, [('X-Pyramid-Test', 'logout')]) - - def test_with_authentication_policy_no_reg_on_request(self): - from pyramid.threadlocal import get_current_registry - - registry = get_current_registry() - request = _makeRequest() - del request.registry - _registerAuthenticationPolicy(registry, 'yo') + _registerSecurityPolicy(request.registry, 'yo') result = self._callFUT(request) self.assertEqual(result, [('X-Pyramid-Test', 'logout')]) @@ -338,6 +318,23 @@ class TestViewExecutionPermitted(unittest.TestCase): self.assertTrue(result) +class TestAuthenticatedIdentity(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def test_identity_no_security_policy(self): + request = _makeRequest() + self.assertEquals(request.authenticated_identity, None) + + def test_identity(self): + request = _makeRequest() + _registerSecurityPolicy(request.registry, 'yo') + self.assertEqual(request.authenticated_identity, 'yo') + + class TestAuthenticatedUserId(unittest.TestCase): def setUp(self): testing.setUp() @@ -352,8 +349,15 @@ class TestAuthenticatedUserId(unittest.TestCase): def test_with_authentication_policy(self): request = _makeRequest() _registerAuthenticationPolicy(request.registry, 'yo') + _registerSecurityPolicy(request.registry, 'wat') self.assertEqual(request.authenticated_userid, 'yo') + def test_with_security_policy(self): + request = _makeRequest() + # Ensure the identity is stringified. + _registerSecurityPolicy(request.registry, 123) + self.assertEqual(request.authenticated_userid, '123') + def test_with_authentication_policy_no_reg_on_request(self): from pyramid.threadlocal import get_current_registry @@ -378,6 +382,12 @@ class TestUnAuthenticatedUserId(unittest.TestCase): def test_with_authentication_policy(self): request = _makeRequest() _registerAuthenticationPolicy(request.registry, 'yo') + _registerSecurityPolicy(request.registry, 'wat') + self.assertEqual(request.unauthenticated_userid, 'yo') + + def test_with_security_policy(self): + request = _makeRequest() + _registerSecurityPolicy(request.registry, 'yo') self.assertEqual(request.unauthenticated_userid, 'yo') def test_with_authentication_policy_no_reg_on_request(self): @@ -426,43 +436,25 @@ class TestHasPermission(unittest.TestCase): testing.tearDown() def _makeOne(self): - from pyramid.security import AuthorizationAPIMixin + from pyramid.security import SecurityAPIMixin from pyramid.registry import Registry - mixin = AuthorizationAPIMixin() + mixin = SecurityAPIMixin() mixin.registry = Registry() mixin.context = object() return mixin - def test_no_authentication_policy(self): + def test_no_security_policy(self): request = self._makeOne() result = request.has_permission('view') self.assertTrue(result) - self.assertEqual(result.msg, 'No authentication policy in use.') + self.assertEqual(result.msg, 'No security policy in use.') - def test_with_no_authorization_policy(self): + def test_with_security_registered(self): request = self._makeOne() - _registerAuthenticationPolicy(request.registry, None) - self.assertRaises( - ValueError, request.has_permission, 'view', context=None - ) - - def test_with_authn_and_authz_policies_registered(self): - request = self._makeOne() - _registerAuthenticationPolicy(request.registry, None) - _registerAuthorizationPolicy(request.registry, 'yo') + _registerSecurityPolicy(request.registry, 'yo') self.assertEqual(request.has_permission('view', context=None), 'yo') - def test_with_no_reg_on_request(self): - from pyramid.threadlocal import get_current_registry - - registry = get_current_registry() - request = self._makeOne() - del request.registry - _registerAuthenticationPolicy(registry, None) - _registerAuthorizationPolicy(registry, 'yo') - self.assertEqual(request.has_permission('view'), 'yo') - def test_with_no_context_passed(self): request = self._makeOne() self.assertTrue(request.has_permission('view')) @@ -473,6 +465,58 @@ class TestHasPermission(unittest.TestCase): self.assertRaises(AttributeError, request.has_permission, 'view') +class TestLegacySecurityPolicy(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def test_identity(self): + from pyramid.security import LegacySecurityPolicy + + request = _makeRequest() + policy = LegacySecurityPolicy() + _registerAuthenticationPolicy(request.registry, 'userid') + + self.assertEqual(policy.identify(request), 'userid') + + def test_remember(self): + from pyramid.security import LegacySecurityPolicy + + request = _makeRequest() + policy = LegacySecurityPolicy() + _registerAuthenticationPolicy(request.registry, None) + + self.assertEqual( + policy.remember(request, 'userid'), [('X-Pyramid-Test', 'userid')] + ) + + def test_forget(self): + from pyramid.security import LegacySecurityPolicy + + request = _makeRequest() + policy = LegacySecurityPolicy() + _registerAuthenticationPolicy(request.registry, None) + + self.assertEqual( + policy.forget(request), [('X-Pyramid-Test', 'logout')] + ) + + def test_permits(self): + from pyramid.security import LegacySecurityPolicy + + request = _makeRequest() + policy = LegacySecurityPolicy() + _registerAuthenticationPolicy(request.registry, ['p1', 'p2']) + _registerAuthorizationPolicy(request.registry, True) + + self.assertIs( + policy.permits(request, request.context, 'userid', 'permission'), + True, + ) + + _TEST_HEADER = 'X-Pyramid-Test' @@ -481,6 +525,27 @@ class DummyContext: self.__dict__.update(kw) +class DummySecurityPolicy: + def __init__(self, result): + self.result = result + + def identify(self, request): + return self.result + + def permits(self, request, context, identity, permission): + return self.result + + def remember(self, request, userid, **kw): + headers = [(_TEST_HEADER, userid)] + self._header_remembered = headers[0] + return headers + + def forget(self, request): + headers = [(_TEST_HEADER, 'logout')] + self._header_forgotten = headers[0] + return headers + + class DummyAuthenticationPolicy: def __init__(self, result): self.result = result @@ -516,6 +581,14 @@ class DummyAuthorizationPolicy: return self.result +def _registerSecurityPolicy(reg, result): + from pyramid.interfaces import ISecurityPolicy + + policy = DummySecurityPolicy(result) + reg.registerUtility(policy, ISecurityPolicy) + return policy + + def _registerAuthenticationPolicy(reg, result): from pyramid.interfaces import IAuthenticationPolicy diff --git a/tests/test_testing.py b/tests/test_testing.py index ddcb35612..ebeafe21d 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -23,52 +23,17 @@ class TestDummySecurityPolicy(unittest.TestCase): return DummySecurityPolicy - def _makeOne(self, userid=None, groupids=(), permissive=True): + def _makeOne(self, identity=None, permissive=True): klass = self._getTargetClass() - return klass(userid, groupids, permissive) + return klass(identity, permissive) - def test_authenticated_userid(self): + def test_identify(self): policy = self._makeOne('user') - self.assertEqual(policy.authenticated_userid(None), 'user') - - def test_unauthenticated_userid(self): - policy = self._makeOne('user') - self.assertEqual(policy.unauthenticated_userid(None), 'user') - - def test_effective_principals_userid(self): - policy = self._makeOne('user', ('group1',)) - from pyramid.security import Everyone - from pyramid.security import Authenticated - - self.assertEqual( - policy.effective_principals(None), - [Everyone, Authenticated, 'user', 'group1'], - ) - - def test_effective_principals_nouserid(self): - policy = self._makeOne() - from pyramid.security import Everyone - - self.assertEqual(policy.effective_principals(None), [Everyone]) + self.assertEqual(policy.identify(None), 'user') def test_permits(self): policy = self._makeOne() - self.assertEqual(policy.permits(None, None, None), True) - - def test_principals_allowed_by_permission(self): - policy = self._makeOne('user', ('group1',)) - from pyramid.security import Everyone - from pyramid.security import Authenticated - - result = policy.principals_allowed_by_permission(None, None) - self.assertEqual(result, [Everyone, Authenticated, 'user', 'group1']) - - def test_principals_allowed_by_permission_not_permissive(self): - policy = self._makeOne('user', ('group1',)) - policy.permissive = False - - result = policy.principals_allowed_by_permission(None, None) - self.assertEqual(result, []) + self.assertEqual(policy.permits(None, None, None, None), True) def test_forget(self): policy = self._makeOne() diff --git a/tests/test_viewderivers.py b/tests/test_viewderivers.py index 3ca5f8534..12a903eaa 100644 --- a/tests/test_viewderivers.py +++ b/tests/test_viewderivers.py @@ -28,12 +28,11 @@ class TestDeriveView(unittest.TestCase): return logger def _registerSecurityPolicy(self, permissive): - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy + from pyramid.interfaces import ISecurityPolicy policy = DummySecurityPolicy(permissive) - self.config.registry.registerUtility(policy, IAuthenticationPolicy) - self.config.registry.registerUtility(policy, IAuthorizationPolicy) + self.config.registry.registerUtility(policy, ISecurityPolicy) + return policy def test_function_returns_non_adaptable(self): def view(request): @@ -421,7 +420,7 @@ class TestDeriveView(unittest.TestCase): self.assertFalse(hasattr(result, '__call_permissive__')) self.assertEqual(result(None, None), response) - def test_with_debug_authorization_no_authpol(self): + def test_with_debug_authorization_no_security_policy(self): response = DummyResponse() view = lambda *arg: response self.config.registry.settings = dict( @@ -442,59 +441,7 @@ class TestDeriveView(unittest.TestCase): logger.messages[0], "debug_authorization of url url (view name " "'view_name' against context None): Allowed " - "(no authorization policy in use)", - ) - - def test_with_debug_authorization_authn_policy_no_authz_policy(self): - response = DummyResponse() - view = lambda *arg: response - self.config.registry.settings = dict(debug_authorization=True) - from pyramid.interfaces import IAuthenticationPolicy - - policy = DummySecurityPolicy(False) - self.config.registry.registerUtility(policy, IAuthenticationPolicy) - logger = self._registerLogger() - result = self.config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertFalse(hasattr(result, '__call_permissive__')) - request = self._makeRequest() - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), response) - self.assertEqual(len(logger.messages), 1) - self.assertEqual( - logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): Allowed " - "(no authorization policy in use)", - ) - - def test_with_debug_authorization_authz_policy_no_authn_policy(self): - response = DummyResponse() - view = lambda *arg: response - self.config.registry.settings = dict(debug_authorization=True) - from pyramid.interfaces import IAuthorizationPolicy - - policy = DummySecurityPolicy(False) - self.config.registry.registerUtility(policy, IAuthorizationPolicy) - logger = self._registerLogger() - result = self.config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertFalse(hasattr(result, '__call_permissive__')) - request = self._makeRequest() - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), response) - self.assertEqual(len(logger.messages), 1) - self.assertEqual( - logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): Allowed " - "(no authorization policy in use)", + "(no security policy in use)", ) def test_with_debug_authorization_no_permission(self): @@ -665,32 +612,11 @@ class TestDeriveView(unittest.TestCase): "'view_name' against context Exception()): True", ) - def test_secured_view_authn_policy_no_authz_policy(self): + def test_secured_view_authn_policy_no_security_policy(self): response = DummyResponse() view = lambda *arg: response self.config.registry.settings = {} - from pyramid.interfaces import IAuthenticationPolicy - policy = DummySecurityPolicy(False) - self.config.registry.registerUtility(policy, IAuthenticationPolicy) - result = self.config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertFalse(hasattr(result, '__call_permissive__')) - request = self._makeRequest() - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), response) - - def test_secured_view_authz_policy_no_authn_policy(self): - response = DummyResponse() - view = lambda *arg: response - self.config.registry.settings = {} - from pyramid.interfaces import IAuthorizationPolicy - - policy = DummySecurityPolicy(False) - self.config.registry.registerUtility(policy, IAuthorizationPolicy) result = self.config._derive_view(view, permission='view') self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) @@ -702,53 +628,41 @@ class TestDeriveView(unittest.TestCase): self.assertEqual(result(None, request), response) def test_secured_view_raises_forbidden_no_name(self): - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy from pyramid.httpexceptions import HTTPForbidden response = DummyResponse() view = lambda *arg: response self.config.registry.settings = {} - policy = DummySecurityPolicy(False) - self.config.registry.registerUtility(policy, IAuthenticationPolicy) - self.config.registry.registerUtility(policy, IAuthorizationPolicy) + self._registerSecurityPolicy(False) result = self.config._derive_view(view, permission='view') request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' - try: + with self.assertRaises(HTTPForbidden) as cm: result(None, request) - except HTTPForbidden as e: - self.assertEqual( - e.message, 'Unauthorized: <lambda> failed permission check' - ) - else: # pragma: no cover - raise AssertionError + self.assertEqual( + cm.exception.message, + 'Unauthorized: <lambda> failed permission check', + ) def test_secured_view_raises_forbidden_with_name(self): - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy from pyramid.httpexceptions import HTTPForbidden def myview(request): # pragma: no cover pass self.config.registry.settings = {} - policy = DummySecurityPolicy(False) - self.config.registry.registerUtility(policy, IAuthenticationPolicy) - self.config.registry.registerUtility(policy, IAuthorizationPolicy) + self._registerSecurityPolicy(False) result = self.config._derive_view(myview, permission='view') request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' - try: + with self.assertRaises(HTTPForbidden) as cm: result(None, request) - except HTTPForbidden as e: - self.assertEqual( - e.message, 'Unauthorized: myview failed permission check' - ) - else: # pragma: no cover - raise AssertionError + self.assertEqual( + cm.exception.message, + 'Unauthorized: myview failed permission check', + ) def test_secured_view_skipped_by_default_on_exception_view(self): from pyramid.request import Request @@ -794,12 +708,8 @@ class TestDeriveView(unittest.TestCase): app = self.config.make_wsgi_app() request = Request.blank('/foo', base_url='http://example.com') request.method = 'POST' - try: + with self.assertRaises(HTTPForbidden): request.get_response(app) - except HTTPForbidden: - pass - else: # pragma: no cover - raise AssertionError def test_secured_view_passed_on_explicit_exception_view(self): from pyramid.request import Request @@ -2151,10 +2061,10 @@ class DummySecurityPolicy: def __init__(self, permitted=True): self.permitted = permitted - def effective_principals(self, request): - return [] + def identify(self, request): + return 123 - def permits(self, context, principals, permission): + def permits(self, request, context, identity, permission): return self.permitted |
