diff --git a/example/app.py b/example/app.py index ededb72..11f74a8 100644 --- a/example/app.py +++ b/example/app.py @@ -106,6 +106,10 @@ def create_app(auth_config): def admin_or_editor(): return render_template('index.html', content='Admin or Editor Page') + @app.route('/unauthorized') + def unauthorized(): + return render_template('unauthorized.html') + return app diff --git a/example/templates/unauthorized.html b/example/templates/unauthorized.html new file mode 100644 index 0000000..10e9c2b --- /dev/null +++ b/example/templates/unauthorized.html @@ -0,0 +1,3 @@ +{% include "_messages.html" %} +{% include "_nav.html" %} +

You are not allowed to access the requested resouce

diff --git a/flask_security/core.py b/flask_security/core.py index f254e0f..012145b 100644 --- a/flask_security/core.py +++ b/flask_security/core.py @@ -42,6 +42,7 @@ _default_config = { 'RESET_PASSWORD_ERROR_VIEW': '/', 'POST_REGISTER_VIEW': None, 'POST_CONFIRM_VIEW': None, + 'UNAUTHORIZED_VIEW': None, 'DEFAULT_ROLES': [], 'CONFIRMABLE': False, 'REGISTERABLE': False, diff --git a/flask_security/decorators.py b/flask_security/decorators.py index b03d9f8..e4848e9 100644 --- a/flask_security/decorators.py +++ b/flask_security/decorators.py @@ -11,7 +11,7 @@ from functools import wraps -from flask import current_app as app, Response, request, abort, redirect +from flask import current_app as app, Response, request, redirect from flask.ext.login import login_required, login_url, current_user from flask.ext.principal import RoleNeed, Permission from werkzeug.local import LocalProxy @@ -23,18 +23,26 @@ from . import utils _security = LocalProxy(lambda: app.security) -_default_http_auth_msg = """ +_default_unauthorized_txt = """

Unauthorized

The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad password), or your browser doesn't understand how to supply the credentials required.

-

In case you are allowed to request the document, please check your - user-id and password and try again.

""" -def _check_token(): +def _get_unauthorized_response(text=None, headers=None): + text = text or _default_unauthorized_txt + headers = headers or {} + return Response(_default_unauthorized_txt, 401, headers) + +def _get_unauthorized_view(): + cv = utils.get_url(utils.config_value('UNAUTHORIZED_VIEW')) + return (cv or request.referrer or '/') + + +def _check_token(): header_key = app.security.token_authentication_header args_key = app.security.token_authentication_key @@ -77,7 +85,7 @@ def http_auth_required(fn): if _check_http_auth(): return fn(*args, **kwargs) - return Response(_default_http_auth_msg, 401, headers) + return _get_unauthorized_response(headers=headers) return decorated @@ -89,7 +97,7 @@ def auth_token_required(fn): if _check_token(): return fn(*args, **kwargs) - abort(401) + return _get_unauthorized_response() return decorated @@ -111,6 +119,7 @@ def roles_required(*roles): perms = [Permission(RoleNeed(role)) for role in roles] def wrapper(fn): + @wraps(fn) def decorated_view(*args, **kwargs): if not current_user.is_authenticated(): @@ -120,10 +129,14 @@ def roles_required(*roles): for perm in perms: if not perm.can(): app.logger.debug('Identity does not provide the ' - 'roles: %s' % [r for r in roles]) - return redirect(request.referrer or '/') + 'roles: %s' % [r for r in roles]) + + return redirect(_get_unauthorized_view()) + return fn(*args, **kwargs) + return decorated_view + return wrapper @@ -144,6 +157,7 @@ def roles_accepted(*roles): perm = Permission(*[RoleNeed(role) for role in roles]) def wrapper(fn): + @wraps(fn) def decorated_view(*args, **kwargs): if not current_user.is_authenticated(): @@ -162,6 +176,8 @@ def roles_accepted(*roles): utils.do_flash('You do not have permission to ' 'view this resource', 'error') - return redirect(request.referrer or '/') + return redirect(_get_unauthorized_view()) + return decorated_view + return wrapper diff --git a/flask_security/templates/security/passwords/edit.html b/flask_security/templates/security/passwords/edit.html index 723773e..e806d0d 100644 --- a/flask_security/templates/security/passwords/edit.html +++ b/flask_security/templates/security/passwords/edit.html @@ -1,6 +1,6 @@ {% include "security/messages.html" %}

Change password

-
+ {{ reset_password_form.hidden_tag() }} {{ reset_password_form.password.label }} {{ reset_password_form.password }}
{{ reset_password_form.password_confirm.label }} {{ reset_password_form.password_confirm }}
diff --git a/tests/functional_tests.py b/tests/functional_tests.py index f55505f..925b5ba 100644 --- a/tests/functional_tests.py +++ b/tests/functional_tests.py @@ -140,7 +140,8 @@ class ConfiguredURLTests(SecurityTest): 'SECURITY_LOGIN_VIEW': '/custom_login', 'SECURITY_POST_LOGIN_VIEW': '/post_login', 'SECURITY_POST_LOGOUT_VIEW': '/post_logout', - 'SECURITY_POST_REGISTER_VIEW': '/post_register' + 'SECURITY_POST_REGISTER_VIEW': '/post_register', + 'SECURITY_UNAUTHORIZED_VIEW': '/unauthorized' } def test_login_view(self): @@ -157,10 +158,19 @@ class ConfiguredURLTests(SecurityTest): self.assertIn('Post Logout', r.data) def test_register(self): - data = dict(email='dude@lp.com', password='password', password_confirm='password') + data = dict(email='dude@lp.com', + password='password', + password_confirm='password') + r = self.client.post('/register', data=data, follow_redirects=True) self.assertIn('Post Register', r.data) + def test_unauthorized(self): + self.authenticate("joe@lp.com", endpoint="/custom_auth") + r = self._get("/admin", follow_redirects=True) + msg = 'You are not allowed to access the requested resouce' + self.assertIn(msg, r.data) + class RegisterableTests(SecurityTest): AUTH_CONFIG = {