diff --git a/bpvalve/auth0/__init__.py b/bpvalve/auth0/__init__.py index 6bfe1274d5a15541c0a1411e63d8620f31537b2d..68b4e7584834250c3b3a71b2def6a6d469fbf6fe 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 0000000000000000000000000000000000000000..ca00b1bd73e7396d60635fd0038d4b02110f81cd --- /dev/null +++ b/bpvalve/auth0/auth0_authorization.py @@ -0,0 +1,65 @@ +import logging +import datetime +import requests +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): + + 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, cache): + """Get user permissions from cache, else get from Auth0""" + redis_key = user_id + '_permissions' + permissions = redis_get_list(cache, redis_key) + if not permissions: + logger.info('No permissions in cache, fetching from Auth0') + api_token = self.get_token() + permissions = [] + + 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: + permissions.extend([val['name'] for val in role['permissions']]) + + permissions = list(set(permissions)) + redis_set_list(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 4fc67d55be2b1a577a9e23923f1642d2d97957ac..ca0175f305c23740202d52c437d003479684fb8e 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 1d3a18ec1a912821260c18bdcb04e27339d0f1a1..6567f82a1b771d8555e99962f89929797e17297b 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,14 @@ def redis_hget_batch(redis, keys): pipe.hgetall(key) results = pipe.execute() return results + +def redis_set_list(redis, key, data): + redis.delete(key) + if not data: + return [] + result = redis.rpush(key, *data) + redis.expire(key, ONE_DAY) + return result + +def redis_get_list(redis, key): + return redis.lrange(key, 0, -1)