Added UnlockedControllerMeta, a controller metaclass which can be used to forcibly apply @unlocked() to every exposed method in a routing branch.
This commit is contained in:
@@ -40,10 +40,41 @@ def walk_controller(root_class, controller):
|
||||
|
||||
|
||||
class SecureController(object):
|
||||
"""
|
||||
Used to apply security to a controller and its children.
|
||||
Implementations of SecureController should extend the
|
||||
`check_permissions` method to return a True or False
|
||||
value (depending on whether or not the user has access
|
||||
to the controller).
|
||||
"""
|
||||
class __metaclass__(type):
|
||||
def __init__(cls, name, bases, dict_):
|
||||
walk_controller(cls, cls)
|
||||
|
||||
@classmethod
|
||||
def check_permissions(cls):
|
||||
return True
|
||||
return True
|
||||
|
||||
|
||||
class UnlockedControllerMeta(type):
|
||||
"""
|
||||
Can be used to force (override) a controller and all of its
|
||||
subcontrollers to be unlocked/unsecured.
|
||||
|
||||
This has the same effect as applying @pecan.secure.unlocked
|
||||
to every method in the class and its subclasses.
|
||||
"""
|
||||
def __init__(cls, name, bases, ns):
|
||||
cls.walk_and_apply_unlocked(cls, cls)
|
||||
|
||||
def walk_and_apply_unlocked(cls, root_class, controller):
|
||||
if not isinstance(controller, (int, dict)):
|
||||
for name, value in getmembers(controller):
|
||||
if name == 'controller': continue
|
||||
|
||||
if ismethod(value):
|
||||
if iscontroller(value):
|
||||
value = unlocked(value)
|
||||
elif hasattr(value, '__class__'):
|
||||
if name.startswith('__') and name.endswith('__'): continue
|
||||
cls.walk_and_apply_unlocked(root_class, value)
|
||||
@@ -1,8 +1,6 @@
|
||||
from pecan import expose, make_app
|
||||
from pecan.secure import secure, unlocked, SecureController
|
||||
from webob import exc
|
||||
from webtest import TestApp, AppError
|
||||
from py.test import raises
|
||||
from pecan.secure import secure, unlocked, SecureController, UnlockedControllerMeta
|
||||
from webtest import TestApp
|
||||
|
||||
class TestSecure(object):
|
||||
|
||||
@@ -58,4 +56,79 @@ class TestSecure(object):
|
||||
|
||||
response = app.get('/secret/allowed')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Allowed!'
|
||||
|
||||
def test_unlocked_meta(self):
|
||||
|
||||
class AuthorizedSubController(object):
|
||||
|
||||
__metaclass__ = UnlockedControllerMeta
|
||||
|
||||
@expose()
|
||||
def index(self):
|
||||
return 'Index'
|
||||
|
||||
@expose()
|
||||
def allowed(self):
|
||||
return 'Allowed!'
|
||||
|
||||
class SecretController(SecureController):
|
||||
@expose()
|
||||
def index(self):
|
||||
return 'Index'
|
||||
|
||||
@expose()
|
||||
@unlocked
|
||||
def allowed(self):
|
||||
return 'Allowed!'
|
||||
|
||||
@classmethod
|
||||
def check_permissions(self):
|
||||
return False
|
||||
|
||||
authorized = AuthorizedSubController()
|
||||
|
||||
class RootController(object):
|
||||
@expose()
|
||||
def index(self):
|
||||
return 'Hello, World!'
|
||||
|
||||
@expose()
|
||||
@secure(lambda: False)
|
||||
def locked(self):
|
||||
return 'No dice!'
|
||||
|
||||
@expose()
|
||||
@secure(lambda: True)
|
||||
def unlocked(self):
|
||||
return 'Sure thing'
|
||||
|
||||
secret = SecretController()
|
||||
|
||||
|
||||
app = TestApp(make_app(RootController(), static_root='tests/static'))
|
||||
response = app.get('/')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Hello, World!'
|
||||
|
||||
response = app.get('/unlocked')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Sure thing'
|
||||
|
||||
response = app.get('/locked', expect_errors=True)
|
||||
assert response.status_int == 401
|
||||
|
||||
response = app.get('/secret', expect_errors=True)
|
||||
assert response.status_int == 401
|
||||
|
||||
response = app.get('/secret/allowed')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Allowed!'
|
||||
|
||||
response = app.get('/secret/authorized')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Index'
|
||||
|
||||
response = app.get('/secret/authorized/allowed')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Allowed!'
|
||||
Reference in New Issue
Block a user