From 1a0ddff82b2d1ffb1a9f0406f80b49431e46dd16 Mon Sep 17 00:00:00 2001 From: apahomov Date: Mon, 14 Jan 2013 10:54:48 +0400 Subject: [PATCH 1/4] Get auth token from JSON request. --- flask_security/decorators.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flask_security/decorators.py b/flask_security/decorators.py index 5787679..930df56 100644 --- a/flask_security/decorators.py +++ b/flask_security/decorators.py @@ -48,6 +48,8 @@ def _check_token(): args_key = _security.token_authentication_key header_token = request.headers.get(header_key, None) token = request.args.get(args_key, header_token) + if request.json: + token = request.json.get(args_key, token) serializer = _security.remember_token_serializer try: From bbed019ca560f3d31a88c682adcb9a5526b639ed Mon Sep 17 00:00:00 2001 From: apahomov Date: Mon, 14 Jan 2013 15:45:18 +0400 Subject: [PATCH 2/4] Add auth_required decorator that allows multiple auth mechanisms --- flask_security/decorators.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/flask_security/decorators.py b/flask_security/decorators.py index 930df56..5e6ecd9 100644 --- a/flask_security/decorators.py +++ b/flask_security/decorators.py @@ -115,6 +115,35 @@ def auth_token_required(fn): return decorated +def auth_required(*auth_methods): + """ + Decorator that protects enpoints through multiple mechanisms + Example:: + + @app.route('/dashboard') + @auth_required('token', 'session') + def dashboard(): + return 'Dashboard' + + :param auth_methods: Specified mechanisms. + """ + login_mechanisms = { + 'token': lambda: _check_token(), + 'basic': lambda: _check_http_auth(), + 'session': lambda: current_user.is_authenticated() + } + + def wrapper(fn): + @wraps(fn) + def decorated_view(*args, **kwargs): + mechanisms = [login_mechanisms.get(method) for method in auth_methods] + if any(mechanisms): + return fn(*args, **kwargs) + return _get_unauthorized_response() + return decorated_view + return wrapper + + def roles_required(*roles): """Decorator which specifies that a user must have all the specified roles. Example:: From 3f9ca423bd1b3e59bcfa13249266d1c496b66c06 Mon Sep 17 00:00:00 2001 From: apahomov Date: Mon, 14 Jan 2013 16:11:09 +0400 Subject: [PATCH 3/4] Calling auth methods --- flask_security/decorators.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flask_security/decorators.py b/flask_security/decorators.py index 5e6ecd9..229e2c6 100644 --- a/flask_security/decorators.py +++ b/flask_security/decorators.py @@ -137,8 +137,9 @@ def auth_required(*auth_methods): @wraps(fn) def decorated_view(*args, **kwargs): mechanisms = [login_mechanisms.get(method) for method in auth_methods] - if any(mechanisms): - return fn(*args, **kwargs) + for mechanism in mechanisms: + if mechanism and mechanism(): + return fn(*args, **kwargs) return _get_unauthorized_response() return decorated_view return wrapper From 39f62374aaccf70ee3770ab2a8a514bfd3f40b0e Mon Sep 17 00:00:00 2001 From: apahomov Date: Tue, 15 Jan 2013 10:30:48 +0400 Subject: [PATCH 4/4] Added tests --- tests/functional_tests.py | 18 ++++++++++++++++++ tests/test_app/__init__.py | 7 ++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/functional_tests.py b/tests/functional_tests.py index 49e9be6..61fc92d 100644 --- a/tests/functional_tests.py +++ b/tests/functional_tests.py @@ -169,6 +169,24 @@ class DefaultSecurityTests(SecurityTest): self.assertEquals('Basic realm="My Realm"', r.headers['WWW-Authenticate']) + def test_multi_auth_basic(self): + r = self._get('/multi_auth', headers={ + 'Authorization': 'Basic ' + base64.b64encode("joe@lp.com:password") + }) + self.assertIn('Basic', r.data) + + def test_multi_auth_token(self): + r = self.json_authenticate() + data = json.loads(r.data) + token = data['response']['user']['authentication_token'] + r = self._get('/multi_auth?auth_token=' + token) + self.assertIn('Token', r.data) + + def test_multi_auth_session(self): + self.authenticate() + r = self._get('/multi_auth') + self.assertIn('Session', r.data) + def test_user_deleted_during_session_reverts_to_anonymous_user(self): self.authenticate() diff --git a/tests/test_app/__init__.py b/tests/test_app/__init__.py index 5cad502..863db79 100644 --- a/tests/test_app/__init__.py +++ b/tests/test_app/__init__.py @@ -4,7 +4,7 @@ from flask import Flask, render_template, current_app from flask.ext.mail import Mail from flask.ext.security import login_required, roles_required, roles_accepted from flask.ext.security.decorators import http_auth_required, \ - auth_token_required + auth_token_required, auth_required from flask.ext.security.utils import encrypt_password from werkzeug.local import LocalProxy @@ -50,6 +50,11 @@ def create_app(config): def token(): return render_template('index.html', content='Token Authentication') + @app.route('/multi_auth') + @auth_required('session', 'token', 'basic') + def multi_auth(): + return render_template('index.html', content='Session, Token, Basic auth') + @app.route('/post_logout') def post_logout(): return render_template('index.html', content='Post Logout')