From e7edf82f14979250422f806d3d510222a160a9df Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Tue, 31 Oct 2023 00:36:55 +0300 Subject: [PATCH 1/8] redis value extended by email --- dcicutils/redis_tools.py | 26 +++++++++++++++++++------- pyproject.toml | 2 +- 2 files changed, 20 insertions(+), 8 deletions(-) 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" From eb4608e5e66f3be51ff041eef2f5580449c021d4 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Tue, 31 Oct 2023 10:45:56 -0400 Subject: [PATCH 2/8] repair tests --- CHANGELOG.rst | 6 ++++++ test/test_redis_tools.py | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 634e1f9c6..41957553a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,12 @@ dcicutils Change Log ---------- +8.0.0.1c1 +========= + +* Updates for RAS to Redis API + + 8.0.0 ===== diff --git a/test/test_redis_tools.py b/test/test_redis_tools.py index 4e63bbdf4..b533ca8b4 100644 --- a/test/test_redis_tools.py +++ b/test/test_redis_tools.py @@ -23,6 +23,7 @@ def test_redis_session_basic(self, redisdb): rd = RedisBase(redisdb) session_token = RedisSessionToken( namespace=self.NAMESPACE, + email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT ) session_token.store_session_token(redis_handler=rd) @@ -34,7 +35,7 @@ def test_redis_session_basic(self, redisdb): assert not session_token.validate_session_token(redis_handler=rd) # update with a new token and expiration session_token.redis_key = working_token - session_token.update_session_token(redis_handler=rd, jwt=self.DUMMY_JWT) + session_token.update_session_token(redis_handler=rd, email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT) assert session_token.validate_session_token(redis_handler=rd) session_token.redis_key = working_token assert not session_token.validate_session_token(redis_handler=rd) @@ -47,13 +48,14 @@ def test_redis_session_expired_token(self, redisdb): with mock.patch.object(RedisSessionToken, '_build_session_expiration', self.mock_build_session_expiration): session_token = RedisSessionToken( namespace=self.NAMESPACE, + email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT ) session_token.store_session_token(redis_handler=rd) time.sleep(2) assert not session_token.validate_session_token(redis_handler=rd) # update then should validate - session_token.update_session_token(redis_handler=rd, jwt=self.DUMMY_JWT) + session_token.update_session_token(redis_handler=rd, email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT) assert session_token.validate_session_token(redis_handler=rd) def test_redis_session_many_sessions(self, redisdb): @@ -65,6 +67,7 @@ def test_redis_session_many_sessions(self, redisdb): for _ in range(5): session_token = RedisSessionToken( namespace=self.NAMESPACE, + email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT ) session_token.store_session_token(redis_handler=rd) @@ -92,6 +95,7 @@ def test_redis_session_from_redis_equality(self, redisdb): rd = RedisBase(redisdb) session_token_local = RedisSessionToken( namespace=self.NAMESPACE, + email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT ) session_token_local.store_session_token(redis_handler=rd) From 8fec7a9d4a2fc4156ae4df4af49344f4f4ed6d5c Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Sun, 5 Nov 2023 00:53:44 +0300 Subject: [PATCH 3/8] bump version --- pyproject.toml | 2 +- test/test_redis_tools.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5c3863c3a..6a87ad3b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicutils" -version = "8.2.0" +version = "8.3.0.0a1" description = "Utility package for interacting with the 4DN Data Portal and other 4DN resources" authors = ["4DN-DCIC Team "] license = "MIT" diff --git a/test/test_redis_tools.py b/test/test_redis_tools.py index 4e63bbdf4..b533ca8b4 100644 --- a/test/test_redis_tools.py +++ b/test/test_redis_tools.py @@ -23,6 +23,7 @@ def test_redis_session_basic(self, redisdb): rd = RedisBase(redisdb) session_token = RedisSessionToken( namespace=self.NAMESPACE, + email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT ) session_token.store_session_token(redis_handler=rd) @@ -34,7 +35,7 @@ def test_redis_session_basic(self, redisdb): assert not session_token.validate_session_token(redis_handler=rd) # update with a new token and expiration session_token.redis_key = working_token - session_token.update_session_token(redis_handler=rd, jwt=self.DUMMY_JWT) + session_token.update_session_token(redis_handler=rd, email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT) assert session_token.validate_session_token(redis_handler=rd) session_token.redis_key = working_token assert not session_token.validate_session_token(redis_handler=rd) @@ -47,13 +48,14 @@ def test_redis_session_expired_token(self, redisdb): with mock.patch.object(RedisSessionToken, '_build_session_expiration', self.mock_build_session_expiration): session_token = RedisSessionToken( namespace=self.NAMESPACE, + email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT ) session_token.store_session_token(redis_handler=rd) time.sleep(2) assert not session_token.validate_session_token(redis_handler=rd) # update then should validate - session_token.update_session_token(redis_handler=rd, jwt=self.DUMMY_JWT) + session_token.update_session_token(redis_handler=rd, email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT) assert session_token.validate_session_token(redis_handler=rd) def test_redis_session_many_sessions(self, redisdb): @@ -65,6 +67,7 @@ def test_redis_session_many_sessions(self, redisdb): for _ in range(5): session_token = RedisSessionToken( namespace=self.NAMESPACE, + email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT ) session_token.store_session_token(redis_handler=rd) @@ -92,6 +95,7 @@ def test_redis_session_from_redis_equality(self, redisdb): rd = RedisBase(redisdb) session_token_local = RedisSessionToken( namespace=self.NAMESPACE, + email=self.DUMMY_EMAIL, jwt=self.DUMMY_JWT ) session_token_local.store_session_token(redis_handler=rd) From 8c36b357e5738255e3ed03468b32ac29598c4e8b Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Mon, 6 Nov 2023 00:59:04 +0300 Subject: [PATCH 4/8] set verify_signature as True --- dcicutils/redis_tools.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dcicutils/redis_tools.py b/dcicutils/redis_tools.py index f3cc2a218..6cd9bf623 100644 --- a/dcicutils/redis_tools.py +++ b/dcicutils/redis_tools.py @@ -113,9 +113,8 @@ def decode_jwt(self, audience: str, secret: str, leeway: int = 30, algorithms: l :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': False}, algorithms=algorithms) + options={'verify_signature': True}, algorithms=algorithms) def store_session_token(self, *, redis_handler: RedisBase) -> bool: """ Stores the created session token object as an hset in Redis From fcc02fe7d91831f823ce65e5f09658d00e3461f4 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Mon, 6 Nov 2023 01:07:43 +0300 Subject: [PATCH 5/8] bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6a87ad3b6..14c639ed1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicutils" -version = "8.3.0.0a1" +version = "8.3.0.0a2" description = "Utility package for interacting with the 4DN Data Portal and other 4DN resources" authors = ["4DN-DCIC Team "] license = "MIT" From e0f12b711393f699e0d8829da84bbb6027f7c66f Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Tue, 7 Nov 2023 10:35:31 -0500 Subject: [PATCH 6/8] default in the appropriate place for auth0 domain --- dcicutils/deployment_utils.py | 4 ++-- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dcicutils/deployment_utils.py b/dcicutils/deployment_utils.py index 69716a990..c17781a4e 100644 --- a/dcicutils/deployment_utils.py +++ b/dcicutils/deployment_utils.py @@ -426,7 +426,7 @@ def build_ini_file_from_template(cls, template_file_name, init_file_name, *, indexer=None, index_server=None, sentry_dsn=None, tibanna_cwls_bucket=None, tibanna_output_bucket=None, application_bucket_prefix=None, foursight_bucket_prefix=None, - auth0_domain=DEFAULT_AUTH0_DOMAIN, auth0_client=None, auth0_secret=None, + auth0_domain=None, auth0_client=None, auth0_secret=None, auth0_allowed_connections=None, re_captcha_key=None, re_captcha_secret=None, redis_server=None, @@ -680,7 +680,7 @@ def build_ini_stream_from_template(cls, template_file_name, init_file_stream, *, sentry_dsn = sentry_dsn or os.environ.get("ENCODED_SENTRY_DSN", "") # Auth0 Configuration - auth0_domain = auth0_domain or os.environ.get("ENCODED_AUTH0_DOMAIN", "") + auth0_domain = auth0_domain or os.environ.get("ENCODED_AUTH0_DOMAIN", cls.DEFAULT_AUTH0_DOMAIN) auth0_client = auth0_client or os.environ.get("ENCODED_AUTH0_CLIENT", "") auth0_secret = auth0_secret or os.environ.get("ENCODED_AUTH0_SECRET", "") auth0_allowed_connections = auth0_allowed_connections or os.environ.get("ENCODED_AUTH0_ALLOWED_CONNECTIONS", "") diff --git a/pyproject.toml b/pyproject.toml index afea908e9..f8b1dd416 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicutils" -version = "8.0.0.1c1" +version = "8.0.0.1c2" description = "Utility package for interacting with the 4DN Data Portal and other 4DN resources" authors = ["4DN-DCIC Team "] license = "MIT" From cce6d05adf3cc996b6500d30ca0eeb2779051d77 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Tue, 7 Nov 2023 10:38:43 -0500 Subject: [PATCH 7/8] fix flake --- dcicutils/redis_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dcicutils/redis_tools.py b/dcicutils/redis_tools.py index 6cd9bf623..ba132fb67 100644 --- a/dcicutils/redis_tools.py +++ b/dcicutils/redis_tools.py @@ -82,7 +82,7 @@ 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 From 0faf22463fc4a1cf7489192497127b1c4e0bb31a Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Fri, 17 Nov 2023 23:53:38 +0300 Subject: [PATCH 8/8] bump version (production) --- CHANGELOG.rst | 3 +-- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 91691a7aa..d8b85d0fb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,7 +6,7 @@ dcicutils Change Log ---------- -8.0.0.0a3 +8.3.0 ========= * Updates for RAS to Redis API @@ -83,7 +83,6 @@ Change Log in each tab conforming to the schema for that tab. * ``summary_of_data_validation_errors`` to summarize the errors obtained from ``validate_data_against_schemas``. ->>>>>>> fcc02fe7d91831f823ce65e5f09658d00e3461f4 8.0.0 diff --git a/pyproject.toml b/pyproject.toml index b56050b53..6d6847fd5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicutils" -version = "8.3.0.0a3" +version = "8.3.0" description = "Utility package for interacting with the 4DN Data Portal and other 4DN resources" authors = ["4DN-DCIC Team "] license = "MIT"