diff --git a/dcicutils/redis_tools.py b/dcicutils/redis_tools.py index fb6418d61..f3cc2a218 100644 --- a/dcicutils/redis_tools.py +++ b/dcicutils/redis_tools.py @@ -44,11 +44,12 @@ def _build_redis_key(namespace: str, token: str) -> str: """ return f'{namespace}:session:{token}' - def __init__(self, *, namespace: str, jwt: str, token=None, expiration=None): + def __init__(self, *, namespace: str, jwt: str, email: str, token=None, expiration=None): """ Creates a Redis Session object, storing a hash of the JWT into Redis and returning this value as the session token. :param namespace: namespace to build key under, for example the env name :param jwt: jwt generated for this user + :param email: email verified for this user :param token: value of token if passed, if not one will be generated :param expiration: expiration of token if passed, if not new expiration will be generated """ @@ -59,11 +60,12 @@ def __init__(self, *, namespace: str, jwt: str, token=None, expiration=None): self.namespace = namespace self.redis_key = self._build_redis_key(self.namespace, self.session_token) self.jwt = jwt + self.email = email self.expiration = expiration or self._build_session_expiration() def __eq__(self, other): """ Evaluates equality of two session objects based on the value of the session hset """ - return (self.redis_key == other.redis_key) and (self.jwt == other.jwt) + return (self.redis_key == other.redis_key) and (self.jwt == other.jwt) and (self.email == other.email) def get_session_token(self) -> str: """ Extracts the session token stored on this object """ @@ -80,6 +82,10 @@ def get_expiration(self) -> datetime: def get_jwt(self) -> str: """ Returns the JWT set on this session token object """ return self.jwt + + def get_email(self) -> str: + """ Returns the email set on this session token object """ + return self.email @classmethod def from_redis(cls, *, redis_handler: RedisBase, namespace: str, token: str): @@ -93,19 +99,23 @@ def from_redis(cls, *, redis_handler: RedisBase, namespace: str, token: str): redis_key = f'{namespace}:session:{token}' redis_entry = redis_handler.get(redis_key) if redis_entry: + jwt_and_email = redis_entry.split(':') + jwt = jwt_and_email[0] + email = jwt_and_email[1] if len(jwt_and_email) > 1 else None expiration = redis_handler.ttl(redis_key) - return cls(namespace=namespace, jwt=redis_entry, + return cls(namespace=namespace, jwt=jwt, email=email, token=token, expiration=expiration) - def decode_jwt(self, audience: str, secret: str, leeway: int = 30) -> dict: + def decode_jwt(self, audience: str, secret: str, leeway: int = 30, algorithms: list = ['HS256']) -> dict: """ Decodes JWT to grab info such as the email :param audience: audience under which to decode, typically Auth0Client :param secret: secret to decrypt using, typically Auth0Secret :param leeway: numerical value in seconds to account for clock drift :return: a decoded JWT in dictionary format """ + #TODO: verify_signature should be True return jwt.decode(self.jwt, secret, audience=audience, leeway=leeway, - options={'verify_signature': True}, algorithms=['HS256']) + options={'verify_signature': False}, algorithms=algorithms) def store_session_token(self, *, redis_handler: RedisBase) -> bool: """ Stores the created session token object as an hset in Redis @@ -113,7 +123,7 @@ def store_session_token(self, *, redis_handler: RedisBase) -> bool: :return: True if successful, raise Exception otherwise """ try: - redis_handler.set(self.redis_key, self.jwt, exp=self.expiration) + redis_handler.set(self.redis_key, f'{self.jwt}:{self.email or ""}', exp=self.expiration) except Exception as e: log.error(str(e)) raise RedisException() @@ -129,10 +139,11 @@ def validate_session_token(self, *, redis_handler: RedisBase) -> bool: return False # if it doesn't exist it's not valid return True # if it does exist it must be valid since we always send with TTL - def update_session_token(self, *, redis_handler: RedisBase, jwt: str) -> bool: + def update_session_token(self, *, redis_handler: RedisBase, jwt: str, email: str) -> bool: """ Refreshes the session token, jwt (if different) and expiration stored in Redis :param redis_handler: handle to Redis API :param jwt: jwt of user + :param email: email of user :return: True if successful, raise Exception otherwise """ # remove old token @@ -142,6 +153,7 @@ def update_session_token(self, *, redis_handler: RedisBase, jwt: str) -> bool: self.redis_key = self._build_redis_key(self.namespace, self.session_token) self.expiration = self._build_session_expiration() self.jwt = jwt + self.email = email return self.store_session_token(redis_handler=redis_handler) def delete_session_token(self, *, redis_handler: RedisBase) -> bool: diff --git a/pyproject.toml b/pyproject.toml index 804de6f74..afea908e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicutils" -version = "8.0.0" +version = "8.0.0.1c1" description = "Utility package for interacting with the 4DN Data Portal and other 4DN resources" authors = ["4DN-DCIC Team "] license = "MIT"