"""Session Model. Session models are stored into redis due to their short life (they expire after a few seconds). The session model only store what is necessary: the user id, the client id. The sessions are very different from the API token in the extend that sessions are used by the user directly (and not by the client.) Related: ``config.SESSION_DURATION``: duration of the session in seconds. """ import json from ddtrace import tracer from grass import api, config from grass.connectors.redis import redis_client from grass.utils import response def get_key(key): """Return a generated redis key. Args: key (string): the key to use. Returns: `string`: the redis key. """ return 'session:%(key)s' % dict(key=key) @tracer.wrap() def create( client_id, user_id, token_id, roles=[], roles_by_name=[], identity_uuid=None, auth0_user_id=None, ): """Create the session for the client id and the token id. Args: client_id (str): The client id. user_id (str): The user id. token_id (str): The token id. roles (list): List of user roles by integer id. roles_by_name (list): List of user roles by string name. identity_uuid (str): The user identity_uuid. auth0_user_id (str): The user auth0_id Returns: tuple: the first part of the response contains the object information and the second part the http status response. """ resp = redis_client.set( get_key(token_id), json.dumps( dict( user_id=str(user_id), client_id=str(client_id), roles=roles, roles_by_name=roles_by_name, identity_uuid=identity_uuid, auth0_user_id=auth0_user_id, ) ), ex=config.SESSION_DURATION, ) if resp: data = dict(token=token_id, token_duration=config.SESSION_DURATION) return response.Response(data) return response.create_error_response() @tracer.wrap() def get_token(token_id): """Return the information about a token. Args: token_id (string): The key stored by the client. Returns: `tuple`: Token response, informations are contained in the dict, and the int represents a standard http status. """ resp = redis_client.get(get_key(token_id)) if resp: return response.Response(message=json.loads(resp.decode('utf8'))) return response.create_not_found_response() @tracer.wrap() def delete(token_id): """Delete the session token from redis. Args: token_id (str): The token id. Returns: 200 Session Deleted if success, else 404 Session does not exist. """ resp = redis_client.delete(get_key(token_id)) if resp: return response.Response(message='Session deleted.') return response.Response(message='Session does not exist.', status=404) @tracer.wrap() def store_auth_on_logout(token, time_to_live): """Use to store authorization token in redis cache on logout Args: token (str): token. time_to_live (int): time_to_live. """ redis_client.set('logged-out-jwt-' + token, '', time_to_live) @tracer.wrap() def is_jwt_cached(token, request_referer): """Use to verify if the jwt exist in our redis auth store Args: token (str): token. request_referer (str): request_referer. Returns: return True or False. """ is_cached = redis_client.exists('logged-out-jwt-' + token) if is_cached: api.logger.info( f'JWT is not invalid, this JWT belongs to a logged out user. ' f'token: {token} ' f'referer={request_referer}' ) return True return False