diff options
author | Ryan Petrello <lists@ryanpetrello.com> | 2013-10-16 10:42:56 -0400 |
---|---|---|
committer | Ryan Petrello <lists@ryanpetrello.com> | 2013-10-16 10:49:21 -0400 |
commit | 676606981efe3b5e8ce4f5bc1ce04629bd616255 (patch) | |
tree | 35fcb4dd9e320d8036b2352d73628865f29f590b | |
parent | 55075fb8cf58b0c4ba029cd097232118b079185c (diff) | |
download | pecan-676606981efe3b5e8ce4f5bc1ce04629bd616255.tar.gz |
Respect security for generic controllers.
Fixes-bug: 1240488
Change-Id: I80cdc4609cb1e977018721db3e37d1d0e217bde2
-rw-r--r-- | pecan/core.py | 2 | ||||
-rw-r--r-- | pecan/secure.py | 5 | ||||
-rw-r--r-- | pecan/tests/test_secure.py | 128 |
3 files changed, 132 insertions, 3 deletions
diff --git a/pecan/core.py b/pecan/core.py index 6fdf024..7f4b8a9 100644 --- a/pecan/core.py +++ b/pecan/core.py @@ -14,6 +14,7 @@ import six from webob import Request, Response, exc, acceptparse from .compat import urlparse, unquote_plus, izip +from .secure import handle_security from .templating import RendererFactory from .routing import lookup_controller, NonCanonicalPath from .util import _cfg, encode_if_needed @@ -428,6 +429,7 @@ class Pecan(object): im_self = six.get_method_self(controller) handlers = cfg['generic_handlers'] controller = handlers.get(req.method, handlers['DEFAULT']) + handle_security(controller, im_self) cfg = _cfg(controller) # add the controller to the state so that hooks can use it diff --git a/pecan/secure.py b/pecan/secure.py index e44aacb..62b82cb 100644 --- a/pecan/secure.py +++ b/pecan/secure.py @@ -119,7 +119,6 @@ def secure(func_or_obj, check_permissions_for_obj=None): To secure a class, invoke with two arguments: secure(<obj instance>, <check_permissions_method>) """ - if _allowed_check_permissions_types(func_or_obj): return _secure_method(func_or_obj) else: @@ -199,14 +198,14 @@ def _make_wrapper(f): # methods to evaluate security during routing -def handle_security(controller): +def handle_security(controller, im_self=None): """ Checks the security of a controller. """ if controller._pecan.get('secured', False): check_permissions = controller._pecan['check_permissions'] if isinstance(check_permissions, six.string_types): check_permissions = getattr( - six.get_method_self(controller), + im_self or six.get_method_self(controller), check_permissions ) diff --git a/pecan/tests/test_secure.py b/pecan/tests/test_secure.py index 53a63ca..0f16a98 100644 --- a/pecan/tests/test_secure.py +++ b/pecan/tests/test_secure.py @@ -174,6 +174,134 @@ class TestSecure(PecanTestCase): assert response.status_int == 200 assert response.body == b_('Hello from sub!') + def test_secured_generic_controller(self): + authorized = False + + class RootController(object): + + @classmethod + def check_permissions(cls): + return authorized + + @expose(generic=True) + def index(self): + return 'Index' + + @secure('check_permissions') + @index.when(method='POST') + def index_post(self): + return 'I should not be allowed' + + @secure('check_permissions') + @expose(generic=True) + def secret(self): + return 'I should not be allowed' + + app = TestApp(make_app( + RootController(), + debug=True, + static_root='tests/static' + )) + response = app.get('/') + assert response.status_int == 200 + response = app.post('/', expect_errors=True) + assert response.status_int == 401 + response = app.get('/secret/', expect_errors=True) + assert response.status_int == 401 + + def test_secured_generic_controller_lambda(self): + authorized = False + + class RootController(object): + + @expose(generic=True) + def index(self): + return 'Index' + + @secure(lambda: authorized) + @index.when(method='POST') + def index_post(self): + return 'I should not be allowed' + + @secure(lambda: authorized) + @expose(generic=True) + def secret(self): + return 'I should not be allowed' + + app = TestApp(make_app( + RootController(), + debug=True, + static_root='tests/static' + )) + response = app.get('/') + assert response.status_int == 200 + response = app.post('/', expect_errors=True) + assert response.status_int == 401 + response = app.get('/secret/', expect_errors=True) + assert response.status_int == 401 + + def test_secured_generic_controller_secure_attribute(self): + authorized = False + + class SecureController(object): + + @expose(generic=True) + def index(self): + return 'I should not be allowed' + + @index.when(method='POST') + def index_post(self): + return 'I should not be allowed' + + @expose(generic=True) + def secret(self): + return 'I should not be allowed' + + class RootController(object): + sub = secure(SecureController(), lambda: authorized) + + app = TestApp(make_app( + RootController(), + debug=True, + static_root='tests/static' + )) + response = app.get('/sub/', expect_errors=True) + assert response.status_int == 401 + response = app.post('/sub/', expect_errors=True) + assert response.status_int == 401 + response = app.get('/sub/secret/', expect_errors=True) + assert response.status_int == 401 + + def test_secured_generic_controller_secure_attribute_with_unlocked(self): + + class RootController(SecureController): + + @unlocked + @expose(generic=True) + def index(self): + return 'Unlocked!' + + @unlocked + @index.when(method='POST') + def index_post(self): + return 'Unlocked!' + + @expose(generic=True) + def secret(self): + return 'I should not be allowed' + + app = TestApp(make_app( + RootController(), + debug=True, + static_root='tests/static' + )) + response = app.get('/') + assert response.status_int == 200 + response = app.post('/') + assert response.status_int == 200 + response = app.get('/secret/', expect_errors=True) + assert response.status_int == 401 + def test_state_attribute(self): from pecan.secure import Any, Protected assert repr(Any) == '<SecureState Any>' |