From 5ed9dc00e69847748e1c235f1ccd1d371dd58ede Mon Sep 17 00:00:00 2001 From: gcarvellas Date: Thu, 14 Sep 2023 23:22:14 -0400 Subject: [PATCH 1/3] WIP: added boto3 to query cognito --- app.py | 3 +++ config/env.py | 3 +++ controllers/me.py | 2 +- database/__init__.py | 3 ++- database/cognito.py | 33 +++++++++++++++++++++++++++++++++ database/users.py | 7 ++++--- managers/contract.py | 2 +- managers/me.py | 4 ++-- requirements.txt | 8 ++++++-- 9 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 database/cognito.py diff --git a/app.py b/app.py index 4544455..d2331b9 100644 --- a/app.py +++ b/app.py @@ -12,9 +12,12 @@ from http import HTTPStatus from utilities.types import FlaskResponseType import traceback +from database import CognitoIdentityProviderWrapper app = Flask(__name__) +print(CognitoIdentityProviderWrapper().get_user("test")) + app.config.update({ 'COGNITO_REGION': COGNITO_REGION, 'COGNITO_USERPOOL_ID': COGNITO_USERPOOL_ID, diff --git a/config/env.py b/config/env.py index 6218a69..4880069 100644 --- a/config/env.py +++ b/config/env.py @@ -14,6 +14,9 @@ def load_env(input: str) -> str: COGNITO_REGION: str = load_env("COGNITO_REGION") COGNITO_USERPOOL_ID: str = load_env("COGNITO_USERPOOL_ID") COGNITO_APP_CLIENT_ID: str = load_env("COGNITO_APP_CLIENT_ID") +AWS_ACCESS_KEY_ID: str = load_env("AWS_ACCESS_KEY_ID") # TODO this should be accessed only with the ~/.aws/credentials file, not directly in the backend +AWS_SECRET_ACCESS_KEY: str = load_env("AWS_SECRET_ACCESS_KEY") # TODO this should be accessed only with the ~/.aws/credentials file, not directly in the backend +AWS_SESSION_TOKEN: str = load_env("AWS_SESSION_TOKEN") # TODO see above MONGO_URI: str = load_env("MONGO_URI") MONGO_DB_NAME: str = load_env("MONGO_DB_NAME") DOCUSIGN_CLIENT_ID: str = load_env("DOCUSIGN_CLIENT_ID") diff --git a/controllers/me.py b/controllers/me.py index aadf70d..bf93d83 100644 --- a/controllers/me.py +++ b/controllers/me.py @@ -24,7 +24,7 @@ def patch(self) -> FlaskResponseType: @swag_from(ME_POST_SCHEMA) def post(self) -> FlaskResponseType: data = self.get_request_data(self.ME_POST_SCHEMA, "NewUserData") - ret = MeManager().create_user(current_cognito_jwt['sub'], data['vendorType']) + ret = MeManager().create_user(current_cognito_jwt['sub'], str(current_user), data['vendorType']) if not ret: return FlaskResponses().bad_request("Failed to make user") return FlaskResponses().created_resource(ret) diff --git a/database/__init__.py b/database/__init__.py index 8b8b392..a32c6e7 100644 --- a/database/__init__.py +++ b/database/__init__.py @@ -1,2 +1,3 @@ from .users import UsersDB -from .contracts import ContractsDB \ No newline at end of file +from .contracts import ContractsDB +from .cognito import CognitoIdentityProviderWrapper \ No newline at end of file diff --git a/database/cognito.py b/database/cognito.py new file mode 100644 index 0000000..2f2250f --- /dev/null +++ b/database/cognito.py @@ -0,0 +1,33 @@ +# https://docs.aws.amazon.com/code-library/latest/ug/python_3_cognito-identity-provider_code_examples.html + +from botocore.exceptions import ClientError +import logging +import boto3 +from config.env import COGNITO_USERPOOL_ID, COGNITO_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN + +class CognitoIdentityProviderWrapper: + """Encapsulates Amazon Cognito actions""" + def __init__(self): + self.cognito_idp_client = boto3.client('cognito-idp', + region_name=COGNITO_REGION, + aws_access_key_id=AWS_ACCESS_KEY_ID, + aws_secret_access_key=AWS_SECRET_ACCESS_KEY, + aws_session_token=AWS_SESSION_TOKEN + ) + + def get_user(self, username: str): + """ + Gets a user in Cognito by it's username. + + :return: user + """ + try: + response = self.cognito_idp_client.admin_get_user(UserPoolId=COGNITO_USERPOOL_ID, Username=username) + user = response + except ClientError as err: + logging.error( + "Couldn't list users for %s. Here's why: %s: %s", COGNITO_USERPOOL_ID, + err.response['Error']['Code'], err.response['Error']['Message']) + raise + else: + return user \ No newline at end of file diff --git a/database/users.py b/database/users.py index 959062d..f99b5fc 100644 --- a/database/users.py +++ b/database/users.py @@ -10,9 +10,10 @@ class UsersDB(BaseDB): @classmethod - def _new_user(cls, _id: str, vendor_type: str) -> JSONDict: + def _new_user(cls, _id: str, username: str, vendor_type: str) -> JSONDict: return { "_id": _id, + "username": username, "contracts": [], "group": Groups.CUSTOMER, "vendor_type": vendor_type, @@ -25,8 +26,8 @@ def get_user(cls, uuid: str) -> Optional[MongoMappingType]: return cls.get_collection().find_one(query) @classmethod - def create_user(cls, uuid: str, vendor_type: str) -> bool: - query = cls._new_user(uuid, vendor_type) + def create_user(cls, uuid: str, username: str, vendor_type: str) -> bool: + query = cls._new_user(uuid, username, vendor_type) # TODO catch error if user already exists ret: pymongo.results.InsertOneResult = cls.get_collection().insert_one(query) return ret.acknowledged diff --git a/managers/contract.py b/managers/contract.py index 29eec2d..3af1e58 100644 --- a/managers/contract.py +++ b/managers/contract.py @@ -7,7 +7,7 @@ class ContractManager(): - + def create_contract(self, user_id: str, contract_type: str, diff --git a/managers/me.py b/managers/me.py index c25adec..78ca6bb 100644 --- a/managers/me.py +++ b/managers/me.py @@ -17,5 +17,5 @@ def get_user(self, current_user: str, current_cognito_jwt: LocalProxy[JSONDict]) 'database': current_cognito_jwt['database'] } - def create_user(self, user_id: str, vendor_type: str) -> bool: - return UsersDB.create_user(user_id, vendor_type) + def create_user(self, user_id: str, username: str, vendor_type: str) -> bool: + return UsersDB.create_user(user_id, username, vendor_type) diff --git a/requirements.txt b/requirements.txt index 1c26b41..bf400df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,8 @@ anyio==3.7.1 asyncio==3.4.3 attrs==23.1.0 blinker==1.6.2 +boto3==1.28.48 +botocore==1.31.48 certifi==2023.7.22 cffi==1.15.1 charset-normalizer==3.2.0 @@ -25,6 +27,7 @@ httpcore==0.17.3 idna==3.4 itsdangerous==2.1.2 Jinja2==3.1.2 +jmespath==1.0.1 jsonschema==4.18.4 jsonschema-specifications==2023.7.1 MarkupSafe==2.1.3 @@ -50,11 +53,12 @@ referencing==0.30.0 requests==2.31.0 rpds-py==0.9.2 rsa==4.9 +s3transfer==0.6.2 six==1.16.0 sniffio==1.3.0 tomli==2.0.1 types-Flask-Cors==4.0.0.1 types-jsonschema==4.17.0.10 typing_extensions==4.7.1 -urllib3==2.0.4 -Werkzeug==2.3.6 \ No newline at end of file +urllib3==1.26.16 +Werkzeug==2.3.6 From 8608c8d59f85bedce5e1871fcca4ae3f77fa1245 Mon Sep 17 00:00:00 2001 From: KevinHa48 Date: Sun, 24 Sep 2023 12:51:16 -0400 Subject: [PATCH 2/3] Removed env variables and added dynamic credential retrieval using AWS SSO profiles --- config/env.py | 3 --- database/cognito.py | 9 ++------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/config/env.py b/config/env.py index 4880069..6218a69 100644 --- a/config/env.py +++ b/config/env.py @@ -14,9 +14,6 @@ def load_env(input: str) -> str: COGNITO_REGION: str = load_env("COGNITO_REGION") COGNITO_USERPOOL_ID: str = load_env("COGNITO_USERPOOL_ID") COGNITO_APP_CLIENT_ID: str = load_env("COGNITO_APP_CLIENT_ID") -AWS_ACCESS_KEY_ID: str = load_env("AWS_ACCESS_KEY_ID") # TODO this should be accessed only with the ~/.aws/credentials file, not directly in the backend -AWS_SECRET_ACCESS_KEY: str = load_env("AWS_SECRET_ACCESS_KEY") # TODO this should be accessed only with the ~/.aws/credentials file, not directly in the backend -AWS_SESSION_TOKEN: str = load_env("AWS_SESSION_TOKEN") # TODO see above MONGO_URI: str = load_env("MONGO_URI") MONGO_DB_NAME: str = load_env("MONGO_DB_NAME") DOCUSIGN_CLIENT_ID: str = load_env("DOCUSIGN_CLIENT_ID") diff --git a/database/cognito.py b/database/cognito.py index 2f2250f..0d2769a 100644 --- a/database/cognito.py +++ b/database/cognito.py @@ -3,17 +3,12 @@ from botocore.exceptions import ClientError import logging import boto3 -from config.env import COGNITO_USERPOOL_ID, COGNITO_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN +from config.env import COGNITO_USERPOOL_ID, COGNITO_REGION class CognitoIdentityProviderWrapper: """Encapsulates Amazon Cognito actions""" def __init__(self): - self.cognito_idp_client = boto3.client('cognito-idp', - region_name=COGNITO_REGION, - aws_access_key_id=AWS_ACCESS_KEY_ID, - aws_secret_access_key=AWS_SECRET_ACCESS_KEY, - aws_session_token=AWS_SESSION_TOKEN - ) + self.cognito_idp_client = boto3.client('cognito-idp') def get_user(self, username: str): """ From 68a6633d1200496ab75a388e9f3313829c104046 Mon Sep 17 00:00:00 2001 From: gcarvellas Date: Wed, 25 Oct 2023 23:44:17 -0400 Subject: [PATCH 3/3] removed unnecessary TODO --- database/users.py | 1 - 1 file changed, 1 deletion(-) diff --git a/database/users.py b/database/users.py index 03dd05c..62c2874 100644 --- a/database/users.py +++ b/database/users.py @@ -29,7 +29,6 @@ async def get_user(cls, uuid: str) -> Optional[MongoMappingType]: @classmethod async def create_user(cls, uuid: str, username: str, vendor_type: str) -> bool: query = cls._new_user(uuid, username, vendor_type) - # TODO catch error if user already exists ret: pymongo.results.InsertOneResult = await cls.get_collection().insert_one(query) return ret.acknowledged