From 4f3bb42b1136106690ef3ddba10c8e541f69f426 Mon Sep 17 00:00:00 2001 From: Conrad Date: Mon, 6 Nov 2017 17:07:11 -0500 Subject: [PATCH 1/9] Add an authorization wrapper class --- bpvalve/auth0/__init__.py | 1 + bpvalve/auth0/auth0_authorization.py | 47 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 bpvalve/auth0/auth0_authorization.py diff --git a/bpvalve/auth0/__init__.py b/bpvalve/auth0/__init__.py index 6bfe127..68b4e75 100644 --- a/bpvalve/auth0/__init__.py +++ b/bpvalve/auth0/__init__.py @@ -1,2 +1,3 @@ from .auth0 import Auth0Wrapper +from .auth0_authorization import Auth0Authorization from .users import Auth0Users diff --git a/bpvalve/auth0/auth0_authorization.py b/bpvalve/auth0/auth0_authorization.py new file mode 100644 index 0000000..6ee0e1a --- /dev/null +++ b/bpvalve/auth0/auth0_authorization.py @@ -0,0 +1,47 @@ +import logging +import datetime +import requests +from auth0.v3.authentication import GetToken + +logger = logging.getLogger(__name__) + +class Auth0Authorization: + api_token = None + expires = None + + def __init__(self, domain, client_id, client_secret, api_audience, api_url): + self.domain = domain + self.client_id = client_id + self.client_secret = client_secret + self.api_audience = api_audience + self.api_url = api_url + + def _get_token(self): + get_token = GetToken(self.domain) + token = get_token.client_credentials(self.client_id, self.client_secret, self.api_audience) + self.api_token = token['access_token'] + self.expires = datetime.datetime.utcnow() + datetime.timedelta(0, token['expires_in']) + + def get_token(self): + logger.debug('Current token expires in %s', self.expires) + if self.api_token is None or self.expires < datetime.datetime.utcnow(): + logger.debug('Retrieving new token') + self._get_token() + + return self.api_token + + def get_user_permissions(self, user_id): + api_token = self.get_token() + permissions = set() + response = requests.get( + '{}/users/{}/groups?expand=true'.format(self.api_url, user_id), + headers={'authorization': 'Bearer {}'.format(api_token)} + ) + result = response.json() + if 'roles' in result: + for role in result['roles']: + if 'permissions' in role: + for permission in role['permissions']: + permissions.add(permission['name']) + return list(permissions) + -- GitLab From 6c55b9d2d9c51bd767cbe8f84720a4a2cc9bec8c Mon Sep 17 00:00:00 2001 From: Conrad Date: Tue, 7 Nov 2017 12:39:30 -0500 Subject: [PATCH 2/9] Add cache for permissions --- bpvalve/auth0/auth0_authorization.py | 54 ++++++++++++++++++++-------- bpvalve/flask/lib/red.py | 3 ++ bpvalve/redis_helper.py | 17 ++++++++- 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/bpvalve/auth0/auth0_authorization.py b/bpvalve/auth0/auth0_authorization.py index 6ee0e1a..a86ac08 100644 --- a/bpvalve/auth0/auth0_authorization.py +++ b/bpvalve/auth0/auth0_authorization.py @@ -1,15 +1,26 @@ import logging import datetime import requests +import json from auth0.v3.authentication import GetToken +from ..redis_helper import redis_get_one, redis_set_one + logger = logging.getLogger(__name__) class Auth0Authorization: api_token = None expires = None - def __init__(self, domain, client_id, client_secret, api_audience, api_url): + def __init__( + self, + domain, + client_id, + client_secret, + api_audience, + api_url, + ): + self.domain = domain self.client_id = client_id self.client_secret = client_secret @@ -30,18 +41,31 @@ class Auth0Authorization: return self.api_token - def get_user_permissions(self, user_id): - api_token = self.get_token() - permissions = set() - response = requests.get( - '{}/users/{}/groups?expand=true'.format(self.api_url, user_id), - headers={'authorization': 'Bearer {}'.format(api_token)} - ) - result = response.json() - if 'roles' in result: - for role in result['roles']: - if 'permissions' in role: - for permission in role['permissions']: - permissions.add(permission['name']) - return list(permissions) + def get_user_permissions(self, user_id, cache): + """ Get user permissions from cache, else get from Auth0 """ + redis_key = user_id + '_permissions' + permissions = redis_get_one(cache, redis_key) + if not permissions: + logger.info('No permissions in cache, fetching from Auth0') + api_token = self.get_token() + permissions = set() + response = requests.get( + '{}/users/{}/groups?expand=true'.format(self.api_url, user_id), + headers={'authorization': 'Bearer {}'.format(api_token)} + ) + result = response.json() + if 'roles' in result: + for role in result['roles']: + if 'permissions' in role: + for permission in role['permissions']: + permissions.add(permission['name']) + permissions = list(permissions) + redis_set_one(cache, redis_key, permissions) + else: + new_list = [] + for val in permissions: + new_list.append(val.decode()) + permissions = new_list + logger.info('Using cached permissions') + return permissions diff --git a/bpvalve/flask/lib/red.py b/bpvalve/flask/lib/red.py index 4fc67d5..ca0175f 100644 --- a/bpvalve/flask/lib/red.py +++ b/bpvalve/flask/lib/red.py @@ -10,6 +10,7 @@ class RedisWrapper(object): server_apps = None client_apps = None users = None + permissions = None def get_database(self, database): """Gets an actual database.""" @@ -27,6 +28,8 @@ class RedisWrapper(object): self.client_apps = self.get_database(1) # key/tokens for users self.users = self.get_database(2) + # key/tokens for permissions + self.permissions = self.get_database(3) def flushdb(self): """Flushes each database.""" diff --git a/bpvalve/redis_helper.py b/bpvalve/redis_helper.py index 1d3a18e..64c89e6 100644 --- a/bpvalve/redis_helper.py +++ b/bpvalve/redis_helper.py @@ -1,5 +1,5 @@ """Helper functions for redis""" - +ONE_DAY =60 * 60 * 24 def redis_hmset_batch(redis, key, data): pipe = redis.pipeline() @@ -14,3 +14,18 @@ def redis_hget_batch(redis, keys): pipe.hgetall(key) results = pipe.execute() return results + +def redis_set_one(redis, key, data): + redis.delete(key) + result = redis.rpush(key, *data) + redis.expire(key, ONE_DAY) + return result + +def redis_get_one(redis, key): + #length = redis.llen(key) + return redis.lrange(key, 0, -1) + pipe = redis.pipeline() + for i in range(length): + pipe.lindex(key, i) + results = pipe.execute() + return results -- GitLab From 03dc4d2da9e0b06295e2877a7b1920ed31b02986 Mon Sep 17 00:00:00 2001 From: Conrad Date: Tue, 7 Nov 2017 16:12:34 -0500 Subject: [PATCH 3/9] Check if no permissions before pushing to redis --- bpvalve/redis_helper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bpvalve/redis_helper.py b/bpvalve/redis_helper.py index 64c89e6..59a7eba 100644 --- a/bpvalve/redis_helper.py +++ b/bpvalve/redis_helper.py @@ -16,13 +16,15 @@ def redis_hget_batch(redis, keys): return results def redis_set_one(redis, key, data): + if not data: + return [] redis.delete(key) result = redis.rpush(key, *data) redis.expire(key, ONE_DAY) return result def redis_get_one(redis, key): - #length = redis.llen(key) + length = redis.llen(key) return redis.lrange(key, 0, -1) pipe = redis.pipeline() for i in range(length): -- GitLab From 8651f93acf100971dbc356c4bbb9e9a1b8549d9b Mon Sep 17 00:00:00 2001 From: Conrad Date: Tue, 7 Nov 2017 16:12:55 -0500 Subject: [PATCH 4/9] Delete before returning --- bpvalve/redis_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bpvalve/redis_helper.py b/bpvalve/redis_helper.py index 59a7eba..1b64426 100644 --- a/bpvalve/redis_helper.py +++ b/bpvalve/redis_helper.py @@ -16,9 +16,9 @@ def redis_hget_batch(redis, keys): return results def redis_set_one(redis, key, data): + redis.delete(key) if not data: return [] - redis.delete(key) result = redis.rpush(key, *data) redis.expire(key, ONE_DAY) return result -- GitLab From 0fa62df8ca671fa18fd1cb66a8770689150c61a5 Mon Sep 17 00:00:00 2001 From: Conrad Date: Tue, 7 Nov 2017 16:14:25 -0500 Subject: [PATCH 5/9] Remove extra code --- bpvalve/redis_helper.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/bpvalve/redis_helper.py b/bpvalve/redis_helper.py index 1b64426..2ab3382 100644 --- a/bpvalve/redis_helper.py +++ b/bpvalve/redis_helper.py @@ -1,5 +1,5 @@ """Helper functions for redis""" -ONE_DAY =60 * 60 * 24 +ONE_DAY = 60 * 60 * 24 def redis_hmset_batch(redis, key, data): pipe = redis.pipeline() @@ -24,10 +24,4 @@ def redis_set_one(redis, key, data): return result def redis_get_one(redis, key): - length = redis.llen(key) return redis.lrange(key, 0, -1) - pipe = redis.pipeline() - for i in range(length): - pipe.lindex(key, i) - results = pipe.execute() - return results -- GitLab From 2439c764fa42127fff1a18e92418d1bf2dc8d133 Mon Sep 17 00:00:00 2001 From: Conrad Date: Tue, 7 Nov 2017 18:00:41 -0500 Subject: [PATCH 6/9] Change to get/set list --- bpvalve/auth0/auth0_authorization.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bpvalve/auth0/auth0_authorization.py b/bpvalve/auth0/auth0_authorization.py index a86ac08..fd8fa19 100644 --- a/bpvalve/auth0/auth0_authorization.py +++ b/bpvalve/auth0/auth0_authorization.py @@ -4,7 +4,7 @@ import requests import json from auth0.v3.authentication import GetToken -from ..redis_helper import redis_get_one, redis_set_one +from ..redis_helper import redis_get_list, redis_set_list logger = logging.getLogger(__name__) @@ -44,7 +44,7 @@ class Auth0Authorization: def get_user_permissions(self, user_id, cache): """ Get user permissions from cache, else get from Auth0 """ redis_key = user_id + '_permissions' - permissions = redis_get_one(cache, redis_key) + permissions = redis_get_list(cache, redis_key) if not permissions: logger.info('No permissions in cache, fetching from Auth0') api_token = self.get_token() @@ -60,7 +60,7 @@ class Auth0Authorization: for permission in role['permissions']: permissions.add(permission['name']) permissions = list(permissions) - redis_set_one(cache, redis_key, permissions) + redis_set_list(cache, redis_key, permissions) else: new_list = [] for val in permissions: -- GitLab From a66c9a1e7bbe6d32944e177c3fca9508f8d548bb Mon Sep 17 00:00:00 2001 From: Conrad Date: Tue, 7 Nov 2017 18:09:17 -0500 Subject: [PATCH 7/9] Rename functions in redis_helper --- bpvalve/redis_helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bpvalve/redis_helper.py b/bpvalve/redis_helper.py index 2ab3382..6567f82 100644 --- a/bpvalve/redis_helper.py +++ b/bpvalve/redis_helper.py @@ -15,7 +15,7 @@ def redis_hget_batch(redis, keys): results = pipe.execute() return results -def redis_set_one(redis, key, data): +def redis_set_list(redis, key, data): redis.delete(key) if not data: return [] @@ -23,5 +23,5 @@ def redis_set_one(redis, key, data): redis.expire(key, ONE_DAY) return result -def redis_get_one(redis, key): +def redis_get_list(redis, key): return redis.lrange(key, 0, -1) -- GitLab From 1caa4f58fb92977b77f8295531f98023fa71a50c Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Tue, 7 Nov 2017 18:09:45 -0500 Subject: [PATCH 8/9] Simplify merging permission list --- bpvalve/auth0/auth0_authorization.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bpvalve/auth0/auth0_authorization.py b/bpvalve/auth0/auth0_authorization.py index fd8fa19..fdba5cc 100644 --- a/bpvalve/auth0/auth0_authorization.py +++ b/bpvalve/auth0/auth0_authorization.py @@ -48,7 +48,8 @@ class Auth0Authorization: if not permissions: logger.info('No permissions in cache, fetching from Auth0') api_token = self.get_token() - permissions = set() + permissions = [] + response = requests.get( '{}/users/{}/groups?expand=true'.format(self.api_url, user_id), headers={'authorization': 'Bearer {}'.format(api_token)} @@ -57,9 +58,9 @@ class Auth0Authorization: if 'roles' in result: for role in result['roles']: if 'permissions' in role: - for permission in role['permissions']: - permissions.add(permission['name']) - permissions = list(permissions) + permissions.extend([val['name'] for val in role['permissions']]) + + permissions = list(set(permissions)) redis_set_list(cache, redis_key, permissions) else: new_list = [] -- GitLab From 4b2ccd969bb337bfe8effa5c0fb622f01a06d0d8 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Tue, 7 Nov 2017 18:12:36 -0500 Subject: [PATCH 9/9] Update auth0_auth syle --- bpvalve/auth0/auth0_authorization.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/bpvalve/auth0/auth0_authorization.py b/bpvalve/auth0/auth0_authorization.py index fdba5cc..ca00b1b 100644 --- a/bpvalve/auth0/auth0_authorization.py +++ b/bpvalve/auth0/auth0_authorization.py @@ -1,25 +1,18 @@ import logging import datetime import requests -import json from auth0.v3.authentication import GetToken from ..redis_helper import redis_get_list, redis_set_list logger = logging.getLogger(__name__) + class Auth0Authorization: api_token = None expires = None - def __init__( - self, - domain, - client_id, - client_secret, - api_audience, - api_url, - ): + def __init__(self, domain, client_id, client_secret, api_audience, api_url): self.domain = domain self.client_id = client_id @@ -42,7 +35,7 @@ class Auth0Authorization: return self.api_token def get_user_permissions(self, user_id, cache): - """ Get user permissions from cache, else get from Auth0 """ + """Get user permissions from cache, else get from Auth0""" redis_key = user_id + '_permissions' permissions = redis_get_list(cache, redis_key) if not permissions: -- GitLab