A bunch of changes from last night, late:
* Secured controller support. * Static file support.
This commit is contained in:
@@ -1,2 +1,25 @@
|
||||
from paste.urlparser import StaticURLParser
|
||||
from paste.cascade import Cascade
|
||||
|
||||
from pecan import Pecan, request, override_template
|
||||
from decorators import expose
|
||||
from decorators import expose
|
||||
|
||||
|
||||
def make_app(root, renderers = None,
|
||||
default_renderer = None,
|
||||
template_path = None,
|
||||
hooks = None,
|
||||
static_root = None):
|
||||
|
||||
kw = {}
|
||||
if renderers is not None: kw['renderers'] = renderers
|
||||
if default_renderer is not None: kw['default_renderer'] = default_renderer
|
||||
if template_path is not None: kw['template_path'] = template_path
|
||||
if hooks is not None: kw['hooks'] = hooks
|
||||
|
||||
app = Pecan(root, **kw)
|
||||
|
||||
if static_root:
|
||||
app = Cascade([StaticURLParser(static_root), app])
|
||||
|
||||
return app
|
||||
@@ -71,6 +71,11 @@ class Pecan(object):
|
||||
response.status = 404
|
||||
return response(environ, start_response)
|
||||
|
||||
# handle security
|
||||
if controller.pecan.get('secured', False):
|
||||
if not controller.pecan['check_permissions']():
|
||||
raise exc.HTTPUnauthorized
|
||||
|
||||
# determine content type
|
||||
if content_type is None:
|
||||
content_type = controller.pecan.get('content_type', 'text/html')
|
||||
|
||||
50
pecan/secure.py
Normal file
50
pecan/secure.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from webob.exc import HTTPUnauthorized
|
||||
from inspect import getmembers, ismethod
|
||||
|
||||
from routing import iscontroller
|
||||
|
||||
|
||||
__all__ = ['secure', 'unlocked', 'SecureController']
|
||||
|
||||
|
||||
def unlocked(func):
|
||||
if not hasattr(func, 'pecan'): func.pecan = {}
|
||||
func.pecan['unlocked'] = True
|
||||
return func
|
||||
|
||||
|
||||
def secure(check_permissions):
|
||||
def wrap(func):
|
||||
if not hasattr(func, 'pecan'): func.pecan = {}
|
||||
func.pecan['secured'] = True
|
||||
func.pecan['check_permissions'] = check_permissions
|
||||
return func
|
||||
return wrap
|
||||
|
||||
|
||||
def walk_controller(root_class, controller):
|
||||
if hasattr(controller, '_lookup'):
|
||||
# TODO: what about this?
|
||||
controller._check_security = root_class._perform_validation
|
||||
|
||||
if not isinstance(controller, (int, dict)):
|
||||
for name, value in getmembers(controller):
|
||||
if name == 'controller': continue
|
||||
|
||||
if ismethod(value):
|
||||
if iscontroller(value) and not value.pecan.get('unlocked', False):
|
||||
value.pecan['secured'] = True
|
||||
value.pecan['check_permissions'] = root_class.check_permissions
|
||||
elif hasattr(value, '__class__'):
|
||||
if name.startswith('__') and name.endswith('__'): continue
|
||||
walk_controller(root_class, value)
|
||||
|
||||
|
||||
class SecureController(object):
|
||||
class __metaclass__(type):
|
||||
def __init__(cls, name, bases, dict_):
|
||||
walk_controller(cls, cls)
|
||||
|
||||
@classmethod
|
||||
def check_permissions(cls):
|
||||
return True
|
||||
3
setup.py
3
setup.py
@@ -24,7 +24,8 @@ setup(
|
||||
"Kajiki >= 0.2.2",
|
||||
"Mako >= 0.3",
|
||||
"py >= 1.3.4",
|
||||
"WebTest >= 1.2.2"
|
||||
"WebTest >= 1.2.2",
|
||||
"Paste >= 1.7.5.1"
|
||||
],
|
||||
entry_points = """
|
||||
# -*- Entry points: -*-
|
||||
|
||||
9
tests/static/test.txt
Normal file
9
tests/static/test.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
This is a test text file.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
||||
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
|
||||
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
|
||||
mollit anim id est laborum.
|
||||
61
tests/test_secure.py
Normal file
61
tests/test_secure.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from pecan import expose, make_app
|
||||
from pecan.secure import secure, unlocked, SecureController
|
||||
from webob import exc
|
||||
from webtest import TestApp
|
||||
from py.test import raises
|
||||
|
||||
class TestSecure(object):
|
||||
|
||||
def test_simple_secure(self):
|
||||
authorized = False
|
||||
|
||||
class SecretController(SecureController):
|
||||
@expose()
|
||||
def index(self):
|
||||
return 'Index'
|
||||
|
||||
@expose()
|
||||
@unlocked
|
||||
def allowed(self):
|
||||
return 'Allowed!'
|
||||
|
||||
@classmethod
|
||||
def check_permissions(self):
|
||||
return authorized
|
||||
|
||||
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'
|
||||
|
||||
with raises(exc.HTTPUnauthorized):
|
||||
response = app.get('/locked')
|
||||
|
||||
with raises(exc.HTTPUnauthorized):
|
||||
response = app.get('/secret')
|
||||
|
||||
response = app.get('/secret/allowed')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Allowed!'
|
||||
21
tests/test_static.py
Normal file
21
tests/test_static.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from pecan import expose, make_app
|
||||
from webtest import TestApp
|
||||
|
||||
class TestStatic(object):
|
||||
|
||||
def test_simple_static(self):
|
||||
class RootController(object):
|
||||
@expose()
|
||||
def index(self):
|
||||
return 'Hello, World!'
|
||||
|
||||
# make sure Cascade is working properly
|
||||
app = TestApp(make_app(RootController(), static_root='tests/static'))
|
||||
response = app.get('/index.html')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Hello, World!'
|
||||
|
||||
# get a static resource
|
||||
response = app.get('/test.txt')
|
||||
assert response.status_int == 200
|
||||
assert response.body == open('tests/static/test.txt', 'rb').read()
|
||||
Reference in New Issue
Block a user