From 2c26a8c33a0bd51e78d320e1c35ea68a314232d8 Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Tue, 10 May 2016 00:50:33 +0200 Subject: [PATCH 1/3] make allows_jwt a real decorator. Tests included! --- gluon/tests/test_tools.py | 36 ++++++++++++++++++++++++++++++ gluon/tools.py | 47 ++++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/gluon/tests/test_tools.py b/gluon/tests/test_tools.py index 8ead4e1a..80dd2115 100644 --- a/gluon/tests/test_tools.py +++ b/gluon/tests/test_tools.py @@ -227,8 +227,44 @@ class TestMail(unittest.TestCase): # self.assertEqual(form.xml(), rtn) # TODO: class TestAuthJWT(unittest.TestCase): +class TestAuthJWT(unittest.TestCase): + def setUp(self): + from gluon.tools import AuthJWT + + from gluon import current + + self.request = Request(env={}) + self.request.application = 'a' + self.request.controller = 'c' + self.request.function = 'f' + self.request.folder = 'applications/admin' + self.current = current + self.current.request = self.request + + self.db = DAL(DEFAULT_URI, check_reserved=['all']) + self.auth = Auth(self.db) + self.auth.define_tables(username=True, signature=False) + self.user_data = dict(username='jwtuser', password='jwtuser123') + self.db.auth_user.insert(username=self.user_data['username'], + password=str( + self.db.auth_user.password.requires[0]( + self.user_data['password'])[0])) + self.jwtauth = AuthJWT(self.auth, secret_key='secret', verify_expiration=True) + def test_jwt_token_manager(self): + self.request.vars.update(self.user_data) + print self.current.request.vars + self.token = self.jwtauth.jwt_token_manager() + + + def test_allows_jwt(self): + request = self.request + @self.jwtauth.allows_jwt() + def optional_auth(): + assertEqual(self.user_data['username'], self.auth.user.username) + + @unittest.skipIf(IS_IMAP, "TODO: Imap raises 'Connection refused'") # class TestAuth(unittest.TestCase): # diff --git a/gluon/tools.py b/gluon/tools.py index 350ee96d..ae776daa 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -1192,6 +1192,15 @@ class AuthJWT(object): def protected(): return '%s$%s' % (request.now, auth.user_id) + To inject optional auth info into the action with JWT + @myjwt.allows_jwt() + def unprotected(): + if auth.user: + return '%s$%s' % (request.now, auth.user_id) + + return "No auth info!" + + """ def __init__(self, @@ -1411,13 +1420,15 @@ class AuthJWT(object): self.auth.user_groups = tokend['user_groups'] self.auth.hmac_key = tokend['hmac_key'] - def allows_jwt(self, otherwise=None): + def get_jwt_token_from_request(self): """ - The validator that checks for the header or the - _token var + The method that extracts and validates the token, either + from the header or the _token var + """ - request = current.request + token = None token_in_header = request.env.http_authorization + logger.debug('%s' % token_in_header) if token_in_header: parts = token_in_header.split() if parts[0].lower() != self.header_prefix.lower(): @@ -1429,11 +1440,31 @@ class AuthJWT(object): token = parts[1] else: token = request.vars._token - if token and len(token) < self.max_header_length: - tokend = self.load_token(token) - self.inject_token(tokend) - return self.auth.requires(True, otherwise=otherwise) + return token + + def allows_jwt(self, otherwise=None): + """ + The decorator that takes care of injecting auth info in the decorated action. + Works w/o resorting to session. + """ + def decorator(action): + def f(*args, **kwargs): + token = self.get_jwt_token_from_request() + if token and len(token) < self.max_header_length: + try: + tokend = self.load_token(token) + except ValueError: + raise HTTP(400, 'Invalid JWT header, wrong token format') + self.inject_token(tokend) + return action(*args, **kwargs) + + f.__doc__ = action.__doc__ + f.__name__ = action.__name__ + f.__dict__.update(action.__dict__) + return f + + return decorator class Auth(object): From 4658e172c3514c0af276665a24ff9c87ca43aca4 Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Tue, 10 May 2016 01:11:08 +0200 Subject: [PATCH 2/3] removed forgotten print statement --- gluon/tests/test_tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gluon/tests/test_tools.py b/gluon/tests/test_tools.py index 80dd2115..cc45154b 100644 --- a/gluon/tests/test_tools.py +++ b/gluon/tests/test_tools.py @@ -254,7 +254,6 @@ class TestAuthJWT(unittest.TestCase): def test_jwt_token_manager(self): self.request.vars.update(self.user_data) - print self.current.request.vars self.token = self.jwtauth.jwt_token_manager() From 7d48d6ba03bf0e4c69206610a170407dfd7a1930 Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Tue, 10 May 2016 01:35:22 +0200 Subject: [PATCH 3/3] removed logging leftover --- gluon/tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gluon/tools.py b/gluon/tools.py index ae776daa..b572175b 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -1428,7 +1428,6 @@ class AuthJWT(object): """ token = None token_in_header = request.env.http_authorization - logger.debug('%s' % token_in_header) if token_in_header: parts = token_in_header.split() if parts[0].lower() != self.header_prefix.lower():