From 624730f8dfe6bd7cfd2d3eb0906061bfc9ce080f Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Wed, 1 Jun 2022 11:08:48 +0200 Subject: [PATCH 01/19] add roles to user --- tdp_core/security/store/alb_security_store.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 417a89312..a68c57277 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -18,13 +18,19 @@ def __init__(self, cookie_name: Optional[str], signout_url: Optional[str]): def load_from_request(self, req): if "X-Amzn-Oidc-Identity" in req.headers and "X-Amzn-Oidc-Accesstoken" in req.headers and "X-Amzn-Oidc-Data" in req.headers: try: + roles = [] # Get token data from header encoded = req.headers["X-Amzn-Oidc-Data"] # Try to decode the oidc data jwt user = jwt.decode(encoded, options={"verify_signature": False}) # Create new user from given attributes email = user["email"] - return User(id=email, roles=[]) + if "roles" in user: + roles.extend(user["roles"]) + if "groups" in user: + roles.extend(user["groups"]) + _log.debug(f"load_from_request - email: {email}, roles: {roles}") + return User(id=email, roles=roles) except Exception: _log.exception("Error in load_from_request") return None From 0c24d3ea24d66b8d158a178beecaeb624b62e226 Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Wed, 29 Jun 2022 08:03:58 +0200 Subject: [PATCH 02/19] add debug --- tdp_core/security/store/alb_security_store.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index a68c57277..7d1689edc 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -23,6 +23,7 @@ def load_from_request(self, req): encoded = req.headers["X-Amzn-Oidc-Data"] # Try to decode the oidc data jwt user = jwt.decode(encoded, options={"verify_signature": False}) + _log.debug(f"user: {user}") # Create new user from given attributes email = user["email"] if "roles" in user: From ebe9e1f8a48456d1822e14c354d0a9d7a3f9dc91 Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Wed, 29 Jun 2022 08:29:40 +0200 Subject: [PATCH 03/19] change cache key --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f841166f8..bcbc7fecd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -92,7 +92,7 @@ jobs: node -v npm -v - restore_cache: - key: deps1-{{ .Branch }}-{{ checksum "package.json" }} + key: deps2-{{ .Branch }}-{{ checksum "package.json" }} - run: name: Install npm dependencies command: npm install @@ -101,7 +101,7 @@ jobs: command: | (grep -l '._resolved.: .\(git[^:]*\|bitbucket\):' ./node_modules/*/package.json || true) | xargs -r dirname | xargs -r rm -rf - save_cache: - key: deps1-{{ .Branch }}-{{ checksum "package.json" }} + key: deps2-{{ .Branch }}-{{ checksum "package.json" }} paths: ./node_modules - run: name: Install npm dependencies from git repositories (always get latest commit) From ad59af2be53ea510549444890d9ccdc3430efa40 Mon Sep 17 00:00:00 2001 From: Anita Steiner Date: Wed, 13 Jul 2022 09:41:01 +0200 Subject: [PATCH 04/19] Update alb_security_store.py --- tdp_core/security/store/alb_security_store.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 7d1689edc..6e9d4bd1d 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -21,6 +21,9 @@ def load_from_request(self, req): roles = [] # Get token data from header encoded = req.headers["X-Amzn-Oidc-Data"] + _log.debug(f"X-Amzn-Oidc-Data: {encoded}") + _log.debug(f"X-Amzn-Oidc-Accesstoken: {req.headers["X-Amzn-Oidc-Accesstoken"]}") + _log.debug(f"X-Amzn-Oidc-Identity: {req.headers["X-Amzn-Oidc-Identity"]}") # Try to decode the oidc data jwt user = jwt.decode(encoded, options={"verify_signature": False}) _log.debug(f"user: {user}") From 78260cd95870f4d80ea4206e311b53907e03c3d9 Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Wed, 13 Jul 2022 09:48:17 +0200 Subject: [PATCH 05/19] change output --- tdp_core/security/store/alb_security_store.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 6e9d4bd1d..fc688a4c1 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -22,8 +22,8 @@ def load_from_request(self, req): # Get token data from header encoded = req.headers["X-Amzn-Oidc-Data"] _log.debug(f"X-Amzn-Oidc-Data: {encoded}") - _log.debug(f"X-Amzn-Oidc-Accesstoken: {req.headers["X-Amzn-Oidc-Accesstoken"]}") - _log.debug(f"X-Amzn-Oidc-Identity: {req.headers["X-Amzn-Oidc-Identity"]}") + _log.debug(f"X-Amzn-Oidc-Accesstoken: {req.headers['X-Amzn-Oidc-Accesstoken']}") + _log.debug(f"X-Amzn-Oidc-Identity: {req.headers['X-Amzn-Oidc-Identity']}") # Try to decode the oidc data jwt user = jwt.decode(encoded, options={"verify_signature": False}) _log.debug(f"user: {user}") From a559ef7dbbae94d6ef446de7bd44e2ce81aee9a7 Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Wed, 13 Jul 2022 10:20:04 +0200 Subject: [PATCH 06/19] add log output --- tdp_core/security/store/alb_security_store.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index fc688a4c1..58434bbdc 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -20,6 +20,7 @@ def load_from_request(self, req): try: roles = [] # Get token data from header + _log.debug(f"headers: {req.headers}") encoded = req.headers["X-Amzn-Oidc-Data"] _log.debug(f"X-Amzn-Oidc-Data: {encoded}") _log.debug(f"X-Amzn-Oidc-Accesstoken: {req.headers['X-Amzn-Oidc-Accesstoken']}") From ce57f103a82491abe65f8b865ea28ee668440d57 Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Thu, 14 Jul 2022 07:43:21 +0200 Subject: [PATCH 07/19] add userinfo endpoint --- tdp_core/security/store/alb_security_store.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 58434bbdc..dedb4985b 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -2,6 +2,7 @@ from typing import Optional import jwt +from fastapi import FastAPI from ... import manager from ..model import User @@ -54,6 +55,12 @@ def logout(self, user): return {"data": payload, "cookies": cookies} + def _get_user_info_from_token(self, token: str): + return jwt.decode(token, options={"verify_signature": False}) + + def init_app(self, app: FastAPI): + app.add_api_route("/userinfo", self.get_user_info) + def create(): # Check if the security store is enabled. From 9728cb595dcb4fabd9424c75fa9f096223542deb Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Thu, 14 Jul 2022 08:30:02 +0200 Subject: [PATCH 08/19] add get_user_info --- tdp_core/security/store/alb_security_store.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index dedb4985b..8d375acac 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -5,6 +5,7 @@ from fastapi import FastAPI from ... import manager +from ..middleware.request_context_middleware import get_request from ..model import User from .base_store import BaseStore @@ -58,6 +59,19 @@ def logout(self, user): def _get_user_info_from_token(self, token: str): return jwt.decode(token, options={"verify_signature": False}) + def get_user_info(self): + req = get_request() + if "Authorization" in req.headers: + try: + access_token = req.headers["Authorization"] + return self._get_user_info_from_token(access_token[7:]) + except Exception as e: + _log.exception("Error in get_user_info: %s", e) + return None + else: + result = {} + return result + def init_app(self, app: FastAPI): app.add_api_route("/userinfo", self.get_user_info) From 2ec841931b310e5292733e0e23a055c8526baf3b Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Thu, 14 Jul 2022 08:42:53 +0200 Subject: [PATCH 09/19] change import --- tdp_core/security/store/alb_security_store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 8d375acac..efee99725 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -5,7 +5,7 @@ from fastapi import FastAPI from ... import manager -from ..middleware.request_context_middleware import get_request +from ...middleware.request_context_middleware import get_request from ..model import User from .base_store import BaseStore From 329daff3bd19a51c9ecaaec684c5cb3579b69905 Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Thu, 14 Jul 2022 09:39:52 +0200 Subject: [PATCH 10/19] add pun instead of email --- tdp_core/security/store/alb_security_store.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index efee99725..c3567b53e 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -31,7 +31,10 @@ def load_from_request(self, req): user = jwt.decode(encoded, options={"verify_signature": False}) _log.debug(f"user: {user}") # Create new user from given attributes - email = user["email"] + if "email" in user: + email = user["email"] + elif "upn" in user: + email = user["upn"] if "roles" in user: roles.extend(user["roles"]) if "groups" in user: From 217a0c86c9b905db544c601f5579830a07cc3534 Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Tue, 9 Aug 2022 11:52:49 +0200 Subject: [PATCH 11/19] remove logutreturnvalue --- tdp_core/security/store/alb_security_store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 3d21e8d43..66c80f990 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -5,7 +5,7 @@ from fastapi import FastAPI from ... import manager -from ..model import LogoutReturnValue, User +from ..model import User from ...middleware.request_context_middleware import get_request from .base_store import BaseStore From 094fe8b17321e9dc760b39a4415dfc1f656b756b Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Tue, 9 Aug 2022 12:00:38 +0200 Subject: [PATCH 12/19] lint --- tdp_core/security/store/alb_security_store.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 66c80f990..9d012a4a0 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -2,11 +2,9 @@ from typing import Optional import jwt -from fastapi import FastAPI from ... import manager from ..model import User -from ...middleware.request_context_middleware import get_request from .base_store import BaseStore _log = logging.getLogger(__name__) From a8add3b205c96641f546bd77dd2efb8cae62cb3d Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Tue, 25 Oct 2022 09:22:00 +0200 Subject: [PATCH 13/19] make user and roles configurable --- tdp_core/security/store/alb_security_store.py | 45 +++++++++++++------ tdp_core/settings/model.py | 2 + 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 9d012a4a0..4f9269dbd 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -11,9 +11,13 @@ class ALBSecurityStore(BaseStore): - def __init__(self, cookie_name: Optional[str], signout_url: Optional[str]): + def __init__( + self, cookie_name: Optional[str], signout_url: Optional[str], token_user_attr: Optional[str], token_roles_attr: Optional[str] + ): self.cookie_name = cookie_name - self.signout_url: Optional[str] = signout_url + self.signout_url = signout_url + self.token_user_attr = token_user_attr + self.token_roles_attr = token_roles_attr def load_from_request(self, req): if "X-Amzn-Oidc-Identity" in req.headers and "X-Amzn-Oidc-Accesstoken" in req.headers and "X-Amzn-Oidc-Data" in req.headers: @@ -26,19 +30,18 @@ def load_from_request(self, req): _log.debug(f"X-Amzn-Oidc-Accesstoken: {req.headers['X-Amzn-Oidc-Accesstoken']}") _log.debug(f"X-Amzn-Oidc-Identity: {req.headers['X-Amzn-Oidc-Identity']}") # Try to decode the oidc data jwt - user = jwt.decode(encoded, options={"verify_signature": False}) - _log.debug(f"user: {user}") + user_data = jwt.decode(encoded, options={"verify_signature": False}) + _log.debug(f"user data: {user_data}") # Create new user from given attributes - if "email" in user: - email = user["email"] - elif "upn" in user: - email = user["upn"] - if "roles" in user: - roles.extend(user["roles"]) - if "groups" in user: - roles.extend(user["groups"]) - _log.debug(f"load_from_request - email: {email}, roles: {roles}") - return User(id=email, roles=roles) + user = deep_get(user_data, self.token_user_attr) + _log.debug("user: %s", user) + roles = deep_get(user_data, self.token_roles_attr) + if not roles: + roles = [] + elif type(roles) != dict: + roles = [roles] + _log.debug("roles: %s", roles) + return User(id=user, roles=roles) except Exception: _log.exception("Error in load_from_request") return None @@ -68,6 +71,20 @@ def create(): return ALBSecurityStore( manager.settings.tdp_core.security.store.alb_security_store.cookie_name, manager.settings.tdp_core.security.store.alb_security_store.signout_url, + manager.settings.tdp_core.security.store.alb_security_store.token_user_attr, + manager.settings.tdp_core.security.store.alb_security_store.token_roles_attr, ) return None + + +def deep_get(obj, path): + if not path: + return None + keys = path.split(".") + for key in keys: + try: + obj = obj[key] + except KeyError: + return None + return obj diff --git a/tdp_core/settings/model.py b/tdp_core/settings/model.py index 18bfbaf21..5c22f28b3 100644 --- a/tdp_core/settings/model.py +++ b/tdp_core/settings/model.py @@ -31,6 +31,8 @@ class AlbSecurityStoreSettings(BaseModel): enable: bool = False cookie_name: Optional[str] = None signout_url: Optional[str] = None + token_user_attr: Optional[str] = "email" + token_roles_attr: Optional[str] = None class NoSecurityStoreSettings(BaseModel): From e2a817ca1e1382f3106cc260d1de5c4d98dd10fd Mon Sep 17 00:00:00 2001 From: anita-steiner Date: Tue, 25 Oct 2022 09:42:53 +0200 Subject: [PATCH 14/19] check if attr is set --- tdp_core/security/store/alb_security_store.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 4f9269dbd..d78d4a0ab 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -35,7 +35,8 @@ def load_from_request(self, req): # Create new user from given attributes user = deep_get(user_data, self.token_user_attr) _log.debug("user: %s", user) - roles = deep_get(user_data, self.token_roles_attr) + if self.token_roles_attr: + roles = deep_get(user_data, self.token_roles_attr) if not roles: roles = [] elif type(roles) != dict: From e838e7ed9b93c499a78867d618eb47815293881b Mon Sep 17 00:00:00 2001 From: Michael Puehringer Date: Wed, 9 Nov 2022 14:10:38 +0100 Subject: [PATCH 15/19] Fix return value --- tdp_core/security/store/alb_security_store.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index d78d4a0ab..434c3c5a3 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -4,7 +4,7 @@ import jwt from ... import manager -from ..model import User +from ..model import LogoutReturnValue, User from .base_store import BaseStore _log = logging.getLogger(__name__) @@ -59,7 +59,7 @@ def logout(self, user): if self.signout_url: payload["alb_security_store"] = {"redirect": self.signout_url} - return {"data": payload, "cookies": cookies} + return LogoutReturnValue(data=payload, cookies=cookies) def create(): From d97eb3e5d901cb9f605a4efeea34b05e08f2fa09 Mon Sep 17 00:00:00 2001 From: Anita Steiner Date: Sat, 12 Nov 2022 14:10:57 +0100 Subject: [PATCH 16/19] Update alb_security_store.py --- tdp_core/security/store/alb_security_store.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 434c3c5a3..60413e6b5 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -33,10 +33,10 @@ def load_from_request(self, req): user_data = jwt.decode(encoded, options={"verify_signature": False}) _log.debug(f"user data: {user_data}") # Create new user from given attributes - user = deep_get(user_data, self.token_user_attr) + user = user_data[self.token_user_attr] _log.debug("user: %s", user) if self.token_roles_attr: - roles = deep_get(user_data, self.token_roles_attr) + roles = user_data[self.token_roles_attr] if not roles: roles = [] elif type(roles) != dict: @@ -77,15 +77,3 @@ def create(): ) return None - - -def deep_get(obj, path): - if not path: - return None - keys = path.split(".") - for key in keys: - try: - obj = obj[key] - except KeyError: - return None - return obj From 23f786011a4a9daabee60119cd435d30c3cc95b8 Mon Sep 17 00:00:00 2001 From: Anita Steiner Date: Sat, 12 Nov 2022 14:44:15 +0100 Subject: [PATCH 17/19] Update alb_security_store.py --- tdp_core/security/store/alb_security_store.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 60413e6b5..4aa2f2527 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -37,6 +37,7 @@ def load_from_request(self, req): _log.debug("user: %s", user) if self.token_roles_attr: roles = user_data[self.token_roles_attr] + _log.debug("roletype: %s", type(roles)) if not roles: roles = [] elif type(roles) != dict: From 1702999141fcb16a321c682d2bb7090c40fe110e Mon Sep 17 00:00:00 2001 From: Anita Steiner Date: Sat, 12 Nov 2022 15:03:45 +0100 Subject: [PATCH 18/19] Update alb_security_store.py --- tdp_core/security/store/alb_security_store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 4aa2f2527..3728ece71 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -40,7 +40,7 @@ def load_from_request(self, req): _log.debug("roletype: %s", type(roles)) if not roles: roles = [] - elif type(roles) != dict: + elif type(roles) == string: roles = [roles] _log.debug("roles: %s", roles) return User(id=user, roles=roles) From 40d5a1564373cd82c0a18fb5a89bb34e53bbcb08 Mon Sep 17 00:00:00 2001 From: Anita Steiner Date: Sat, 12 Nov 2022 15:14:20 +0100 Subject: [PATCH 19/19] Update alb_security_store.py --- tdp_core/security/store/alb_security_store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tdp_core/security/store/alb_security_store.py b/tdp_core/security/store/alb_security_store.py index 3728ece71..b8d1eb9b2 100644 --- a/tdp_core/security/store/alb_security_store.py +++ b/tdp_core/security/store/alb_security_store.py @@ -40,7 +40,7 @@ def load_from_request(self, req): _log.debug("roletype: %s", type(roles)) if not roles: roles = [] - elif type(roles) == string: + elif type(roles) == str: roles = [roles] _log.debug("roles: %s", roles) return User(id=user, roles=roles)