From cf01f0d51472e428636f60f9566a5839a9b806ab Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 10:51:47 -0400 Subject: [PATCH 01/24] Add refresh_token model --- .../{07ff3fbef405_.py => 71d3a02ed025_.py} | 15 ++++++++++++--- .../load_database_models.py | 1 + .../models/refresh_token.py | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) rename migrations/versions/{07ff3fbef405_.py => 71d3a02ed025_.py} (97%) create mode 100644 src/spiffworkflow_backend/models/refresh_token.py diff --git a/migrations/versions/07ff3fbef405_.py b/migrations/versions/71d3a02ed025_.py similarity index 97% rename from migrations/versions/07ff3fbef405_.py rename to migrations/versions/71d3a02ed025_.py index 6adc88cb..6d0ad913 100644 --- a/migrations/versions/07ff3fbef405_.py +++ b/migrations/versions/71d3a02ed025_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 07ff3fbef405 +Revision ID: 71d3a02ed025 Revises: -Create Date: 2022-10-13 07:56:01.234090 +Create Date: 2022-10-13 16:44:03.884031 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '07ff3fbef405' +revision = '71d3a02ed025' down_revision = None branch_labels = None depends_on = None @@ -134,6 +134,14 @@ def upgrade(): op.create_index(op.f('ix_process_instance_report_identifier'), 'process_instance_report', ['identifier'], unique=False) op.create_index(op.f('ix_process_instance_report_process_group_identifier'), 'process_instance_report', ['process_group_identifier'], unique=False) op.create_index(op.f('ix_process_instance_report_process_model_identifier'), 'process_instance_report', ['process_model_identifier'], unique=False) + op.create_table('refresh_token', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('token', sa.String(length=1024), nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('user_id') + ) op.create_table('secret', sa.Column('id', sa.Integer(), nullable=False), sa.Column('key', sa.String(length=50), nullable=False), @@ -316,6 +324,7 @@ def downgrade(): op.drop_table('active_task') op.drop_table('user_group_assignment') op.drop_table('secret') + op.drop_table('refresh_token') op.drop_index(op.f('ix_process_instance_report_process_model_identifier'), table_name='process_instance_report') op.drop_index(op.f('ix_process_instance_report_process_group_identifier'), table_name='process_instance_report') op.drop_index(op.f('ix_process_instance_report_identifier'), table_name='process_instance_report') diff --git a/src/spiffworkflow_backend/load_database_models.py b/src/spiffworkflow_backend/load_database_models.py index 33d32c1a..c064613a 100644 --- a/src/spiffworkflow_backend/load_database_models.py +++ b/src/spiffworkflow_backend/load_database_models.py @@ -45,6 +45,7 @@ from spiffworkflow_backend.models.process_instance import ( from spiffworkflow_backend.models.process_instance_report import ( ProcessInstanceReportModel, ) # noqa: F401 +from spiffworkflow_backend.models.refresh_token import RefreshTokenModel # noqa: F401 from spiffworkflow_backend.models.secret_model import SecretModel # noqa: F401 from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel # noqa: F401 from spiffworkflow_backend.models.task_event import TaskEventModel # noqa: F401 diff --git a/src/spiffworkflow_backend/models/refresh_token.py b/src/spiffworkflow_backend/models/refresh_token.py new file mode 100644 index 00000000..ad571601 --- /dev/null +++ b/src/spiffworkflow_backend/models/refresh_token.py @@ -0,0 +1,19 @@ +from dataclasses import dataclass +from flask_bpmn.models.db import db +from flask_bpmn.models.db import SpiffworkflowBaseDBModel +from sqlalchemy import ForeignKey +# from sqlalchemy.orm import relationship + +# from spiffworkflow_backend.models.user import UserModel + + +@dataclass() +class RefreshTokenModel(SpiffworkflowBaseDBModel): + """RefreshTokenModel.""" + + __tablename__ = "refresh_token" + + id: int = db.Column(db.Integer, primary_key=True) + user_id: int = db.Column(ForeignKey("user.id"), nullable=False, unique=True) + token: str = db.Column(db.String(1024), nullable=False) + # user = relationship("UserModel", back_populates="refresh_token") From be0557013e24909b1e4557a9403addcf8d117721 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 10:54:54 -0400 Subject: [PATCH 02/24] Cleanup - remove unused code --- src/spiffworkflow_backend/routes/user.py | 47 ------------------------ 1 file changed, 47 deletions(-) diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index a87f7b72..cb989a1b 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -116,27 +116,6 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i raise ApiError( error_code="invalid_token", message="Cannot validate token.", status_code=401 ) - # no token -- do we ever get here? - # else: - # ... - # if current_app.config.get("DEVELOPMENT"): - # # Fall back to a default user if this is not production. - # g.user = UserModel.query.first() - # if not g.user: - # raise ApiError( - # "no_user", - # "You are in development mode, but there are no users in the database. Add one, and it will use it.", - # ) - # token_from_user = g.user.encode_auth_token() - # token_info = UserModel.decode_auth_token(token_from_user) - # return token_info - # - # else: - # raise ApiError( - # error_code="no_auth_token", - # message="No authorization token was available.", - # status_code=401, - # ) def validate_scope(token: Any) -> bool: @@ -151,32 +130,6 @@ def validate_scope(token: Any) -> bool: return True -# def login_api(redirect_url: str = "/v1.0/ui") -> Response: -# """Api_login.""" -# # TODO: Fix this! mac 20220801 -# # token:dict = PublicAuthenticationService().get_public_access_token(uid, password) -# # -# # return token -# # if uid: -# # sub = f"service:internal::service_id:{uid}" -# # token = encode_auth_token(sub) -# # user_model = UserModel(username=uid, -# # uid=uid, -# # service='internal', -# # name="API User") -# # g.user = user_model -# # -# # g.token = token -# # scope = get_scope(token) -# # return token -# # return {"uid": uid, "sub": uid, "scope": scope} -# return login(redirect_url) - - -# def login_api_return(code: str, state: str, session_state: str) -> Optional[Response]: -# print("login_api_return") - - def encode_auth_token(sub: str, token_type: Optional[str] = None) -> str: """Generates the Auth Token. From 74883fb23117b26cf8d9318f9c041978ee70ffd0 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 11:10:47 -0400 Subject: [PATCH 03/24] Noe store refresh_token, and try to use it if auth_token is expired Renamed some methods to use correct token type --- src/spiffworkflow_backend/routes/user.py | 38 +++++++--- .../services/authentication_service.py | 69 +++++++++++++++---- .../services/user_service.py | 24 +++++++ 3 files changed, 106 insertions(+), 25 deletions(-) diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index cb989a1b..f42ed296 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -59,11 +59,24 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i elif "iss" in decoded_token.keys(): try: - user_info = PublicAuthenticationService.get_user_info_from_id_token( + user_info = PublicAuthenticationService.get_user_info_from_open_id_using_id_token( token ) except ApiError as ae: - raise ae + # Try to refresh the token + user = UserService.get_user_by_service_and_service_id('open_id', decoded_token['sub']) + refresh_token = PublicAuthenticationService.get_refresh_token(user.id) + if refresh_token: + auth_token = PublicAuthenticationService.get_auth_token_from_refresh_token(refresh_token) + if auth_token and "error" not in auth_token: + # redirect to original url, with auth_token? + user_info = PublicAuthenticationService.get_user_info_from_open_id_using_id_token( + auth_token + ) + if not user_info: + raise ae + else: + raise ae except Exception as e: current_app.logger.error(f"Exception raised in get_token: {e}") raise ApiError( @@ -106,7 +119,8 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i # If the user is valid, store the token for this session if g.user: - g.token = token + # This is an id token, so we don't have a refresh token yet + g.id_token = token scope = get_scope(token) return {"uid": g.user.id, "sub": g.user.id, "scope": scope} # return validate_scope(token, user_info, user_model) @@ -167,13 +181,13 @@ def login_return(code: str, state: str, session_state: str) -> Optional[Response state_dict = ast.literal_eval(base64.b64decode(state).decode("utf-8")) state_redirect_url = state_dict["redirect_url"] - id_token_object = PublicAuthenticationService().get_id_token_object(code) - if "id_token" in id_token_object: - id_token = id_token_object["id_token"] + auth_token_object = PublicAuthenticationService().get_auth_token_object(code) + if "id_token" in auth_token_object: + id_token = auth_token_object["id_token"] if PublicAuthenticationService.validate_id_token(id_token): - user_info = PublicAuthenticationService.get_user_info_from_id_token( - id_token_object["access_token"] + user_info = PublicAuthenticationService.get_user_info_from_open_id_using_id_token( + auth_token_object["access_token"] ) if user_info and "error" not in user_info: user_model = ( @@ -203,6 +217,8 @@ def login_return(code: str, state: str, session_state: str) -> Optional[Response if user_model: g.user = user_model.id + g.id_token = auth_token_object['id_token'] + UserService.store_refresh_token(user_model.id, auth_token_object['refresh_token']) # this may eventually get too slow. # when it does, be careful about backgrounding, because @@ -214,7 +230,7 @@ def login_return(code: str, state: str, session_state: str) -> Optional[Response redirect_url = ( f"{state_redirect_url}?" - + f"access_token={id_token_object['access_token']}&" + + f"access_token={auth_token_object['access_token']}&" + f"id_token={id_token}" ) return redirect(redirect_url) @@ -248,10 +264,10 @@ def login_api_return(code: str, state: str, session_state: str) -> str: state_dict = ast.literal_eval(base64.b64decode(state).decode("utf-8")) state_dict["redirect_url"] - id_token_object = PublicAuthenticationService().get_id_token_object( + auth_token_object = PublicAuthenticationService().get_auth_token_object( code, "/v1.0/login_api_return" ) - access_token: str = id_token_object["access_token"] + access_token: str = auth_token_object["access_token"] assert access_token # noqa: S101 return access_token # return redirect("localhost:7000/v1.0/ui") diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index 73559590..215ee7e0 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -7,11 +7,14 @@ from typing import Optional import jwt import requests +from flask import g from flask import current_app from flask import redirect from flask_bpmn.api.api_error import ApiError from werkzeug.wrappers.response import Response +from spiffworkflow_backend.models.refresh_token import RefreshTokenModel + class AuthenticationProviderTypes(enum.Enum): """AuthenticationServiceProviders.""" @@ -45,7 +48,7 @@ class PublicAuthenticationService: ) @classmethod - def get_user_info_from_id_token(cls, token: str) -> dict: + def get_user_info_from_open_id_using_id_token(cls, token: str) -> dict: """This seems to work with basic tokens too.""" ( open_id_server_url, @@ -54,10 +57,6 @@ class PublicAuthenticationService: open_id_client_secret_key, ) = cls.get_open_id_args() - # backend_basic_auth_string = f"{open_id_client_id}:{open_id_client_secret_key}" - # backend_basic_auth_bytes = bytes(backend_basic_auth_string, encoding="ascii") - # backend_basic_auth = base64.b64encode(backend_basic_auth_bytes) - headers = {"Authorization": f"Bearer {token}"} request_url = f"{open_id_server_url}/realms/{open_id_realm_name}/protocol/openid-connect/userinfo" @@ -85,7 +84,8 @@ class PublicAuthenticationService: status_code=401, ) - def get_backend_url(self) -> str: + @staticmethod + def get_backend_url() -> str: """Get_backend_url.""" return str(current_app.config["SPIFFWORKFLOW_BACKEND_URL"]) @@ -135,7 +135,7 @@ class PublicAuthenticationService: ) return login_redirect_url - def get_id_token_object( + def get_auth_token_object( self, code: str, redirect_url: str = "/v1.0/login_return" ) -> dict: """Get_id_token_object.""" @@ -162,8 +162,8 @@ class PublicAuthenticationService: request_url = f"{open_id_server_url}/realms/{open_id_realm_name}/protocol/openid-connect/token" response = requests.post(request_url, data=data, headers=headers) - id_token_object: dict = json.loads(response.text) - return id_token_object + auth_token_object: dict = json.loads(response.text) + return auth_token_object @classmethod def validate_id_token(cls, id_token: str) -> bool: @@ -204,10 +204,51 @@ class PublicAuthenticationService: return False if now > decoded_token["exp"]: - raise ApiError( - error_code="invalid_token", - message="Your token is expired. Please Login", - status_code=401, - ) + refresh_token = cls.get_refresh_token(decoded_token['preferred_username']) + if refresh_token: + auth_token = cls.get_auth_token_from_refresh_token(refresh_token) + # TODO: Add redirecting code. Not sure where yet. + else: + raise ApiError( + error_code="invalid_token", + message="Your token is expired. Please Login", + status_code=401, + ) return True + + @staticmethod + def get_refresh_token(user_id: int) -> str: + refresh_token_object = RefreshTokenModel.query.filter(RefreshTokenModel.user_id == user_id).first() + if refresh_token_object: + return refresh_token_object.token + + @classmethod + def get_auth_token_from_refresh_token(cls, refresh_token: str) -> str: + ( + open_id_server_url, + open_id_client_id, + open_id_realm_name, + open_id_client_secret_key, + ) = cls.get_open_id_args() + + backend_basic_auth_string = f"{open_id_client_id}:{open_id_client_secret_key}" + backend_basic_auth_bytes = bytes(backend_basic_auth_string, encoding="ascii") + backend_basic_auth = base64.b64encode(backend_basic_auth_bytes) + headers = { + "Content-Type": "application/x-www-form-urlencoded", + "Authorization": f"Basic {backend_basic_auth.decode('utf-8')}", + } + + data = { + "grant_type": "refresh_token", + "refresh_token": refresh_token, + "client_id": open_id_client_id, + "client_secret": open_id_client_secret_key + } + + request_url = f"{open_id_server_url}/realms/{open_id_realm_name}/protocol/openid-connect/token" + + response = requests.post(request_url, data=data, headers=headers) + print("get_auth_token_from_refresh_token") + return response.text diff --git a/src/spiffworkflow_backend/services/user_service.py b/src/spiffworkflow_backend/services/user_service.py index 3bf7f092..a79a5509 100644 --- a/src/spiffworkflow_backend/services/user_service.py +++ b/src/spiffworkflow_backend/services/user_service.py @@ -9,6 +9,7 @@ from flask_bpmn.models.db import db from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.principal import PrincipalModel +from spiffworkflow_backend.models.refresh_token import RefreshTokenModel from spiffworkflow_backend.models.user import AdminSessionModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user_group_assignment import UserGroupAssignmentModel @@ -299,3 +300,26 @@ class UserService: ugam = UserGroupAssignmentModel(user_id=user.id, group_id=group.id) db.session.add(ugam) db.session.commit() + + @staticmethod + def store_refresh_token(user_id: int, refresh_token: str) -> None: + # TODO: maybe move this to authentication service + refresh_token_model = RefreshTokenModel.query.filter(RefreshTokenModel.user_id == user_id).first() + if refresh_token_model: + refresh_token_model.token = refresh_token + else: + refresh_token_model = RefreshTokenModel(user_id=user_id, + token=refresh_token) + db.session.add(refresh_token_model) + try: + db.session.commit() + except Exception as e: + db.session.rollback() + raise ApiError(error_code="store_refresh_token_error", + message=f"We could not store the refresh token. Original error is {e}") + + @staticmethod + def get_user_by_service_and_service_id(service: str, service_id: str) -> Optional[UserModel]: + user = UserModel.query.filter(UserModel.service == service).filter(UserModel.service_id == service_id).first() + if user: + return user From 9a6700a6d50d357a6a0b38cb8223783bec428027 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 11:28:09 -0400 Subject: [PATCH 04/24] Too many things expect g.token. Reverting my change --- src/spiffworkflow_backend/routes/user.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index f42ed296..86f615fb 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -120,7 +120,7 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i # If the user is valid, store the token for this session if g.user: # This is an id token, so we don't have a refresh token yet - g.id_token = token + g.token = token scope = get_scope(token) return {"uid": g.user.id, "sub": g.user.id, "scope": scope} # return validate_scope(token, user_info, user_model) @@ -217,7 +217,7 @@ def login_return(code: str, state: str, session_state: str) -> Optional[Response if user_model: g.user = user_model.id - g.id_token = auth_token_object['id_token'] + g.token = auth_token_object['id_token'] UserService.store_refresh_token(user_model.id, auth_token_object['refresh_token']) # this may eventually get too slow. From a72b03e092fedcc614190b5e33d8202c0949ada6 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 11:39:22 -0400 Subject: [PATCH 05/24] Rename method. We pass it auth_tokens, not id_tokens --- src/spiffworkflow_backend/routes/user.py | 6 +++--- .../services/authentication_service.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index 86f615fb..8ef8fc80 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -59,7 +59,7 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i elif "iss" in decoded_token.keys(): try: - user_info = PublicAuthenticationService.get_user_info_from_open_id_using_id_token( + user_info = PublicAuthenticationService.get_user_info_from_open_id( token ) except ApiError as ae: @@ -70,7 +70,7 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i auth_token = PublicAuthenticationService.get_auth_token_from_refresh_token(refresh_token) if auth_token and "error" not in auth_token: # redirect to original url, with auth_token? - user_info = PublicAuthenticationService.get_user_info_from_open_id_using_id_token( + user_info = PublicAuthenticationService.get_user_info_from_open_id( auth_token ) if not user_info: @@ -186,7 +186,7 @@ def login_return(code: str, state: str, session_state: str) -> Optional[Response id_token = auth_token_object["id_token"] if PublicAuthenticationService.validate_id_token(id_token): - user_info = PublicAuthenticationService.get_user_info_from_open_id_using_id_token( + user_info = PublicAuthenticationService.get_user_info_from_open_id( auth_token_object["access_token"] ) if user_info and "error" not in user_info: diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index 215ee7e0..84cec645 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -48,8 +48,8 @@ class PublicAuthenticationService: ) @classmethod - def get_user_info_from_open_id_using_id_token(cls, token: str) -> dict: - """This seems to work with basic tokens too.""" + def get_user_info_from_open_id(cls, token: str) -> dict: + """The token is an auth_token.""" ( open_id_server_url, open_id_client_id, From 1e75716ebde17de47d8f7abdc3e3ad8002e3ffae Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 11:46:39 -0400 Subject: [PATCH 06/24] Pre commit --- bin/wait_for_db_to_be_ready.py | 11 +++++--- .../models/refresh_token.py | 3 +++ src/spiffworkflow_backend/routes/user.py | 24 ++++++++++++----- .../services/authentication_service.py | 13 +++++---- .../services/user_service.py | 27 ++++++++++++++----- 5 files changed, 55 insertions(+), 23 deletions(-) diff --git a/bin/wait_for_db_to_be_ready.py b/bin/wait_for_db_to_be_ready.py index 00903a93..40497ae9 100644 --- a/bin/wait_for_db_to_be_ready.py +++ b/bin/wait_for_db_to_be_ready.py @@ -1,14 +1,16 @@ """Grabs tickets from csv and makes process instances.""" +import time + +import sqlalchemy +from flask_bpmn.models.db import db from spiffworkflow_backend import get_hacked_up_app_for_script -from flask_bpmn.models.db import db -import sqlalchemy -import time def try_to_connect(start_time: float) -> None: + """Try to connect.""" try: - db.first_or_404('select 1') + db.first_or_404("select 1") except sqlalchemy.exc.DatabaseError as exception: if time.time() - start_time > 15: raise exception @@ -16,6 +18,7 @@ def try_to_connect(start_time: float) -> None: time.sleep(1) try_to_connect(start_time) + def main() -> None: """Main.""" app = get_hacked_up_app_for_script() diff --git a/src/spiffworkflow_backend/models/refresh_token.py b/src/spiffworkflow_backend/models/refresh_token.py index ad571601..2e96b7f0 100644 --- a/src/spiffworkflow_backend/models/refresh_token.py +++ b/src/spiffworkflow_backend/models/refresh_token.py @@ -1,7 +1,10 @@ +"""Refresh_token.""" from dataclasses import dataclass + from flask_bpmn.models.db import db from flask_bpmn.models.db import SpiffworkflowBaseDBModel from sqlalchemy import ForeignKey + # from sqlalchemy.orm import relationship # from spiffworkflow_backend.models.user import UserModel diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index 8ef8fc80..e6713b4f 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -64,14 +64,22 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i ) except ApiError as ae: # Try to refresh the token - user = UserService.get_user_by_service_and_service_id('open_id', decoded_token['sub']) - refresh_token = PublicAuthenticationService.get_refresh_token(user.id) + user = UserService.get_user_by_service_and_service_id( + "open_id", decoded_token["sub"] + ) + refresh_token = PublicAuthenticationService.get_refresh_token( + user.id + ) if refresh_token: - auth_token = PublicAuthenticationService.get_auth_token_from_refresh_token(refresh_token) + auth_token = PublicAuthenticationService.get_auth_token_from_refresh_token( + refresh_token + ) if auth_token and "error" not in auth_token: # redirect to original url, with auth_token? - user_info = PublicAuthenticationService.get_user_info_from_open_id( - auth_token + user_info = ( + PublicAuthenticationService.get_user_info_from_open_id( + auth_token + ) ) if not user_info: raise ae @@ -217,8 +225,10 @@ def login_return(code: str, state: str, session_state: str) -> Optional[Response if user_model: g.user = user_model.id - g.token = auth_token_object['id_token'] - UserService.store_refresh_token(user_model.id, auth_token_object['refresh_token']) + g.token = auth_token_object["id_token"] + UserService.store_refresh_token( + user_model.id, auth_token_object["refresh_token"] + ) # this may eventually get too slow. # when it does, be careful about backgrounding, because diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index 84cec645..40c78b17 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -7,7 +7,6 @@ from typing import Optional import jwt import requests -from flask import g from flask import current_app from flask import redirect from flask_bpmn.api.api_error import ApiError @@ -204,9 +203,9 @@ class PublicAuthenticationService: return False if now > decoded_token["exp"]: - refresh_token = cls.get_refresh_token(decoded_token['preferred_username']) + refresh_token = cls.get_refresh_token(decoded_token["preferred_username"]) if refresh_token: - auth_token = cls.get_auth_token_from_refresh_token(refresh_token) + cls.get_auth_token_from_refresh_token(refresh_token) # TODO: Add redirecting code. Not sure where yet. else: raise ApiError( @@ -219,12 +218,16 @@ class PublicAuthenticationService: @staticmethod def get_refresh_token(user_id: int) -> str: - refresh_token_object = RefreshTokenModel.query.filter(RefreshTokenModel.user_id == user_id).first() + """Get_refresh_token.""" + refresh_token_object = RefreshTokenModel.query.filter( + RefreshTokenModel.user_id == user_id + ).first() if refresh_token_object: return refresh_token_object.token @classmethod def get_auth_token_from_refresh_token(cls, refresh_token: str) -> str: + """Get a new auth_token from a refresh_token.""" ( open_id_server_url, open_id_client_id, @@ -244,7 +247,7 @@ class PublicAuthenticationService: "grant_type": "refresh_token", "refresh_token": refresh_token, "client_id": open_id_client_id, - "client_secret": open_id_client_secret_key + "client_secret": open_id_client_secret_key, } request_url = f"{open_id_server_url}/realms/{open_id_realm_name}/protocol/openid-connect/token" diff --git a/src/spiffworkflow_backend/services/user_service.py b/src/spiffworkflow_backend/services/user_service.py index a79a5509..a00cc928 100644 --- a/src/spiffworkflow_backend/services/user_service.py +++ b/src/spiffworkflow_backend/services/user_service.py @@ -303,23 +303,36 @@ class UserService: @staticmethod def store_refresh_token(user_id: int, refresh_token: str) -> None: + """Store_refresh_token.""" # TODO: maybe move this to authentication service - refresh_token_model = RefreshTokenModel.query.filter(RefreshTokenModel.user_id == user_id).first() + refresh_token_model = RefreshTokenModel.query.filter( + RefreshTokenModel.user_id == user_id + ).first() if refresh_token_model: refresh_token_model.token = refresh_token else: - refresh_token_model = RefreshTokenModel(user_id=user_id, - token=refresh_token) + refresh_token_model = RefreshTokenModel( + user_id=user_id, token=refresh_token + ) db.session.add(refresh_token_model) try: db.session.commit() except Exception as e: db.session.rollback() - raise ApiError(error_code="store_refresh_token_error", - message=f"We could not store the refresh token. Original error is {e}") + raise ApiError( + error_code="store_refresh_token_error", + message=f"We could not store the refresh token. Original error is {e}", + ) from e @staticmethod - def get_user_by_service_and_service_id(service: str, service_id: str) -> Optional[UserModel]: - user = UserModel.query.filter(UserModel.service == service).filter(UserModel.service_id == service_id).first() + def get_user_by_service_and_service_id( + service: str, service_id: str + ) -> Optional[UserModel]: + """Get_user_by_service_and_service_id.""" + user = ( + UserModel.query.filter(UserModel.service == service) + .filter(UserModel.service_id == service_id) + .first() + ) if user: return user From c4e415dbee0f01bf49e767220ba86057bf87a03a Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 11:57:58 -0400 Subject: [PATCH 07/24] mypy --- src/spiffworkflow_backend/routes/user.py | 25 +++++++++---------- .../services/authentication_service.py | 8 +++--- .../services/user_service.py | 6 ++--- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index e6713b4f..92466a96 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -67,22 +67,21 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i user = UserService.get_user_by_service_and_service_id( "open_id", decoded_token["sub"] ) - refresh_token = PublicAuthenticationService.get_refresh_token( - user.id - ) - if refresh_token: - auth_token = PublicAuthenticationService.get_auth_token_from_refresh_token( - refresh_token + if user: + refresh_token = PublicAuthenticationService.get_refresh_token( + user.id ) - if auth_token and "error" not in auth_token: - # redirect to original url, with auth_token? - user_info = ( - PublicAuthenticationService.get_user_info_from_open_id( + if refresh_token: + auth_token = PublicAuthenticationService.get_auth_token_from_refresh_token( + refresh_token + ) + if auth_token and "error" not in auth_token: + # redirect to original url, with auth_token? + user_info = PublicAuthenticationService.get_user_info_from_open_id( auth_token ) - ) - if not user_info: - raise ae + if not user_info: + raise ae else: raise ae except Exception as e: diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index 40c78b17..d753437a 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -217,13 +217,13 @@ class PublicAuthenticationService: return True @staticmethod - def get_refresh_token(user_id: int) -> str: + def get_refresh_token(user_id: int) -> Optional[str]: """Get_refresh_token.""" - refresh_token_object = RefreshTokenModel.query.filter( + refresh_token_object: RefreshTokenModel = RefreshTokenModel.query.filter( RefreshTokenModel.user_id == user_id ).first() - if refresh_token_object: - return refresh_token_object.token + assert refresh_token_object + return refresh_token_object.token @classmethod def get_auth_token_from_refresh_token(cls, refresh_token: str) -> str: diff --git a/src/spiffworkflow_backend/services/user_service.py b/src/spiffworkflow_backend/services/user_service.py index a00cc928..f325205a 100644 --- a/src/spiffworkflow_backend/services/user_service.py +++ b/src/spiffworkflow_backend/services/user_service.py @@ -329,10 +329,10 @@ class UserService: service: str, service_id: str ) -> Optional[UserModel]: """Get_user_by_service_and_service_id.""" - user = ( + user: UserModel = ( UserModel.query.filter(UserModel.service == service) .filter(UserModel.service_id == service_id) .first() ) - if user: - return user + assert user + return user From 00d66e9c515e2f834357fd3d2108a9d1bf60412f Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 12:02:29 -0400 Subject: [PATCH 08/24] mypy --- src/spiffworkflow_backend/services/authentication_service.py | 2 +- src/spiffworkflow_backend/services/user_service.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index d753437a..ce22c3cb 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -222,7 +222,7 @@ class PublicAuthenticationService: refresh_token_object: RefreshTokenModel = RefreshTokenModel.query.filter( RefreshTokenModel.user_id == user_id ).first() - assert refresh_token_object + assert refresh_token_object # noqa: S101 return refresh_token_object.token @classmethod diff --git a/src/spiffworkflow_backend/services/user_service.py b/src/spiffworkflow_backend/services/user_service.py index f325205a..b01e4a4b 100644 --- a/src/spiffworkflow_backend/services/user_service.py +++ b/src/spiffworkflow_backend/services/user_service.py @@ -334,5 +334,5 @@ class UserService: .filter(UserModel.service_id == service_id) .first() ) - assert user + assert user # noqa: S101 return user From fc94774bb53da42672858d1c2ea03411065475eb Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 12:37:22 -0400 Subject: [PATCH 09/24] Move `store_refresh_token` to authentication_service --- src/spiffworkflow_backend/routes/user.py | 2 +- .../services/authentication_service.py | 23 +++++++++++++++++++ .../services/user_service.py | 23 ------------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index 92466a96..cb12f425 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -225,7 +225,7 @@ def login_return(code: str, state: str, session_state: str) -> Optional[Response if user_model: g.user = user_model.id g.token = auth_token_object["id_token"] - UserService.store_refresh_token( + PublicAuthenticationService.store_refresh_token( user_model.id, auth_token_object["refresh_token"] ) diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index ce22c3cb..4b9c45e9 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -10,6 +10,7 @@ import requests from flask import current_app from flask import redirect from flask_bpmn.api.api_error import ApiError +from flask_bpmn.models.db import db from werkzeug.wrappers.response import Response from spiffworkflow_backend.models.refresh_token import RefreshTokenModel @@ -216,6 +217,28 @@ class PublicAuthenticationService: return True + @staticmethod + def store_refresh_token(user_id: int, refresh_token: str) -> None: + """Store_refresh_token.""" + refresh_token_model = RefreshTokenModel.query.filter( + RefreshTokenModel.user_id == user_id + ).first() + if refresh_token_model: + refresh_token_model.token = refresh_token + else: + refresh_token_model = RefreshTokenModel( + user_id=user_id, token=refresh_token + ) + db.session.add(refresh_token_model) + try: + db.session.commit() + except Exception as e: + db.session.rollback() + raise ApiError( + error_code="store_refresh_token_error", + message=f"We could not store the refresh token. Original error is {e}", + ) from e + @staticmethod def get_refresh_token(user_id: int) -> Optional[str]: """Get_refresh_token.""" diff --git a/src/spiffworkflow_backend/services/user_service.py b/src/spiffworkflow_backend/services/user_service.py index b01e4a4b..c5594218 100644 --- a/src/spiffworkflow_backend/services/user_service.py +++ b/src/spiffworkflow_backend/services/user_service.py @@ -301,29 +301,6 @@ class UserService: db.session.add(ugam) db.session.commit() - @staticmethod - def store_refresh_token(user_id: int, refresh_token: str) -> None: - """Store_refresh_token.""" - # TODO: maybe move this to authentication service - refresh_token_model = RefreshTokenModel.query.filter( - RefreshTokenModel.user_id == user_id - ).first() - if refresh_token_model: - refresh_token_model.token = refresh_token - else: - refresh_token_model = RefreshTokenModel( - user_id=user_id, token=refresh_token - ) - db.session.add(refresh_token_model) - try: - db.session.commit() - except Exception as e: - db.session.rollback() - raise ApiError( - error_code="store_refresh_token_error", - message=f"We could not store the refresh token. Original error is {e}", - ) from e - @staticmethod def get_user_by_service_and_service_id( service: str, service_id: str From 91b8649f86a7abba9bc041660d35e995c23c36c4 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 12:37:55 -0400 Subject: [PATCH 10/24] id_token -> auth_token --- src/spiffworkflow_backend/services/authentication_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index 4b9c45e9..629d30e7 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -138,7 +138,7 @@ class PublicAuthenticationService: def get_auth_token_object( self, code: str, redirect_url: str = "/v1.0/login_return" ) -> dict: - """Get_id_token_object.""" + """Get_auth_token_object.""" ( open_id_server_url, open_id_client_id, From 6c491a3df3c5eac0b57e74e0a5c1de5919011b0f Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 12:38:47 -0400 Subject: [PATCH 11/24] Don't refresh token here. They just logged in. We are validating the returned token. If it is bad, raise an error. --- .../services/authentication_service.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index 629d30e7..bd4f2c0d 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -204,16 +204,11 @@ class PublicAuthenticationService: return False if now > decoded_token["exp"]: - refresh_token = cls.get_refresh_token(decoded_token["preferred_username"]) - if refresh_token: - cls.get_auth_token_from_refresh_token(refresh_token) - # TODO: Add redirecting code. Not sure where yet. - else: - raise ApiError( - error_code="invalid_token", - message="Your token is expired. Please Login", - status_code=401, - ) + raise ApiError( + error_code="invalid_token", + message="Your token is expired. Please Login", + status_code=401, + ) return True From 1f443bb9467cdde5e4a422699e436a3088a2c45f Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 12:43:17 -0400 Subject: [PATCH 12/24] PublicAuthenticationService -> AuthenticationService --- src/spiffworkflow_backend/routes/user.py | 42 +++++++++---------- .../services/authentication_service.py | 15 +++---- .../helpers/base_test.py | 2 +- .../integration/test_authentication.py | 8 ++-- .../integration/test_authorization.py | 12 +++--- 5 files changed, 37 insertions(+), 42 deletions(-) diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index cb12f425..775ef66c 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -15,7 +15,7 @@ from werkzeug.wrappers.response import Response from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.authentication_service import ( - PublicAuthenticationService, + AuthenticationService, ) from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.user_service import UserService @@ -59,7 +59,7 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i elif "iss" in decoded_token.keys(): try: - user_info = PublicAuthenticationService.get_user_info_from_open_id( + user_info = AuthenticationService.get_user_info_from_open_id( token ) except ApiError as ae: @@ -68,16 +68,16 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i "open_id", decoded_token["sub"] ) if user: - refresh_token = PublicAuthenticationService.get_refresh_token( + refresh_token = AuthenticationService.get_refresh_token( user.id ) if refresh_token: - auth_token = PublicAuthenticationService.get_auth_token_from_refresh_token( + auth_token = AuthenticationService.get_auth_token_from_refresh_token( refresh_token ) if auth_token and "error" not in auth_token: # redirect to original url, with auth_token? - user_info = PublicAuthenticationService.get_user_info_from_open_id( + user_info = AuthenticationService.get_user_info_from_open_id( auth_token ) if not user_info: @@ -142,12 +142,12 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i def validate_scope(token: Any) -> bool: """Validate_scope.""" print("validate_scope") - # token = PublicAuthenticationService.refresh_token(token) - # user_info = PublicAuthenticationService.get_user_info_from_public_access_token(token) - # bearer_token = PublicAuthenticationService.get_bearer_token(token) - # permission = PublicAuthenticationService.get_permission_by_basic_token(token) - # permissions = PublicAuthenticationService.get_permissions_by_token_for_resource_and_scope(token) - # introspection = PublicAuthenticationService.introspect_token(basic_token) + # token = AuthenticationService.refresh_token(token) + # user_info = AuthenticationService.get_user_info_from_public_access_token(token) + # bearer_token = AuthenticationService.get_bearer_token(token) + # permission = AuthenticationService.get_permission_by_basic_token(token) + # permissions = AuthenticationService.get_permissions_by_token_for_resource_and_scope(token) + # introspection = AuthenticationService.introspect_token(basic_token) return True @@ -176,8 +176,8 @@ def encode_auth_token(sub: str, token_type: Optional[str] = None) -> str: def login(redirect_url: str = "/") -> Response: """Login.""" - state = PublicAuthenticationService.generate_state(redirect_url) - login_redirect_url = PublicAuthenticationService().get_login_redirect_url( + state = AuthenticationService.generate_state(redirect_url) + login_redirect_url = AuthenticationService().get_login_redirect_url( state.decode("UTF-8") ) return redirect(login_redirect_url) @@ -188,12 +188,12 @@ def login_return(code: str, state: str, session_state: str) -> Optional[Response state_dict = ast.literal_eval(base64.b64decode(state).decode("utf-8")) state_redirect_url = state_dict["redirect_url"] - auth_token_object = PublicAuthenticationService().get_auth_token_object(code) + auth_token_object = AuthenticationService().get_auth_token_object(code) if "id_token" in auth_token_object: id_token = auth_token_object["id_token"] - if PublicAuthenticationService.validate_id_token(id_token): - user_info = PublicAuthenticationService.get_user_info_from_open_id( + if AuthenticationService.validate_id_token(id_token): + user_info = AuthenticationService.get_user_info_from_open_id( auth_token_object["access_token"] ) if user_info and "error" not in user_info: @@ -225,7 +225,7 @@ def login_return(code: str, state: str, session_state: str) -> Optional[Response if user_model: g.user = user_model.id g.token = auth_token_object["id_token"] - PublicAuthenticationService.store_refresh_token( + AuthenticationService.store_refresh_token( user_model.id, auth_token_object["refresh_token"] ) @@ -261,8 +261,8 @@ def login_return(code: str, state: str, session_state: str) -> Optional[Response def login_api() -> Response: """Login_api.""" redirect_url = "/v1.0/login_api_return" - state = PublicAuthenticationService.generate_state(redirect_url) - login_redirect_url = PublicAuthenticationService().get_login_redirect_url( + state = AuthenticationService.generate_state(redirect_url) + login_redirect_url = AuthenticationService().get_login_redirect_url( state.decode("UTF-8"), redirect_url ) return redirect(login_redirect_url) @@ -273,7 +273,7 @@ def login_api_return(code: str, state: str, session_state: str) -> str: state_dict = ast.literal_eval(base64.b64decode(state).decode("utf-8")) state_dict["redirect_url"] - auth_token_object = PublicAuthenticationService().get_auth_token_object( + auth_token_object = AuthenticationService().get_auth_token_object( code, "/v1.0/login_api_return" ) access_token: str = auth_token_object["access_token"] @@ -287,7 +287,7 @@ def logout(id_token: str, redirect_url: Optional[str]) -> Response: """Logout.""" if redirect_url is None: redirect_url = "" - return PublicAuthenticationService().logout( + return AuthenticationService().logout( redirect_url=redirect_url, id_token=id_token ) diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index bd4f2c0d..8b0aa2e8 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -23,13 +23,8 @@ class AuthenticationProviderTypes(enum.Enum): internal = "internal" -class PublicAuthenticationService: - """PublicAuthenticationService.""" - - """Not sure where/if this ultimately lives. - It uses a separate public open_id client: spiffworkflow-frontend - Used during development to make testing easy. - """ +class AuthenticationService: + """AuthenticationService.""" @staticmethod def get_open_id_args() -> tuple: @@ -99,7 +94,7 @@ class PublicAuthenticationService: open_id_client_id, open_id_realm_name, open_id_client_secret_key, - ) = PublicAuthenticationService.get_open_id_args() + ) = AuthenticationService.get_open_id_args() request_url = ( f"{open_id_server_url}/realms/{open_id_realm_name}/protocol/openid-connect/logout?" + f"post_logout_redirect_uri={return_redirect_url}&" @@ -123,7 +118,7 @@ class PublicAuthenticationService: open_id_client_id, open_id_realm_name, open_id_client_secret_key, - ) = PublicAuthenticationService.get_open_id_args() + ) = AuthenticationService.get_open_id_args() return_redirect_url = f"{self.get_backend_url()}{redirect_url}" login_redirect_url = ( f"{open_id_server_url}/realms/{open_id_realm_name}/protocol/openid-connect/auth?" @@ -144,7 +139,7 @@ class PublicAuthenticationService: open_id_client_id, open_id_realm_name, open_id_client_secret_key, - ) = PublicAuthenticationService.get_open_id_args() + ) = AuthenticationService.get_open_id_args() backend_basic_auth_string = f"{open_id_client_id}:{open_id_client_secret_key}" backend_basic_auth_bytes = bytes(backend_basic_auth_string, encoding="ascii") diff --git a/tests/spiffworkflow_backend/helpers/base_test.py b/tests/spiffworkflow_backend/helpers/base_test.py index f323a497..6bbe7814 100644 --- a/tests/spiffworkflow_backend/helpers/base_test.py +++ b/tests/spiffworkflow_backend/helpers/base_test.py @@ -198,7 +198,7 @@ class BaseTest: # @staticmethod # def get_public_access_token(username: str, password: str) -> dict: # """Get_public_access_token.""" - # public_access_token = PublicAuthenticationService().get_public_access_token( + # public_access_token = AuthenticationService().get_public_access_token( # username, password # ) # return public_access_token diff --git a/tests/spiffworkflow_backend/integration/test_authentication.py b/tests/spiffworkflow_backend/integration/test_authentication.py index 934c1b24..34e4d71b 100644 --- a/tests/spiffworkflow_backend/integration/test_authentication.py +++ b/tests/spiffworkflow_backend/integration/test_authentication.py @@ -5,7 +5,7 @@ import base64 from tests.spiffworkflow_backend.helpers.base_test import BaseTest from spiffworkflow_backend.services.authentication_service import ( - PublicAuthenticationService, + AuthenticationService, ) @@ -15,7 +15,7 @@ class TestAuthentication(BaseTest): def test_get_login_state(self) -> None: """Test_get_login_state.""" redirect_url = "http://example.com/" - state = PublicAuthenticationService.generate_state(redirect_url) + state = AuthenticationService.generate_state(redirect_url) state_dict = ast.literal_eval(base64.b64decode(state).decode("utf-8")) assert isinstance(state_dict, dict) @@ -24,9 +24,9 @@ class TestAuthentication(BaseTest): # def test_get_login_redirect_url(self): # redirect_url = "http://example.com/" - # state = PublicAuthenticationService.generate_state(redirect_url) + # state = AuthenticationService.generate_state(redirect_url) # with current_app.app_context(): - # login_redirect_url = PublicAuthenticationService().get_login_redirect_url(state.decode("UTF-8")) + # login_redirect_url = AuthenticationService().get_login_redirect_url(state.decode("UTF-8")) # print("test_get_login_redirect_url") # print("test_get_login_redirect_url") diff --git a/tests/spiffworkflow_backend/integration/test_authorization.py b/tests/spiffworkflow_backend/integration/test_authorization.py index 912e039a..51eae72e 100644 --- a/tests/spiffworkflow_backend/integration/test_authorization.py +++ b/tests/spiffworkflow_backend/integration/test_authorization.py @@ -9,7 +9,7 @@ class TestAuthorization(BaseTest): # """Test_get_bearer_token.""" # for user_id in ("user_1", "user_2", "admin_1", "admin_2"): # public_access_token = self.get_public_access_token(user_id, user_id) - # bearer_token = PublicAuthenticationService.get_bearer_token(public_access_token) + # bearer_token = AuthenticationService.get_bearer_token(public_access_token) # assert isinstance(public_access_token, str) # assert isinstance(bearer_token, dict) # assert "access_token" in bearer_token @@ -25,7 +25,7 @@ class TestAuthorization(BaseTest): # """Test_get_user_info_from_public_access_token.""" # for user_id in ("user_1", "user_2", "admin_1", "admin_2"): # public_access_token = self.get_public_access_token(user_id, user_id) - # user_info = PublicAuthenticationService.get_user_info_from_id_token( + # user_info = AuthenticationService.get_user_info_from_id_token( # public_access_token # ) # assert "sub" in user_info @@ -46,7 +46,7 @@ class TestAuthorization(BaseTest): # ) = self.get_keycloak_constants(app) # for user_id in ("user_1", "user_2", "admin_1", "admin_2"): # basic_token = self.get_public_access_token(user_id, user_id) - # introspection = PublicAuthenticationService.introspect_token(basic_token) + # introspection = AuthenticationService.introspect_token(basic_token) # assert isinstance(introspection, dict) # assert introspection["typ"] == "Bearer" # assert introspection["preferred_username"] == user_id @@ -80,7 +80,7 @@ class TestAuthorization(BaseTest): # for user_id in ("user_1", "user_2", "admin_1", "admin_2"): # output[user_id] = {} # basic_token = self.get_public_access_token(user_id, user_id) - # permissions = PublicAuthenticationService.get_permission_by_basic_token( + # permissions = AuthenticationService.get_permission_by_basic_token( # basic_token # ) # if isinstance(permissions, list): @@ -136,7 +136,7 @@ class TestAuthorization(BaseTest): # for resource in resources: # output[user_id][resource] = {} # for scope in "instantiate", "read", "update", "delete": - # auth_status = PublicAuthenticationService.get_auth_status_for_resource_and_scope_by_token( + # auth_status = AuthenticationService.get_auth_status_for_resource_and_scope_by_token( # basic_token, resource, scope # ) # output[user_id][resource][scope] = auth_status @@ -152,7 +152,7 @@ class TestAuthorization(BaseTest): # for resource in resource_names: # output[user_id][resource] = {} # for scope in "instantiate", "read", "update", "delete": - # permissions = PublicAuthenticationService.get_permissions_by_token_for_resource_and_scope( + # permissions = AuthenticationService.get_permissions_by_token_for_resource_and_scope( # basic_token, resource, scope # ) # output[user_id][resource][scope] = permissions From f163de61c2af2bf613b497c972889792a96fc59f Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 12:54:29 -0400 Subject: [PATCH 13/24] pyl --- src/spiffworkflow_backend/routes/user.py | 24 +++++++++---------- .../services/user_service.py | 1 - 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index 775ef66c..9133ef7a 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -59,26 +59,26 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i elif "iss" in decoded_token.keys(): try: - user_info = AuthenticationService.get_user_info_from_open_id( - token - ) + user_info = AuthenticationService.get_user_info_from_open_id(token) except ApiError as ae: # Try to refresh the token user = UserService.get_user_by_service_and_service_id( "open_id", decoded_token["sub"] ) if user: - refresh_token = AuthenticationService.get_refresh_token( - user.id - ) + refresh_token = AuthenticationService.get_refresh_token(user.id) if refresh_token: - auth_token = AuthenticationService.get_auth_token_from_refresh_token( - refresh_token + auth_token = ( + AuthenticationService.get_auth_token_from_refresh_token( + refresh_token + ) ) if auth_token and "error" not in auth_token: # redirect to original url, with auth_token? - user_info = AuthenticationService.get_user_info_from_open_id( - auth_token + user_info = ( + AuthenticationService.get_user_info_from_open_id( + auth_token + ) ) if not user_info: raise ae @@ -287,9 +287,7 @@ def logout(id_token: str, redirect_url: Optional[str]) -> Response: """Logout.""" if redirect_url is None: redirect_url = "" - return AuthenticationService().logout( - redirect_url=redirect_url, id_token=id_token - ) + return AuthenticationService().logout(redirect_url=redirect_url, id_token=id_token) def logout_return() -> Response: diff --git a/src/spiffworkflow_backend/services/user_service.py b/src/spiffworkflow_backend/services/user_service.py index c5594218..7ea90e9f 100644 --- a/src/spiffworkflow_backend/services/user_service.py +++ b/src/spiffworkflow_backend/services/user_service.py @@ -9,7 +9,6 @@ from flask_bpmn.models.db import db from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.principal import PrincipalModel -from spiffworkflow_backend.models.refresh_token import RefreshTokenModel from spiffworkflow_backend.models.user import AdminSessionModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user_group_assignment import UserGroupAssignmentModel From 87f65a6c62730b3775c8c01a4c2d7d8d0f8e3ed4 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 16:56:31 -0400 Subject: [PATCH 14/24] auth_token should be dictionary, not string --- src/spiffworkflow_backend/routes/user.py | 6 +++++- .../services/authentication_service.py | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index 9133ef7a..ae75ec50 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -77,11 +77,15 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i # redirect to original url, with auth_token? user_info = ( AuthenticationService.get_user_info_from_open_id( - auth_token + auth_token['access_token'] ) ) if not user_info: raise ae + else: + raise ae + else: + raise ae else: raise ae except Exception as e: diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index 8b0aa2e8..5d248db7 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -266,5 +266,5 @@ class AuthenticationService: request_url = f"{open_id_server_url}/realms/{open_id_realm_name}/protocol/openid-connect/token" response = requests.post(request_url, data=data, headers=headers) - print("get_auth_token_from_refresh_token") - return response.text + auth_token_object: dict = json.loads(response.text) + return auth_token_object From 201a6918a05c738d69f050530f4b70f5abbac26f Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 17 Oct 2022 17:03:34 -0400 Subject: [PATCH 15/24] pyl changes --- src/spiffworkflow_backend/__init__.py | 2 +- src/spiffworkflow_backend/routes/user.py | 4 ++-- src/spiffworkflow_backend/services/authentication_service.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/spiffworkflow_backend/__init__.py b/src/spiffworkflow_backend/__init__.py index b5438b2c..817ef321 100644 --- a/src/spiffworkflow_backend/__init__.py +++ b/src/spiffworkflow_backend/__init__.py @@ -148,7 +148,7 @@ def configure_sentry(app: flask.app.Flask) -> None: integrations=[ FlaskIntegration(), ], - environment=app.config['ENV_IDENTIFIER'], + environment=app.config["ENV_IDENTIFIER"], # Set traces_sample_rate to 1.0 to capture 100% # of transactions for performance monitoring. # We recommend adjusting this value in production. diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index ae75ec50..92c96f53 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -68,7 +68,7 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i if user: refresh_token = AuthenticationService.get_refresh_token(user.id) if refresh_token: - auth_token = ( + auth_token: dict = ( AuthenticationService.get_auth_token_from_refresh_token( refresh_token ) @@ -77,7 +77,7 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[Union[str, i # redirect to original url, with auth_token? user_info = ( AuthenticationService.get_user_info_from_open_id( - auth_token['access_token'] + auth_token["access_token"] ) ) if not user_info: diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index 5d248db7..c4a7a5e3 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -239,7 +239,7 @@ class AuthenticationService: return refresh_token_object.token @classmethod - def get_auth_token_from_refresh_token(cls, refresh_token: str) -> str: + def get_auth_token_from_refresh_token(cls, refresh_token: str) -> dict: """Get a new auth_token from a refresh_token.""" ( open_id_server_url, From 3ddaa5d08899dac2da62351220668d0dc9235b95 Mon Sep 17 00:00:00 2001 From: burnettk Date: Wed, 19 Oct 2022 22:01:39 -0400 Subject: [PATCH 16/24] lib updates and mypy --- bin/import_tickets_for_script_task.py | 2 +- poetry.lock | 479 +++++++++--------- pyproject.toml | 3 +- .../services/secret_service.py | 4 +- .../services/user_service.py | 2 +- .../integration/test_process_api.py | 5 + 6 files changed, 264 insertions(+), 231 deletions(-) diff --git a/bin/import_tickets_for_script_task.py b/bin/import_tickets_for_script_task.py index f747c5f7..f8977978 100644 --- a/bin/import_tickets_for_script_task.py +++ b/bin/import_tickets_for_script_task.py @@ -91,7 +91,7 @@ def main(): # you at least need a month, or else this row in the csv is considered garbage month_value = processor.bpmn_process_instance.data["month"] if month_value == "" or month_value is None: - db.delete(process_instance) + db.session.delete(process_instance) db.session.commit() continue diff --git a/poetry.lock b/poetry.lock index 0c85c676..8c6ec149 100644 --- a/poetry.lock +++ b/poetry.lock @@ -72,7 +72,7 @@ zookeeper = ["kazoo"] [[package]] name = "astroid" -version = "2.12.10" +version = "2.12.12" description = "An abstract syntax tree for Python with inference support." category = "main" optional = false @@ -129,7 +129,7 @@ yaml = ["PyYAML"] [[package]] name = "bcrypt" -version = "4.0.0" +version = "4.0.1" description = "Modern password hashing for your software and your servers" category = "main" optional = false @@ -164,11 +164,11 @@ python-versions = "*" [[package]] name = "black" -version = "22.8.0" +version = "22.10.0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7" [package.dependencies] click = ">=8.0.0" @@ -717,7 +717,7 @@ docs = ["sphinx"] [[package]] name = "flask-sqlalchemy" -version = "3.0.0" +version = "3.0.2" description = "Add SQLAlchemy support to your Flask application." category = "main" optional = false @@ -754,7 +754,7 @@ smmap = ">=3.0.1,<6" [[package]] name = "GitPython" -version = "3.1.27" +version = "3.1.29" description = "GitPython is a python library used to interact with Git repositories" category = "dev" optional = false @@ -765,7 +765,7 @@ gitdb = ">=4.0.1,<5" [[package]] name = "greenlet" -version = "1.1.3" +version = "1.1.3.post0" description = "Lightweight in-process concurrent programming" category = "main" optional = false @@ -820,7 +820,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "importlib-metadata" -version = "4.13.0" +version = "5.0.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -1085,7 +1085,7 @@ python-versions = "*" [[package]] name = "mysql-connector-python" -version = "8.0.30" +version = "8.0.31" description = "MySQL driver written in Python" category = "main" optional = false @@ -1097,7 +1097,7 @@ protobuf = ">=3.11.0,<=3.20.1" [package.extras] compression = ["lz4 (>=2.1.6,<=3.1.3)", "zstandard (>=0.12.0,<=0.15.2)"] dns-srv = ["dnspython (>=1.16.0,<=2.1.0)"] -gssapi = ["gssapi (>=1.6.9,<=1.7.3)"] +gssapi = ["gssapi (>=1.6.9,<=1.8.1)"] [[package]] name = "nodeenv" @@ -1229,7 +1229,7 @@ python-versions = ">=3.7" [[package]] name = "psycopg2" -version = "2.9.3" +version = "2.9.4" description = "psycopg2 - Python-PostgreSQL Database Adapter" category = "main" optional = false @@ -1381,7 +1381,7 @@ tests = ["psycopg2-binary", "pytest (>=6.0.1)", "pytest-postgresql (>=2.4.0,<4.0 [[package]] name = "pytest-mock" -version = "3.9.0" +version = "3.10.0" description = "Thin-wrapper around the mock package for easier use with pytest" category = "main" optional = false @@ -1441,7 +1441,7 @@ docs = ["Sphinx (>=5.0.2,<6.0.0)", "alabaster (>=0.7.12,<0.8.0)", "commonmark (> [[package]] name = "pytz" -version = "2022.4" +version = "2022.5" description = "World timezone definitions, modern and historical" category = "main" optional = false @@ -1487,7 +1487,7 @@ python-versions = ">=3.6" [[package]] name = "reorder-python-imports" -version = "3.8.3" +version = "3.8.5" description = "Tool for reordering python imports" category = "dev" optional = false @@ -1636,7 +1636,7 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "65.4.1" +version = "65.5.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false @@ -1681,7 +1681,7 @@ python-versions = ">=3.6" [[package]] name = "Sphinx" -version = "5.2.3" +version = "5.3.0" description = "Python documentation generator" category = "main" optional = false @@ -1847,7 +1847,7 @@ test = ["pytest"] [[package]] name = "SpiffWorkflow" -version = "1.2.0" +version = "1.2.1" description = "A workflow framework and BPMN/DMN Processor" category = "main" optional = false @@ -1858,7 +1858,6 @@ develop = false celery = "*" configparser = "*" dateparser = "*" -importlib-metadata = "<5.0" lxml = "*" pytz = "*" @@ -1870,7 +1869,7 @@ resolved_reference = "a094adad8767f82e9c5fa806a46597e066252a72" [[package]] name = "SQLAlchemy" -version = "1.4.41" +version = "1.4.42" description = "Database Abstraction Library" category = "main" optional = false @@ -1903,18 +1902,25 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "sqlalchemy-stubs" version = "0.4" -description = "SQLAlchemy stubs and mypy plugin" +description = "" category = "dev" optional = false python-versions = "*" +develop = false [package.dependencies] mypy = ">=0.790" typing-extensions = ">=3.7.4" +[package.source] +type = "git" +url = "https://github.com/dropbox/sqlalchemy-stubs.git" +reference = "master" +resolved_reference = "0e59ea37e7909ccf0eebbe9a1c0ee60827ac6870" + [[package]] name = "stevedore" -version = "4.0.0" +version = "4.0.1" description = "Manage dynamic plugins for Python applications" category = "dev" optional = false @@ -2020,7 +2026,7 @@ python-versions = "*" [[package]] name = "types-pytz" -version = "2022.4.0.0" +version = "2022.5.0.0" description = "Typing stubs for pytz" category = "dev" optional = false @@ -2030,13 +2036,13 @@ python-versions = "*" name = "types-PyYAML" version = "6.0.12" description = "Typing stubs for PyYAML" -category = "main" +category = "dev" optional = false python-versions = "*" [[package]] name = "types-requests" -version = "2.28.11.1" +version = "2.28.11.2" description = "Typing stubs for requests" category = "dev" optional = false @@ -2047,7 +2053,7 @@ types-urllib3 = "<1.27" [[package]] name = "types-urllib3" -version = "1.26.25" +version = "1.26.25.1" description = "Typing stubs for urllib3" category = "dev" optional = false @@ -2083,7 +2089,7 @@ typing-extensions = ">=3.7.4" [[package]] name = "tzdata" -version = "2022.4" +version = "2022.5" description = "Provider of IANA time zone data" category = "main" optional = false @@ -2221,20 +2227,20 @@ tests-strict = ["cmake (==3.21.2)", "codecov (==2.0.15)", "ninja (==1.10.2)", "p [[package]] name = "zipp" -version = "3.8.1" +version = "3.9.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.7" [package.extras] -docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] -testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.11" -content-hash = "2602fd47f14d1163b2590ab01d3adb1ce881c699bb09630e6fdfc56b919a7a4e" +content-hash = "f1a91728d5e8a582afeb13e972d2235f41d6452f8b8b160ec70d355dbe9b0069" [metadata.files] alabaster = [ @@ -2258,8 +2264,8 @@ APScheduler = [ {file = "APScheduler-3.9.1.tar.gz", hash = "sha256:65e6574b6395498d371d045f2a8a7e4f7d50c6ad21ef7313d15b1c7cf20df1e3"}, ] astroid = [ - {file = "astroid-2.12.10-py3-none-any.whl", hash = "sha256:997e0c735df60d4a4caff27080a3afc51f9bdd693d3572a4a0b7090b645c36c5"}, - {file = "astroid-2.12.10.tar.gz", hash = "sha256:81f870105d892e73bf535da77a8261aa5bde838fa4ed12bb2f435291a098c581"}, + {file = "astroid-2.12.12-py3-none-any.whl", hash = "sha256:72702205200b2a638358369d90c222d74ebc376787af8fb2f7f2a86f7b5cc85f"}, + {file = "astroid-2.12.12.tar.gz", hash = "sha256:1c00a14f5a3ed0339d38d2e2e5b74ea2591df5861c0936bb292b84ccf3a78d83"}, ] attrs = [ {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, @@ -2274,18 +2280,27 @@ bandit = [ {file = "bandit-1.7.2.tar.gz", hash = "sha256:6d11adea0214a43813887bfe71a377b5a9955e4c826c8ffd341b494e3ab25260"}, ] bcrypt = [ - {file = "bcrypt-4.0.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:845b1daf4df2dd94d2fdbc9454953ca9dd0e12970a0bfc9f3dcc6faea3fa96e4"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8780e69f9deec9d60f947b169507d2c9816e4f11548f1f7ebee2af38b9b22ae4"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c3334446fac200499e8bc04a530ce3cf0b3d7151e0e4ac5c0dddd3d95e97843"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb67f6a6c72dfb0a02f3df51550aa1862708e55128b22543e2b42c74f3620d7"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:7c7dd6c1f05bf89e65261d97ac3a6520f34c2acb369afb57e3ea4449be6ff8fd"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:594780b364fb45f2634c46ec8d3e61c1c0f1811c4f2da60e8eb15594ecbf93ed"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2d0dd19aad87e4ab882ef1d12df505f4c52b28b69666ce83c528f42c07379227"}, - {file = "bcrypt-4.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bf413f2a9b0a2950fc750998899013f2e718d20fa4a58b85ca50b6df5ed1bbf9"}, - {file = "bcrypt-4.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ede0f506554571c8eda80db22b83c139303ec6b595b8f60c4c8157bdd0bdee36"}, - {file = "bcrypt-4.0.0-cp36-abi3-win32.whl", hash = "sha256:dc6ec3dc19b1c193b2f7cf279d3e32e7caf447532fbcb7af0906fe4398900c33"}, - {file = "bcrypt-4.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:0b0f0c7141622a31e9734b7f649451147c04ebb5122327ac0bd23744df84be90"}, - {file = "bcrypt-4.0.0.tar.gz", hash = "sha256:c59c170fc9225faad04dde1ba61d85b413946e8ce2e5f5f5ff30dfd67283f319"}, + {file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"}, + {file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"}, + {file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"}, + {file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"}, ] beautifulsoup4 = [ {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, @@ -2296,29 +2311,27 @@ billiard = [ {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, ] black = [ - {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, - {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, - {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, - {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, - {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, - {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, - {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, - {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, - {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, - {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, - {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, - {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, - {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, - {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, - {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, - {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, - {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, - {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, - {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, + {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, + {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, + {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, + {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, + {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, + {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, + {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, + {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, + {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, + {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, + {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, + {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, + {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, + {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, + {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, + {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, + {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, + {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, + {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, + {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, + {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, ] blinker = [ {file = "blinker-1.5-py2.py3-none-any.whl", hash = "sha256:1eb563df6fdbc39eeddc177d953203f99f097e9bf0e2b8f9f3cf18b6ca425e36"}, @@ -2511,8 +2524,8 @@ Flask-RESTful = [ {file = "Flask_RESTful-0.3.9-py2.py3-none-any.whl", hash = "sha256:4970c49b6488e46c520b325f54833374dc2b98e211f1b272bd4b0c516232afe2"}, ] flask-sqlalchemy = [ - {file = "Flask-SQLAlchemy-3.0.0.tar.gz", hash = "sha256:b54939fd5f48184742b7d5b222d86983e233b43140c1071a36327353e86f3b56"}, - {file = "Flask_SQLAlchemy-3.0.0-py3-none-any.whl", hash = "sha256:741dabf0903569a89e4793667e25be5bb9581e614fa0eeb81a395cc7dee40c4b"}, + {file = "Flask-SQLAlchemy-3.0.2.tar.gz", hash = "sha256:16199f5b3ddfb69e0df2f52ae4c76aedbfec823462349dabb21a1b2e0a2b65e9"}, + {file = "Flask_SQLAlchemy-3.0.2-py3-none-any.whl", hash = "sha256:7d0cd9cf73e64a996bb881a1ebd01633fc5a6d11c36ea27f7b5e251dc45476e7"}, ] furo = [ {file = "furo-2022.9.29-py3-none-any.whl", hash = "sha256:559ee17999c0f52728481dcf6b1b0cf8c9743e68c5e3a18cb45a7992747869a9"}, @@ -2523,64 +2536,76 @@ gitdb = [ {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, ] GitPython = [ - {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, - {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, + {file = "GitPython-3.1.29-py3-none-any.whl", hash = "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f"}, + {file = "GitPython-3.1.29.tar.gz", hash = "sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd"}, ] greenlet = [ - {file = "greenlet-1.1.3-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:8c287ae7ac921dfde88b1c125bd9590b7ec3c900c2d3db5197f1286e144e712b"}, - {file = "greenlet-1.1.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:870a48007872d12e95a996fca3c03a64290d3ea2e61076aa35d3b253cf34cd32"}, - {file = "greenlet-1.1.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7c5227963409551ae4a6938beb70d56bf1918c554a287d3da6853526212fbe0a"}, - {file = "greenlet-1.1.3-cp27-cp27m-win32.whl", hash = "sha256:9fae214f6c43cd47f7bef98c56919b9222481e833be2915f6857a1e9e8a15318"}, - {file = "greenlet-1.1.3-cp27-cp27m-win_amd64.whl", hash = "sha256:de431765bd5fe62119e0bc6bc6e7b17ac53017ae1782acf88fcf6b7eae475a49"}, - {file = "greenlet-1.1.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:510c3b15587afce9800198b4b142202b323bf4b4b5f9d6c79cb9a35e5e3c30d2"}, - {file = "greenlet-1.1.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:9951dcbd37850da32b2cb6e391f621c1ee456191c6ae5528af4a34afe357c30e"}, - {file = "greenlet-1.1.3-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:07c58e169bbe1e87b8bbf15a5c1b779a7616df9fd3e61cadc9d691740015b4f8"}, - {file = "greenlet-1.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df02fdec0c533301497acb0bc0f27f479a3a63dcdc3a099ae33a902857f07477"}, - {file = "greenlet-1.1.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c88e134d51d5e82315a7c32b914a58751b7353eb5268dbd02eabf020b4c4700"}, - {file = "greenlet-1.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b41d19c0cfe5c259fe6c539fd75051cd39a5d33d05482f885faf43f7f5e7d26"}, - {file = "greenlet-1.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:6f5d4b2280ceea76c55c893827961ed0a6eadd5a584a7c4e6e6dd7bc10dfdd96"}, - {file = "greenlet-1.1.3-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:184416e481295832350a4bf731ba619a92f5689bf5d0fa4341e98b98b1265bd7"}, - {file = "greenlet-1.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd0404d154084a371e6d2bafc787201612a1359c2dee688ae334f9118aa0bf47"}, - {file = "greenlet-1.1.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a43bbfa9b6cfdfaeefbd91038dde65ea2c421dc387ed171613df340650874f2"}, - {file = "greenlet-1.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce5b64dfe8d0cca407d88b0ee619d80d4215a2612c1af8c98a92180e7109f4b5"}, - {file = "greenlet-1.1.3-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:903fa5716b8fbb21019268b44f73f3748c41d1a30d71b4a49c84b642c2fed5fa"}, - {file = "greenlet-1.1.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:0118817c9341ef2b0f75f5af79ac377e4da6ff637e5ee4ac91802c0e379dadb4"}, - {file = "greenlet-1.1.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:466ce0928e33421ee84ae04c4ac6f253a3a3e6b8d600a79bd43fd4403e0a7a76"}, - {file = "greenlet-1.1.3-cp35-cp35m-win32.whl", hash = "sha256:65ad1a7a463a2a6f863661329a944a5802c7129f7ad33583dcc11069c17e622c"}, - {file = "greenlet-1.1.3-cp35-cp35m-win_amd64.whl", hash = "sha256:7532a46505470be30cbf1dbadb20379fb481244f1ca54207d7df3bf0bbab6a20"}, - {file = "greenlet-1.1.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:caff52cb5cd7626872d9696aee5b794abe172804beb7db52eed1fd5824b63910"}, - {file = "greenlet-1.1.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:db41f3845eb579b544c962864cce2c2a0257fe30f0f1e18e51b1e8cbb4e0ac6d"}, - {file = "greenlet-1.1.3-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e8533f5111704d75de3139bf0b8136d3a6c1642c55c067866fa0a51c2155ee33"}, - {file = "greenlet-1.1.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537e4baf0db67f382eb29255a03154fcd4984638303ff9baaa738b10371fa57"}, - {file = "greenlet-1.1.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8bfd36f368efe0ab2a6aa3db7f14598aac454b06849fb633b762ddbede1db90"}, - {file = "greenlet-1.1.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0877a9a2129a2c56a2eae2da016743db7d9d6a05d5e1c198f1b7808c602a30e"}, - {file = "greenlet-1.1.3-cp36-cp36m-win32.whl", hash = "sha256:88b04e12c9b041a1e0bcb886fec709c488192638a9a7a3677513ac6ba81d8e79"}, - {file = "greenlet-1.1.3-cp36-cp36m-win_amd64.whl", hash = "sha256:4f166b4aca8d7d489e82d74627a7069ab34211ef5ebb57c300ec4b9337b60fc0"}, - {file = "greenlet-1.1.3-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:cd16a89efe3a003029c87ff19e9fba635864e064da646bc749fc1908a4af18f3"}, - {file = "greenlet-1.1.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5b756e6730ea59b2745072e28ad27f4c837084688e6a6b3633c8b1e509e6ae0e"}, - {file = "greenlet-1.1.3-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:9b2f7d0408ddeb8ea1fd43d3db79a8cefaccadd2a812f021333b338ed6b10aba"}, - {file = "greenlet-1.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44b4817c34c9272c65550b788913620f1fdc80362b209bc9d7dd2f40d8793080"}, - {file = "greenlet-1.1.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d58a5a71c4c37354f9e0c24c9c8321f0185f6945ef027460b809f4bb474bfe41"}, - {file = "greenlet-1.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dd51d2650e70c6c4af37f454737bf4a11e568945b27f74b471e8e2a9fd21268"}, - {file = "greenlet-1.1.3-cp37-cp37m-win32.whl", hash = "sha256:048d2bed76c2aa6de7af500ae0ea51dd2267aec0e0f2a436981159053d0bc7cc"}, - {file = "greenlet-1.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:77e41db75f9958f2083e03e9dd39da12247b3430c92267df3af77c83d8ff9eed"}, - {file = "greenlet-1.1.3-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:1626185d938d7381631e48e6f7713e8d4b964be246073e1a1d15c2f061ac9f08"}, - {file = "greenlet-1.1.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:1ec2779774d8e42ed0440cf8bc55540175187e8e934f2be25199bf4ed948cd9e"}, - {file = "greenlet-1.1.3-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f2f908239b7098799b8845e5936c2ccb91d8c2323be02e82f8dcb4a80dcf4a25"}, - {file = "greenlet-1.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b181e9aa6cb2f5ec0cacc8cee6e5a3093416c841ba32c185c30c160487f0380"}, - {file = "greenlet-1.1.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2cf45e339cabea16c07586306a31cfcc5a3b5e1626d365714d283732afed6809"}, - {file = "greenlet-1.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6200a11f003ec26815f7e3d2ded01b43a3810be3528dd760d2f1fa777490c3cd"}, - {file = "greenlet-1.1.3-cp38-cp38-win32.whl", hash = "sha256:db5b25265010a1b3dca6a174a443a0ed4c4ab12d5e2883a11c97d6e6d59b12f9"}, - {file = "greenlet-1.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:095a980288fe05adf3d002fbb180c99bdcf0f930e220aa66fcd56e7914a38202"}, - {file = "greenlet-1.1.3-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:cbc1eb55342cbac8f7ec159088d54e2cfdd5ddf61c87b8bbe682d113789331b2"}, - {file = "greenlet-1.1.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:694ffa7144fa5cc526c8f4512665003a39fa09ef00d19bbca5c8d3406db72fbe"}, - {file = "greenlet-1.1.3-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:aa741c1a8a8cc25eb3a3a01a62bdb5095a773d8c6a86470bde7f607a447e7905"}, - {file = "greenlet-1.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3a669f11289a8995d24fbfc0e63f8289dd03c9aaa0cc8f1eab31d18ca61a382"}, - {file = "greenlet-1.1.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76a53bfa10b367ee734b95988bd82a9a5f0038a25030f9f23bbbc005010ca600"}, - {file = "greenlet-1.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fb0aa7f6996879551fd67461d5d3ab0c3c0245da98be90c89fcb7a18d437403"}, - {file = "greenlet-1.1.3-cp39-cp39-win32.whl", hash = "sha256:5fbe1ab72b998ca77ceabbae63a9b2e2dc2d963f4299b9b278252ddba142d3f1"}, - {file = "greenlet-1.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:ffe73f9e7aea404722058405ff24041e59d31ca23d1da0895af48050a07b6932"}, - {file = "greenlet-1.1.3.tar.gz", hash = "sha256:bcb6c6dd1d6be6d38d6db283747d07fda089ff8c559a835236560a4410340455"}, + {file = "greenlet-1.1.3.post0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:949c9061b8c6d3e6e439466a9be1e787208dec6246f4ec5fffe9677b4c19fcc3"}, + {file = "greenlet-1.1.3.post0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d7815e1519a8361c5ea2a7a5864945906f8e386fa1bc26797b4d443ab11a4589"}, + {file = "greenlet-1.1.3.post0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9649891ab4153f217f319914455ccf0b86986b55fc0573ce803eb998ad7d6854"}, + {file = "greenlet-1.1.3.post0-cp27-cp27m-win32.whl", hash = "sha256:11fc7692d95cc7a6a8447bb160d98671ab291e0a8ea90572d582d57361360f05"}, + {file = "greenlet-1.1.3.post0-cp27-cp27m-win_amd64.whl", hash = "sha256:05ae7383f968bba4211b1fbfc90158f8e3da86804878442b4fb6c16ccbcaa519"}, + {file = "greenlet-1.1.3.post0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ccbe7129a282ec5797df0451ca1802f11578be018a32979131065565da89b392"}, + {file = "greenlet-1.1.3.post0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a8b58232f5b72973350c2b917ea3df0bebd07c3c82a0a0e34775fc2c1f857e9"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:f6661b58412879a2aa099abb26d3c93e91dedaba55a6394d1fb1512a77e85de9"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c6e942ca9835c0b97814d14f78da453241837419e0d26f7403058e8db3e38f8"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a812df7282a8fc717eafd487fccc5ba40ea83bb5b13eb3c90c446d88dbdfd2be"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83a7a6560df073ec9de2b7cb685b199dfd12519bc0020c62db9d1bb522f989fa"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:17a69967561269b691747e7f436d75a4def47e5efcbc3c573180fc828e176d80"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:60839ab4ea7de6139a3be35b77e22e0398c270020050458b3d25db4c7c394df5"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-win_amd64.whl", hash = "sha256:8926a78192b8b73c936f3e87929931455a6a6c6c385448a07b9f7d1072c19ff3"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c6f90234e4438062d6d09f7d667f79edcc7c5e354ba3a145ff98176f974b8132"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:814f26b864ed2230d3a7efe0336f5766ad012f94aad6ba43a7c54ca88dd77cba"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8fda1139d87ce5f7bd80e80e54f9f2c6fe2f47983f1a6f128c47bf310197deb6"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0643250dd0756f4960633f5359884f609a234d4066686754e834073d84e9b51"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cb863057bed786f6622982fb8b2c122c68e6e9eddccaa9fa98fd937e45ee6c4f"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8c0581077cf2734569f3e500fab09c0ff6a2ab99b1afcacbad09b3c2843ae743"}, + {file = "greenlet-1.1.3.post0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:695d0d8b5ae42c800f1763c9fce9d7b94ae3b878919379150ee5ba458a460d57"}, + {file = "greenlet-1.1.3.post0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:5662492df0588a51d5690f6578f3bbbd803e7f8d99a99f3bf6128a401be9c269"}, + {file = "greenlet-1.1.3.post0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:bffba15cff4802ff493d6edcf20d7f94ab1c2aee7cfc1e1c7627c05f1102eee8"}, + {file = "greenlet-1.1.3.post0-cp35-cp35m-win32.whl", hash = "sha256:7afa706510ab079fd6d039cc6e369d4535a48e202d042c32e2097f030a16450f"}, + {file = "greenlet-1.1.3.post0-cp35-cp35m-win_amd64.whl", hash = "sha256:3a24f3213579dc8459e485e333330a921f579543a5214dbc935bc0763474ece3"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:64e10f303ea354500c927da5b59c3802196a07468332d292aef9ddaca08d03dd"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:eb6ac495dccb1520667cfea50d89e26f9ffb49fa28496dea2b95720d8b45eb54"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:88720794390002b0c8fa29e9602b395093a9a766b229a847e8d88349e418b28a"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39464518a2abe9c505a727af7c0b4efff2cf242aa168be5f0daa47649f4d7ca8"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0914f02fcaa8f84f13b2df4a81645d9e82de21ed95633765dd5cc4d3af9d7403"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96656c5f7c95fc02c36d4f6ef32f4e94bb0b6b36e6a002c21c39785a4eec5f5d"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4f74aa0092602da2069df0bc6553919a15169d77bcdab52a21f8c5242898f519"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:3aeac044c324c1a4027dca0cde550bd83a0c0fbff7ef2c98df9e718a5086c194"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-win32.whl", hash = "sha256:fe7c51f8a2ab616cb34bc33d810c887e89117771028e1e3d3b77ca25ddeace04"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:70048d7b2c07c5eadf8393e6398595591df5f59a2f26abc2f81abca09610492f"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:66aa4e9a726b70bcbfcc446b7ba89c8cec40f405e51422c39f42dfa206a96a05"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:025b8de2273d2809f027d347aa2541651d2e15d593bbce0d5f502ca438c54136"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:82a38d7d2077128a017094aff334e67e26194f46bd709f9dcdacbf3835d47ef5"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7d20c3267385236b4ce54575cc8e9f43e7673fc761b069c820097092e318e3b"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8ece5d1a99a2adcb38f69af2f07d96fb615415d32820108cd340361f590d128"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2794eef1b04b5ba8948c72cc606aab62ac4b0c538b14806d9c0d88afd0576d6b"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a8d24eb5cb67996fb84633fdc96dbc04f2d8b12bfcb20ab3222d6be271616b67"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0120a879aa2b1ac5118bce959ea2492ba18783f65ea15821680a256dfad04754"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-win32.whl", hash = "sha256:bef49c07fcb411c942da6ee7d7ea37430f830c482bf6e4b72d92fd506dd3a427"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:62723e7eb85fa52e536e516ee2ac91433c7bb60d51099293671815ff49ed1c21"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d25cdedd72aa2271b984af54294e9527306966ec18963fd032cc851a725ddc1b"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:924df1e7e5db27d19b1359dc7d052a917529c95ba5b8b62f4af611176da7c8ad"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ec615d2912b9ad807afd3be80bf32711c0ff9c2b00aa004a45fd5d5dde7853d9"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0971d37ae0eaf42344e8610d340aa0ad3d06cd2eee381891a10fe771879791f9"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:325f272eb997916b4a3fc1fea7313a8adb760934c2140ce13a2117e1b0a8095d"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75afcbb214d429dacdf75e03a1d6d6c5bd1fa9c35e360df8ea5b6270fb2211c"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5c2d21c2b768d8c86ad935e404cc78c30d53dea009609c3ef3a9d49970c864b5"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:467b73ce5dcd89e381292fb4314aede9b12906c18fab903f995b86034d96d5c8"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-win32.whl", hash = "sha256:8149a6865b14c33be7ae760bcdb73548bb01e8e47ae15e013bf7ef9290ca309a"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-win_amd64.whl", hash = "sha256:104f29dd822be678ef6b16bf0035dcd43206a8a48668a6cae4d2fe9c7a7abdeb"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:c8c9301e3274276d3d20ab6335aa7c5d9e5da2009cccb01127bddb5c951f8870"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:8415239c68b2ec9de10a5adf1130ee9cb0ebd3e19573c55ba160ff0ca809e012"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:3c22998bfef3fcc1b15694818fc9b1b87c6cc8398198b96b6d355a7bcb8c934e"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0aa1845944e62f358d63fcc911ad3b415f585612946b8edc824825929b40e59e"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:890f633dc8cb307761ec566bc0b4e350a93ddd77dc172839be122be12bae3e10"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cf37343e43404699d58808e51f347f57efd3010cc7cee134cdb9141bd1ad9ea"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5edf75e7fcfa9725064ae0d8407c849456553a181ebefedb7606bac19aa1478b"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a954002064ee919b444b19c1185e8cce307a1f20600f47d6f4b6d336972c809"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-win32.whl", hash = "sha256:2ccdc818cc106cc238ff7eba0d71b9c77be868fdca31d6c3b1347a54c9b187b2"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-win_amd64.whl", hash = "sha256:91a84faf718e6f8b888ca63d0b2d6d185c8e2a198d2a7322d75c303e7097c8b7"}, + {file = "greenlet-1.1.3.post0.tar.gz", hash = "sha256:f5e09dc5c6e1796969fd4b775ea1417d70e49a5df29aaa8e5d10675d9e11872c"}, ] gunicorn = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, @@ -2599,8 +2624,8 @@ imagesize = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, - {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, + {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"}, + {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"}, ] inflection = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, @@ -2863,27 +2888,32 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] mysql-connector-python = [ - {file = "mysql-connector-python-8.0.30.tar.gz", hash = "sha256:59a8592e154c874c299763bb8aa12c518384c364bcfd0d193e85c869ea81a895"}, - {file = "mysql_connector_python-8.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f1eb74eb30bb04ff314f5e19af5421d23b504e41d16ddcee2603b4100d18fd68"}, - {file = "mysql_connector_python-8.0.30-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:712cdfa97f35fec715e8d7aaa15ed9ce04f3cf71b3c177fcca273047040de9f2"}, - {file = "mysql_connector_python-8.0.30-cp310-cp310-manylinux1_i686.whl", hash = "sha256:ce23ca9c27e1f7b4707b3299ce515125f312736d86a7e5b2aa778484fa3ffa10"}, - {file = "mysql_connector_python-8.0.30-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:8876b1d51cae33cdfe7021d68206661e94dcd2666e5e14a743f8321e2b068e84"}, - {file = "mysql_connector_python-8.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:41a04d1900e366bf6c2a645ead89ab9a567806d5ada7d417a3a31f170321dd14"}, - {file = "mysql_connector_python-8.0.30-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:7f771bd5cba3ade6d9f7a649e65d7c030f69f0e69980632b5cbbd3d19c39cee5"}, - {file = "mysql_connector_python-8.0.30-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:611c6945805216104575f7143ff6497c87396ce82d3257e6da7257b65406f13e"}, - {file = "mysql_connector_python-8.0.30-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:47deb8c3324db7eb2bfb720ec8084d547b1bce457672ea261bc21836024249db"}, - {file = "mysql_connector_python-8.0.30-cp37-cp37m-win_amd64.whl", hash = "sha256:234c6b156a1989bebca6eb564dc8f2e9d352f90a51bd228ccd68eb66fcd5fd7a"}, - {file = "mysql_connector_python-8.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8b7d50c221320b0e609dce9ca8801ab2f2a748dfee65cd76b1e4c6940757734a"}, - {file = "mysql_connector_python-8.0.30-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:d8f74c9388176635f75c01d47d0abc783a47e58d7f36d04fb6ee40ab6fb35c9b"}, - {file = "mysql_connector_python-8.0.30-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d9d3af14594aceda2c3096564b4c87ffac21e375806a802daeaf7adcd18d36b"}, - {file = "mysql_connector_python-8.0.30-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f5d812245754d4759ebc8c075662fef65397e1e2a438a3c391eac9d545077b8b"}, - {file = "mysql_connector_python-8.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:a130c5489861c7ff2990e5b503c37beb2fb7b32211b92f9107ad864ee90654c0"}, - {file = "mysql_connector_python-8.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:954a1fc2e9a811662c5b17cea24819c020ff9d56b2ff8e583dd0a233fb2399f6"}, - {file = "mysql_connector_python-8.0.30-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:62266d1b18cb4e286a05df0e1c99163a4955c82d41045305bcf0ab2aac107843"}, - {file = "mysql_connector_python-8.0.30-cp39-cp39-manylinux1_i686.whl", hash = "sha256:36e763f21e62b3c9623a264f2513ee11924ea1c9cc8640c115a279d3087064be"}, - {file = "mysql_connector_python-8.0.30-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b5dc0f3295e404f93b674bfaff7589a9fbb8b5ae6c1c134112a1d1beb2f664b2"}, - {file = "mysql_connector_python-8.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:33c4e567547a9a1868462fda8f2b19ea186a7b1afe498171dca39c0f3aa43a75"}, - {file = "mysql_connector_python-8.0.30-py2.py3-none-any.whl", hash = "sha256:f1d40cac9c786e292433716c1ade7a8968cbc3ea177026697b86a63188ddba34"}, + {file = "mysql-connector-python-8.0.31.tar.gz", hash = "sha256:0fbe8f5441ad781b4f65c54a10ac77c6a329591456607e042786528599519636"}, + {file = "mysql_connector_python-8.0.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3e271d8de00d5e9f9bd4b212c8e23d2986dead0f20379010f3b274a3e24cbfcb"}, + {file = "mysql_connector_python-8.0.31-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f3ee04a601f9cb90ace9618bbe2fa8e5bb59be3eb0c2bd8a5405fe69e05e446b"}, + {file = "mysql_connector_python-8.0.31-cp310-cp310-manylinux1_i686.whl", hash = "sha256:f89b7a731885b8a04248e4d8d124705ca836f0ddd3b7cf0c789e21f4b32810ed"}, + {file = "mysql_connector_python-8.0.31-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:48eb34f4e69a2fba56f310de6682862a15d46cd2bd51ee6eebc3a244e4ee0aa6"}, + {file = "mysql_connector_python-8.0.31-cp310-cp310-win_amd64.whl", hash = "sha256:a570a72e0015b36b9c0775ae27c1d4946225f02f62129d16a14e9d77a38c0717"}, + {file = "mysql_connector_python-8.0.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7ac859a52486ac319e37f61469bbb9023faef38018223efa74e953f1fe23d36"}, + {file = "mysql_connector_python-8.0.31-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:79d6a6e8ce955df5ca0786cb8ed8fbd999745c9b50def89993a2a0f4732de721"}, + {file = "mysql_connector_python-8.0.31-cp311-cp311-manylinux1_i686.whl", hash = "sha256:e60426af313dcd526028d018d70757a82c5cc0673776b2a614e2180b5970feed"}, + {file = "mysql_connector_python-8.0.31-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:d0ca1ba3e5fb2f2cddcf271c320cd5c368f8d392c034ddab7a1c8dfd19510351"}, + {file = "mysql_connector_python-8.0.31-cp311-cp311-win_amd64.whl", hash = "sha256:a1d8c1509c740649f352400d50360185e5473371507bb6498ceda0c6e877920c"}, + {file = "mysql_connector_python-8.0.31-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:447847396d1b51edd9cfe05a8c5ba82836d8ea4866f25f36a836cab322fdc4f0"}, + {file = "mysql_connector_python-8.0.31-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5e01a2f50378c13407a32e40dd4d225cfee5996d9d11968f76720ec28aa45421"}, + {file = "mysql_connector_python-8.0.31-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ac85883ec3b3a9a0e36cacc89b8f5e666206842c432a5f69b09a7687ddf51d4a"}, + {file = "mysql_connector_python-8.0.31-cp37-cp37m-win_amd64.whl", hash = "sha256:28cb3667be64ebfbd3d477bbd2c71e50d48bd5ed7ba2072dd460ae886d27e88e"}, + {file = "mysql_connector_python-8.0.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:30f4542d4d20357c79604e6bf1a801e71dfc45c759c22b502ca5aa8122c3e859"}, + {file = "mysql_connector_python-8.0.31-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:e9e5ad544adfc82ffbda2c74685c8c953bce2e212c56f117020079f05e2c68b2"}, + {file = "mysql_connector_python-8.0.31-cp38-cp38-manylinux1_i686.whl", hash = "sha256:744c976569e81eecce5e8c7e8f80df2a1c3f64414829addc69c64aef8f56d091"}, + {file = "mysql_connector_python-8.0.31-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:17d6ea22dacca7fa78a73a81f2b186d4c5c6e70b7be314e352526654e9ba4713"}, + {file = "mysql_connector_python-8.0.31-cp38-cp38-win_amd64.whl", hash = "sha256:ae1b3d03802474a161cce8a97024484d18bef43b86d20114908cbc263817cade"}, + {file = "mysql_connector_python-8.0.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:746df133c677fbe4687da33aad5a711abdd9bd2277bbc350e20f903f07c81ef5"}, + {file = "mysql_connector_python-8.0.31-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:4d75e6c3a7f18004e8279cbd9f5edc70089d6aaf3cb64374e21098d9bf0b93c4"}, + {file = "mysql_connector_python-8.0.31-cp39-cp39-manylinux1_i686.whl", hash = "sha256:8ad0d08f3f7c9e48d6d102c7de718e5e44f630f916ff2f4b4ff8a3756b5d10ac"}, + {file = "mysql_connector_python-8.0.31-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:02526f16eacc3961ff681c5c8455d2306a9b45124f2f012ca75a1eac9ceb5165"}, + {file = "mysql_connector_python-8.0.31-cp39-cp39-win_amd64.whl", hash = "sha256:b2bbf443f6346e46c26a3e91dd96a428a1038f2d3c5e466541078479c64a1833"}, + {file = "mysql_connector_python-8.0.31-py2.py3-none-any.whl", hash = "sha256:9be9c4dcae987a2a3f07b2ad984984c24f90887dbfab3c8a971e631ad4ca5ccf"}, ] nodeenv = [ {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, @@ -2996,17 +3026,17 @@ protobuf = [ {file = "protobuf-3.20.1.tar.gz", hash = "sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9"}, ] psycopg2 = [ - {file = "psycopg2-2.9.3-cp310-cp310-win32.whl", hash = "sha256:083707a696e5e1c330af2508d8fab36f9700b26621ccbcb538abe22e15485362"}, - {file = "psycopg2-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:d3ca6421b942f60c008f81a3541e8faf6865a28d5a9b48544b0ee4f40cac7fca"}, - {file = "psycopg2-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:9572e08b50aed176ef6d66f15a21d823bb6f6d23152d35e8451d7d2d18fdac56"}, - {file = "psycopg2-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:a81e3866f99382dfe8c15a151f1ca5fde5815fde879348fe5a9884a7c092a305"}, - {file = "psycopg2-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:cb10d44e6694d763fa1078a26f7f6137d69f555a78ec85dc2ef716c37447e4b2"}, - {file = "psycopg2-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4295093a6ae3434d33ec6baab4ca5512a5082cc43c0505293087b8a46d108461"}, - {file = "psycopg2-2.9.3-cp38-cp38-win32.whl", hash = "sha256:34b33e0162cfcaad151f249c2649fd1030010c16f4bbc40a604c1cb77173dcf7"}, - {file = "psycopg2-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:0762c27d018edbcb2d34d51596e4346c983bd27c330218c56c4dc25ef7e819bf"}, - {file = "psycopg2-2.9.3-cp39-cp39-win32.whl", hash = "sha256:8cf3878353cc04b053822896bc4922b194792df9df2f1ad8da01fb3043602126"}, - {file = "psycopg2-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:06f32425949bd5fe8f625c49f17ebb9784e1e4fe928b7cce72edc36fb68e4c0c"}, - {file = "psycopg2-2.9.3.tar.gz", hash = "sha256:8e841d1bf3434da985cc5ef13e6f75c8981ced601fd70cc6bf33351b91562981"}, + {file = "psycopg2-2.9.4-cp310-cp310-win32.whl", hash = "sha256:8de6a9fc5f42fa52f559e65120dcd7502394692490c98fed1221acf0819d7797"}, + {file = "psycopg2-2.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:1da77c061bdaab450581458932ae5e469cc6e36e0d62f988376e9f513f11cb5c"}, + {file = "psycopg2-2.9.4-cp36-cp36m-win32.whl", hash = "sha256:a11946bad3557ca254f17357d5a4ed63bdca45163e7a7d2bfb8e695df069cc3a"}, + {file = "psycopg2-2.9.4-cp36-cp36m-win_amd64.whl", hash = "sha256:46361c054df612c3cc813fdb343733d56543fb93565cff0f8ace422e4da06acb"}, + {file = "psycopg2-2.9.4-cp37-cp37m-win32.whl", hash = "sha256:aafa96f2da0071d6dd0cbb7633406d99f414b40ab0f918c9d9af7df928a1accb"}, + {file = "psycopg2-2.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:aa184d551a767ad25df3b8d22a0a62ef2962e0e374c04f6cbd1204947f540d61"}, + {file = "psycopg2-2.9.4-cp38-cp38-win32.whl", hash = "sha256:839f9ea8f6098e39966d97fcb8d08548fbc57c523a1e27a1f0609addf40f777c"}, + {file = "psycopg2-2.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:c7fa041b4acb913f6968fce10169105af5200f296028251d817ab37847c30184"}, + {file = "psycopg2-2.9.4-cp39-cp39-win32.whl", hash = "sha256:07b90a24d5056687781ddaef0ea172fd951f2f7293f6ffdd03d4f5077801f426"}, + {file = "psycopg2-2.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:849bd868ae3369932127f0771c08d1109b254f08d48dc42493c3d1b87cb2d308"}, + {file = "psycopg2-2.9.4.tar.gz", hash = "sha256:d529926254e093a1b669f692a3aa50069bc71faf5b0ecd91686a78f62767d52f"}, ] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, @@ -3087,8 +3117,8 @@ pytest-flask-sqlalchemy = [ {file = "pytest_flask_sqlalchemy-1.1.0-py3-none-any.whl", hash = "sha256:b9f272d5c4092fcbe4a6284e402a37cad84f5b9be3c0bbe1a11927f24c99ff83"}, ] pytest-mock = [ - {file = "pytest-mock-3.9.0.tar.gz", hash = "sha256:c899a0dcc8a5f22930acd020b500abd5f956911f326864a3b979e4866e14da82"}, - {file = "pytest_mock-3.9.0-py3-none-any.whl", hash = "sha256:1a1b9264224d026932d6685a0f9cef3b61d91563c3e74af9fe5afb2767e13812"}, + {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"}, + {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"}, ] python-dateutil = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, @@ -3103,8 +3133,8 @@ python-keycloak = [ {file = "python_keycloak-2.6.0-py3-none-any.whl", hash = "sha256:a1ce102b978beb56d385319b3ca20992b915c2c12d15a2d0c23f1104882f3fb6"}, ] pytz = [ - {file = "pytz-2022.4-py2.py3-none-any.whl", hash = "sha256:2c0784747071402c6e99f0bafdb7da0fa22645f06554c7ae06bf6358897e9c91"}, - {file = "pytz-2022.4.tar.gz", hash = "sha256:48ce799d83b6f8aab2020e369b627446696619e79645419610b9facd909b3174"}, + {file = "pytz-2022.5-py2.py3-none-any.whl", hash = "sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22"}, + {file = "pytz-2022.5.tar.gz", hash = "sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914"}, ] pytz-deprecation-shim = [ {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, @@ -3233,8 +3263,8 @@ regex = [ {file = "regex-2022.3.2.tar.gz", hash = "sha256:79e5af1ff258bc0fe0bdd6f69bc4ae33935a898e3cbefbbccf22e88a27fa053b"}, ] reorder-python-imports = [ - {file = "reorder_python_imports-3.8.3-py2.py3-none-any.whl", hash = "sha256:783f9575f76dd21ae5de974edf514cebc2b6904bbfe7cda515c24f1815fa22bf"}, - {file = "reorder_python_imports-3.8.3.tar.gz", hash = "sha256:cb622aa0ea505972b59cc01aa26c08fb17d39a8fc62ff488288908725927d968"}, + {file = "reorder_python_imports-3.8.5-py2.py3-none-any.whl", hash = "sha256:6c36b84add1a8125479e1de97a21b7797ee6df51530b5340857d65c79d6882ac"}, + {file = "reorder_python_imports-3.8.5.tar.gz", hash = "sha256:5e018dceb889688eafd41a1b217420f810e0400f5a26c679a08f7f9de956ca3b"}, ] requests = [ {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, @@ -3300,8 +3330,8 @@ sentry-sdk = [ {file = "sentry_sdk-1.9.10-py2.py3-none-any.whl", hash = "sha256:2469240f6190aaebcb453033519eae69cfe8cc602065b4667e18ee14fc1e35dc"}, ] setuptools = [ - {file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"}, - {file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"}, + {file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"}, + {file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"}, ] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, @@ -3320,8 +3350,8 @@ soupsieve = [ {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, ] Sphinx = [ - {file = "Sphinx-5.2.3.tar.gz", hash = "sha256:5b10cb1022dac8c035f75767799c39217a05fc0fe2d6fe5597560d38e44f0363"}, - {file = "sphinx-5.2.3-py3-none-any.whl", hash = "sha256:7abf6fabd7b58d0727b7317d5e2650ef68765bbe0ccb63c8795fa8683477eaa2"}, + {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, + {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, ] sphinx-autoapi = [ {file = "sphinx-autoapi-1.9.0.tar.gz", hash = "sha256:c897ea337df16ad0cde307cbdfe2bece207788dde1587fa4fc8b857d1fc5dcba"}, @@ -3365,55 +3395,52 @@ sphinxcontrib-serializinghtml = [ ] SpiffWorkflow = [] SQLAlchemy = [ - {file = "SQLAlchemy-1.4.41-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:13e397a9371ecd25573a7b90bd037db604331cf403f5318038c46ee44908c44d"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2d6495f84c4fd11584f34e62f9feec81bf373787b3942270487074e35cbe5330"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27m-win32.whl", hash = "sha256:e570cfc40a29d6ad46c9aeaddbdcee687880940a3a327f2c668dd0e4ef0a441d"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27m-win_amd64.whl", hash = "sha256:5facb7fd6fa8a7353bbe88b95695e555338fb038ad19ceb29c82d94f62775a05"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f37fa70d95658763254941ddd30ecb23fc4ec0c5a788a7c21034fc2305dab7cc"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:361f6b5e3f659e3c56ea3518cf85fbdae1b9e788ade0219a67eeaaea8a4e4d2a"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0990932f7cca97fece8017414f57fdd80db506a045869d7ddf2dda1d7cf69ecc"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd767cf5d7252b1c88fcfb58426a32d7bd14a7e4942497e15b68ff5d822b41ad"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5102fb9ee2c258a2218281adcb3e1918b793c51d6c2b4666ce38c35101bb940e"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-win32.whl", hash = "sha256:2082a2d2fca363a3ce21cfa3d068c5a1ce4bf720cf6497fb3a9fc643a8ee4ddd"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-win_amd64.whl", hash = "sha256:e4b12e3d88a8fffd0b4ca559f6d4957ed91bd4c0613a4e13846ab8729dc5c251"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:90484a2b00baedad361402c257895b13faa3f01780f18f4a104a2f5c413e4536"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b67fc780cfe2b306180e56daaa411dd3186bf979d50a6a7c2a5b5036575cbdbb"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad2b727fc41c7f8757098903f85fafb4bf587ca6605f82d9bf5604bd9c7cded"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-win32.whl", hash = "sha256:59bdc291165b6119fc6cdbc287c36f7f2859e6051dd923bdf47b4c55fd2f8bd0"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-win_amd64.whl", hash = "sha256:d2e054aed4645f9b755db85bc69fc4ed2c9020c19c8027976f66576b906a74f1"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:4ba7e122510bbc07258dc42be6ed45997efdf38129bde3e3f12649be70683546"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0dcf127bb99458a9d211e6e1f0f3edb96c874dd12f2503d4d8e4f1fd103790b"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e16c2be5cb19e2c08da7bd3a87fed2a0d4e90065ee553a940c4fc1a0fb1ab72b"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ebeeec5c14533221eb30bad716bc1fd32f509196318fb9caa7002c4a364e4c"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-win32.whl", hash = "sha256:3e2ef592ac3693c65210f8b53d0edcf9f4405925adcfc031ff495e8d18169682"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-win_amd64.whl", hash = "sha256:eb30cf008850c0a26b72bd1b9be6730830165ce049d239cfdccd906f2685f892"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:c23d64a0b28fc78c96289ffbd0d9d1abd48d267269b27f2d34e430ea73ce4b26"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8eb8897367a21b578b26f5713833836f886817ee2ffba1177d446fa3f77e67c8"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:14576238a5f89bcf504c5f0a388d0ca78df61fb42cb2af0efe239dc965d4f5c9"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:639e1ae8d48b3c86ffe59c0daa9a02e2bfe17ca3d2b41611b30a0073937d4497"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-win32.whl", hash = "sha256:0005bd73026cd239fc1e8ccdf54db58b6193be9a02b3f0c5983808f84862c767"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-win_amd64.whl", hash = "sha256:5323252be2bd261e0aa3f33cb3a64c45d76829989fa3ce90652838397d84197d"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:05f0de3a1dc3810a776275763764bb0015a02ae0f698a794646ebc5fb06fad33"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0002e829142b2af00b4eaa26c51728f3ea68235f232a2e72a9508a3116bd6ed0"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22ff16cedab5b16a0db79f1bc99e46a6ddececb60c396562e50aab58ddb2871c"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccfd238f766a5bb5ee5545a62dd03f316ac67966a6a658efb63eeff8158a4bbf"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-win32.whl", hash = "sha256:58bb65b3274b0c8a02cea9f91d6f44d0da79abc993b33bdedbfec98c8440175a"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-win_amd64.whl", hash = "sha256:ce8feaa52c1640de9541eeaaa8b5fb632d9d66249c947bb0d89dd01f87c7c288"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:199a73c31ac8ea59937cc0bf3dfc04392e81afe2ec8a74f26f489d268867846c"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676d51c9f6f6226ae8f26dc83ec291c088fe7633269757d333978df78d931ab"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:036d8472356e1d5f096c5e0e1a7e0f9182140ada3602f8fff6b7329e9e7cfbcd"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2307495d9e0ea00d0c726be97a5b96615035854972cc538f6e7eaed23a35886c"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-win32.whl", hash = "sha256:9c56e19780cd1344fcd362fd6265a15f48aa8d365996a37fab1495cae8fcd97d"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-win_amd64.whl", hash = "sha256:f5fa526d027d804b1f85cdda1eb091f70bde6fb7d87892f6dd5a48925bc88898"}, - {file = "SQLAlchemy-1.4.41.tar.gz", hash = "sha256:0292f70d1797e3c54e862e6f30ae474014648bc9c723e14a2fda730adb0a9791"}, -] -sqlalchemy-stubs = [ - {file = "sqlalchemy-stubs-0.4.tar.gz", hash = "sha256:c665d6dd4482ef642f01027fa06c3d5e91befabb219dc71fc2a09e7d7695f7ae"}, - {file = "sqlalchemy_stubs-0.4-py3-none-any.whl", hash = "sha256:5eec7aa110adf9b957b631799a72fef396b23ff99fe296df726645d01e312aa5"}, + {file = "SQLAlchemy-1.4.42-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:28e881266a172a4d3c5929182fde6bb6fba22ac93f137d5380cc78a11a9dd124"}, + {file = "SQLAlchemy-1.4.42-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ca9389a00f639383c93ed00333ed763812f80b5ae9e772ea32f627043f8c9c88"}, + {file = "SQLAlchemy-1.4.42-cp27-cp27m-win32.whl", hash = "sha256:1d0c23ecf7b3bc81e29459c34a3f4c68ca538de01254e24718a7926810dc39a6"}, + {file = "SQLAlchemy-1.4.42-cp27-cp27m-win_amd64.whl", hash = "sha256:6c9d004eb78c71dd4d3ce625b80c96a827d2e67af9c0d32b1c1e75992a7916cc"}, + {file = "SQLAlchemy-1.4.42-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9e3a65ce9ed250b2f096f7b559fe3ee92e6605fab3099b661f0397a9ac7c8d95"}, + {file = "SQLAlchemy-1.4.42-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:2e56dfed0cc3e57b2f5c35719d64f4682ef26836b81067ee6cfad062290fd9e2"}, + {file = "SQLAlchemy-1.4.42-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b42c59ffd2d625b28cdb2ae4cde8488543d428cba17ff672a543062f7caee525"}, + {file = "SQLAlchemy-1.4.42-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22459fc1718785d8a86171bbe7f01b5c9d7297301ac150f508d06e62a2b4e8d2"}, + {file = "SQLAlchemy-1.4.42-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df76e9c60879fdc785a34a82bf1e8691716ffac32e7790d31a98d7dec6e81545"}, + {file = "SQLAlchemy-1.4.42-cp310-cp310-win32.whl", hash = "sha256:e7e740453f0149437c101ea4fdc7eea2689938c5760d7dcc436c863a12f1f565"}, + {file = "SQLAlchemy-1.4.42-cp310-cp310-win_amd64.whl", hash = "sha256:effc89e606165ca55f04f3f24b86d3e1c605e534bf1a96e4e077ce1b027d0b71"}, + {file = "SQLAlchemy-1.4.42-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:97ff50cd85bb907c2a14afb50157d0d5486a4b4639976b4a3346f34b6d1b5272"}, + {file = "SQLAlchemy-1.4.42-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12c6949bae10f1012ab5c0ea52ab8db99adcb8c7b717938252137cdf694c775"}, + {file = "SQLAlchemy-1.4.42-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11b2ec26c5d2eefbc3e6dca4ec3d3d95028be62320b96d687b6e740424f83b7d"}, + {file = "SQLAlchemy-1.4.42-cp311-cp311-win32.whl", hash = "sha256:6045b3089195bc008aee5c273ec3ba9a93f6a55bc1b288841bd4cfac729b6516"}, + {file = "SQLAlchemy-1.4.42-cp311-cp311-win_amd64.whl", hash = "sha256:0501f74dd2745ec38f44c3a3900fb38b9db1ce21586b691482a19134062bf049"}, + {file = "SQLAlchemy-1.4.42-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:6e39e97102f8e26c6c8550cb368c724028c575ec8bc71afbbf8faaffe2b2092a"}, + {file = "SQLAlchemy-1.4.42-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15d878929c30e41fb3d757a5853b680a561974a0168cd33a750be4ab93181628"}, + {file = "SQLAlchemy-1.4.42-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fa5b7eb2051e857bf83bade0641628efe5a88de189390725d3e6033a1fff4257"}, + {file = "SQLAlchemy-1.4.42-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e1c5f8182b4f89628d782a183d44db51b5af84abd6ce17ebb9804355c88a7b5"}, + {file = "SQLAlchemy-1.4.42-cp36-cp36m-win32.whl", hash = "sha256:a7dd5b7b34a8ba8d181402d824b87c5cee8963cb2e23aa03dbfe8b1f1e417cde"}, + {file = "SQLAlchemy-1.4.42-cp36-cp36m-win_amd64.whl", hash = "sha256:5ede1495174e69e273fad68ad45b6d25c135c1ce67723e40f6cf536cb515e20b"}, + {file = "SQLAlchemy-1.4.42-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:9256563506e040daddccaa948d055e006e971771768df3bb01feeb4386c242b0"}, + {file = "SQLAlchemy-1.4.42-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4948b6c5f4e56693bbeff52f574279e4ff972ea3353f45967a14c30fb7ae2beb"}, + {file = "SQLAlchemy-1.4.42-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1811a0b19a08af7750c0b69e38dec3d46e47c4ec1d74b6184d69f12e1c99a5e0"}, + {file = "SQLAlchemy-1.4.42-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b01d9cd2f9096f688c71a3d0f33f3cd0af8549014e66a7a7dee6fc214a7277d"}, + {file = "SQLAlchemy-1.4.42-cp37-cp37m-win32.whl", hash = "sha256:bd448b262544b47a2766c34c0364de830f7fb0772d9959c1c42ad61d91ab6565"}, + {file = "SQLAlchemy-1.4.42-cp37-cp37m-win_amd64.whl", hash = "sha256:04f2598c70ea4a29b12d429a80fad3a5202d56dce19dd4916cc46a965a5ca2e9"}, + {file = "SQLAlchemy-1.4.42-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3ab7c158f98de6cb4f1faab2d12973b330c2878d0c6b689a8ca424c02d66e1b3"}, + {file = "SQLAlchemy-1.4.42-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee377eb5c878f7cefd633ab23c09e99d97c449dd999df639600f49b74725b80"}, + {file = "SQLAlchemy-1.4.42-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:934472bb7d8666727746a75670a1f8d91a9cae8c464bba79da30a0f6faccd9e1"}, + {file = "SQLAlchemy-1.4.42-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb94a3d1ba77ff2ef11912192c066f01e68416f554c194d769391638c8ad09a"}, + {file = "SQLAlchemy-1.4.42-cp38-cp38-win32.whl", hash = "sha256:f0f574465b78f29f533976c06b913e54ab4980b9931b69aa9d306afff13a9471"}, + {file = "SQLAlchemy-1.4.42-cp38-cp38-win_amd64.whl", hash = "sha256:a85723c00a636eed863adb11f1e8aaa36ad1c10089537823b4540948a8429798"}, + {file = "SQLAlchemy-1.4.42-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5ce6929417d5dce5ad1d3f147db81735a4a0573b8fb36e3f95500a06eaddd93e"}, + {file = "SQLAlchemy-1.4.42-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723e3b9374c1ce1b53564c863d1a6b2f1dc4e97b1c178d9b643b191d8b1be738"}, + {file = "SQLAlchemy-1.4.42-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:876eb185911c8b95342b50a8c4435e1c625944b698a5b4a978ad2ffe74502908"}, + {file = "SQLAlchemy-1.4.42-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd49af453e590884d9cdad3586415922a8e9bb669d874ee1dc55d2bc425aacd"}, + {file = "SQLAlchemy-1.4.42-cp39-cp39-win32.whl", hash = "sha256:e4ef8cb3c5b326f839bfeb6af5f406ba02ad69a78c7aac0fbeeba994ad9bb48a"}, + {file = "SQLAlchemy-1.4.42-cp39-cp39-win_amd64.whl", hash = "sha256:5f966b64c852592469a7eb759615bbd351571340b8b344f1d3fa2478b5a4c934"}, + {file = "SQLAlchemy-1.4.42.tar.gz", hash = "sha256:177e41914c476ed1e1b77fd05966ea88c094053e17a85303c4ce007f88eff363"}, ] +sqlalchemy-stubs = [] stevedore = [ - {file = "stevedore-4.0.0-py3-none-any.whl", hash = "sha256:87e4d27fe96d0d7e4fc24f0cbe3463baae4ec51e81d95fbe60d2474636e0c7d8"}, - {file = "stevedore-4.0.0.tar.gz", hash = "sha256:f82cc99a1ff552310d19c379827c2c64dd9f85a38bcd5559db2470161867b786"}, + {file = "stevedore-4.0.1-py3-none-any.whl", hash = "sha256:01645addb67beff04c7cfcbb0a6af8327d2efc3380b0f034aa316d4576c4d470"}, + {file = "stevedore-4.0.1.tar.gz", hash = "sha256:9a23111a6e612270c591fd31ff3321c6b5f3d5f3dabb1427317a5ab608fc261a"}, ] swagger-ui-bundle = [ {file = "swagger_ui_bundle-0.0.9-py3-none-any.whl", hash = "sha256:cea116ed81147c345001027325c1ddc9ca78c1ee7319935c3c75d3669279d575"}, @@ -3465,20 +3492,20 @@ types-MarkupSafe = [ {file = "types_MarkupSafe-1.1.10-py3-none-any.whl", hash = "sha256:ca2bee0f4faafc45250602567ef38d533e877d2ddca13003b319c551ff5b3cc5"}, ] types-pytz = [ - {file = "types-pytz-2022.4.0.0.tar.gz", hash = "sha256:17d66e4b16e80ceae0787726f3a22288df7d3f9fdebeb091dc64b92c0e4ea09d"}, - {file = "types_pytz-2022.4.0.0-py3-none-any.whl", hash = "sha256:950b0f3d64ed5b03a3e29c1e38fe2be8371c933c8e97922d0352345336eb8af4"}, + {file = "types-pytz-2022.5.0.0.tar.gz", hash = "sha256:0c163b15d3e598e6cc7074a99ca9ec72b25dc1b446acc133b827667af0b7b09a"}, + {file = "types_pytz-2022.5.0.0-py3-none-any.whl", hash = "sha256:a8e1fe6a1b270fbfaf2553b20ad0f1316707cc320e596da903bb17d7373fed2d"}, ] types-PyYAML = [ {file = "types-PyYAML-6.0.12.tar.gz", hash = "sha256:f6f350418125872f3f0409d96a62a5a5ceb45231af5cc07ee0034ec48a3c82fa"}, {file = "types_PyYAML-6.0.12-py3-none-any.whl", hash = "sha256:29228db9f82df4f1b7febee06bbfb601677882e98a3da98132e31c6874163e15"}, ] types-requests = [ - {file = "types-requests-2.28.11.1.tar.gz", hash = "sha256:02b1806c5b9904edcd87fa29236164aea0e6cdc4d93ea020cd615ef65cb43d65"}, - {file = "types_requests-2.28.11.1-py3-none-any.whl", hash = "sha256:1ff2c1301f6fe58b5d1c66cdf631ca19734cb3b1a4bbadc878d75557d183291a"}, + {file = "types-requests-2.28.11.2.tar.gz", hash = "sha256:fdcd7bd148139fb8eef72cf4a41ac7273872cad9e6ada14b11ff5dfdeee60ed3"}, + {file = "types_requests-2.28.11.2-py3-none-any.whl", hash = "sha256:14941f8023a80b16441b3b46caffcbfce5265fd14555844d6029697824b5a2ef"}, ] types-urllib3 = [ - {file = "types-urllib3-1.26.25.tar.gz", hash = "sha256:5aef0e663724eef924afa8b320b62ffef2c1736c1fa6caecfc9bc6c8ae2c3def"}, - {file = "types_urllib3-1.26.25-py3-none-any.whl", hash = "sha256:c1d78cef7bd581e162e46c20a57b2e1aa6ebecdcf01fd0713bb90978ff3e3427"}, + {file = "types-urllib3-1.26.25.1.tar.gz", hash = "sha256:a948584944b2412c9a74b9cf64f6c48caf8652cb88b38361316f6d15d8a184cd"}, + {file = "types_urllib3-1.26.25.1-py3-none-any.whl", hash = "sha256:f6422596cc9ee5fdf68f9d547f541096a20c2dcfd587e37c804c9ea720bf5cb2"}, ] types-Werkzeug = [ {file = "types-Werkzeug-1.0.9.tar.gz", hash = "sha256:5cc269604c400133d452a40cee6397655f878fc460e03fde291b9e3a5eaa518c"}, @@ -3493,8 +3520,8 @@ typing-inspect = [ {file = "typing_inspect-0.8.0.tar.gz", hash = "sha256:8b1ff0c400943b6145df8119c41c244ca8207f1f10c9c057aeed1560e4806e3d"}, ] tzdata = [ - {file = "tzdata-2022.4-py2.py3-none-any.whl", hash = "sha256:74da81ecf2b3887c94e53fc1d466d4362aaf8b26fc87cda18f22004544694583"}, - {file = "tzdata-2022.4.tar.gz", hash = "sha256:ada9133fbd561e6ec3d1674d3fba50251636e918aa97bd59d63735bef5a513bb"}, + {file = "tzdata-2022.5-py2.py3-none-any.whl", hash = "sha256:323161b22b7802fdc78f20ca5f6073639c64f1a7227c40cd3e19fd1d0ce6650a"}, + {file = "tzdata-2022.5.tar.gz", hash = "sha256:e15b2b3005e2546108af42a0eb4ccab4d9e225e2dfbf4f77aad50c70a4b1f3ab"}, ] tzlocal = [ {file = "tzlocal-4.2-py3-none-any.whl", hash = "sha256:89885494684c929d9191c57aa27502afc87a579be5cdd3225c77c463ea043745"}, @@ -3599,6 +3626,6 @@ xdoctest = [ {file = "xdoctest-1.1.0.tar.gz", hash = "sha256:0fd4fad7932f0a2f082dfdfb857dd6ca41603757586c39b1e5b4d333fc389f8a"}, ] zipp = [ - {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, - {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, + {file = "zipp-3.9.0-py3-none-any.whl", hash = "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980"}, + {file = "zipp-3.9.0.tar.gz", hash = "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb"}, ] diff --git a/pyproject.toml b/pyproject.toml index f1037540..59e66a36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,7 +90,8 @@ Pygments = "^2.10.0" pyupgrade = "^2.37.1" furo = ">=2021.11.12" MonkeyType = "^22.2.0" -sqlalchemy-stubs = "^0.4" +# sqlalchemy-stubs = "^0.4" +sqlalchemy-stubs = { git = "https://github.com/dropbox/sqlalchemy-stubs.git", rev = "master" } [tool.poetry.scripts] spiffworkflow-backend = "spiffworkflow_backend.__main__:main" diff --git a/src/spiffworkflow_backend/services/secret_service.py b/src/spiffworkflow_backend/services/secret_service.py index fb71be4d..864c2fe0 100644 --- a/src/spiffworkflow_backend/services/secret_service.py +++ b/src/spiffworkflow_backend/services/secret_service.py @@ -52,9 +52,9 @@ class SecretService: return secret_model @staticmethod - def get_secret(key: str) -> Optional[SecretModel]: + def get_secret(key: str) -> SecretModel: """Get_secret.""" - secret: SecretModel = ( + secret = ( db.session.query(SecretModel).filter(SecretModel.key == key).first() ) if secret is not None: diff --git a/src/spiffworkflow_backend/services/user_service.py b/src/spiffworkflow_backend/services/user_service.py index 3bf7f092..c84a45dc 100644 --- a/src/spiffworkflow_backend/services/user_service.py +++ b/src/spiffworkflow_backend/services/user_service.py @@ -257,7 +257,7 @@ class UserService: @staticmethod def get_principal_by_user_id(user_id: int) -> PrincipalModel: """Get_principal_by_user_id.""" - principal: PrincipalModel = ( + principal = ( db.session.query(PrincipalModel) .filter(PrincipalModel.user_id == user_id) .first() diff --git a/tests/spiffworkflow_backend/integration/test_process_api.py b/tests/spiffworkflow_backend/integration/test_process_api.py index aff5e5e4..a5501dae 100644 --- a/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1368,6 +1368,7 @@ class TestProcessApi(BaseTest): .filter(ProcessInstanceModel.id == process_instance_id) .first() ) + assert process is not None assert process.status == "not_started" response = client.post( @@ -1388,6 +1389,7 @@ class TestProcessApi(BaseTest): .filter(ProcessInstanceModel.id == process_instance_id) .first() ) + assert process is not None assert process.status == "faulted" def test_error_handler_suspend( @@ -1414,6 +1416,7 @@ class TestProcessApi(BaseTest): .filter(ProcessInstanceModel.id == process_instance_id) .first() ) + assert process is not None assert process.status == "not_started" response = client.post( @@ -1427,6 +1430,7 @@ class TestProcessApi(BaseTest): .filter(ProcessInstanceModel.id == process_instance_id) .first() ) + assert process is not None assert process.status == "suspended" def test_error_handler_with_email( @@ -1469,6 +1473,7 @@ class TestProcessApi(BaseTest): .filter(ProcessInstanceModel.id == process_instance_id) .first() ) + assert process is not None assert process.status == "faulted" def test_process_model_file_create( From 9ea3def22175fa1a7020e8701c9d2d76ad852e1d Mon Sep 17 00:00:00 2001 From: burnettk Date: Thu, 20 Oct 2022 09:05:44 -0400 Subject: [PATCH 17/24] lint --- src/spiffworkflow_backend/services/secret_service.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/spiffworkflow_backend/services/secret_service.py b/src/spiffworkflow_backend/services/secret_service.py index 864c2fe0..b5557a49 100644 --- a/src/spiffworkflow_backend/services/secret_service.py +++ b/src/spiffworkflow_backend/services/secret_service.py @@ -54,9 +54,7 @@ class SecretService: @staticmethod def get_secret(key: str) -> SecretModel: """Get_secret.""" - secret = ( - db.session.query(SecretModel).filter(SecretModel.key == key).first() - ) + secret = db.session.query(SecretModel).filter(SecretModel.key == key).first() if secret is not None: return secret else: From 6403e62c0ec3e6180cc0ddb388ddd3a143cb32a8 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Thu, 20 Oct 2022 11:30:28 -0400 Subject: [PATCH 18/24] Fix migration after merging main --- migrations/versions/{71d3a02ed025_.py => c98445562ced_.py} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename migrations/versions/{71d3a02ed025_.py => c98445562ced_.py} (99%) diff --git a/migrations/versions/71d3a02ed025_.py b/migrations/versions/c98445562ced_.py similarity index 99% rename from migrations/versions/71d3a02ed025_.py rename to migrations/versions/c98445562ced_.py index 6d0ad913..240bb56a 100644 --- a/migrations/versions/71d3a02ed025_.py +++ b/migrations/versions/c98445562ced_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 71d3a02ed025 +Revision ID: c98445562ced Revises: -Create Date: 2022-10-13 16:44:03.884031 +Create Date: 2022-10-20 11:29:08.160790 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '71d3a02ed025' +revision = 'c98445562ced' down_revision = None branch_labels = None depends_on = None From 476e36c7d0c408212e8d2ae60686ed35cb5dacfe Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Thu, 20 Oct 2022 11:40:35 -0400 Subject: [PATCH 19/24] mypy changes --- src/spiffworkflow_backend/services/user_service.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/spiffworkflow_backend/services/user_service.py b/src/spiffworkflow_backend/services/user_service.py index 7ea90e9f..c66eb211 100644 --- a/src/spiffworkflow_backend/services/user_service.py +++ b/src/spiffworkflow_backend/services/user_service.py @@ -310,5 +310,6 @@ class UserService: .filter(UserModel.service_id == service_id) .first() ) - assert user # noqa: S101 - return user + if user: + return user + return None From 5225a8b4c101133567d4f7efa33632d36c29c81d Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Thu, 20 Oct 2022 13:10:13 -0400 Subject: [PATCH 20/24] pyl --- src/spiffworkflow_backend/services/authentication_service.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index a0367cbd..18f08d0f 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -12,6 +12,7 @@ from flask import redirect from flask_bpmn.api.api_error import ApiError from flask_bpmn.models.db import db from werkzeug.wrappers import Response + from spiffworkflow_backend.models.refresh_token import RefreshTokenModel From 18a892f6c6c88304feb63ac5660340e62198f241 Mon Sep 17 00:00:00 2001 From: burnettk Date: Thu, 20 Oct 2022 17:30:51 -0400 Subject: [PATCH 21/24] work in progress --- noxfile.py | 2 +- poetry.lock | 2 +- pyproject.toml | 3 ++- src/spiffworkflow_backend/routes/process_api_blueprint.py | 3 +++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/noxfile.py b/noxfile.py index ef4f7787..3956a19d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -132,7 +132,7 @@ def mypy(session: Session) -> None: """Type-check using mypy.""" args = session.posargs or ["src", "tests", "docs/conf.py"] session.install(".") - session.install("mypy", "pytest", "sqlalchemy-stubs") + session.install("mypy", "pytest", "sqlalchemy-stubs", "types-Werkzeug", "types-PyYAML", "types-Flask", "types-requests", "types-pytz") session.run("mypy", *args) if not session.posargs: session.run("mypy", f"--python-executable={sys.executable}", "noxfile.py") diff --git a/poetry.lock b/poetry.lock index 8c6ec149..a4511682 100644 --- a/poetry.lock +++ b/poetry.lock @@ -639,7 +639,7 @@ werkzeug = "*" type = "git" url = "https://github.com/sartography/flask-bpmn" reference = "main" -resolved_reference = "bd4b45a842ed63a29e74ff02ea7f2a56d7b2298a" +resolved_reference = "70ee69c9ba14fb4f0f4b8701b319f22e5eba4b99" [[package]] name = "Flask-Cors" diff --git a/pyproject.toml b/pyproject.toml index 59e66a36..ef047f33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,7 +91,8 @@ pyupgrade = "^2.37.1" furo = ">=2021.11.12" MonkeyType = "^22.2.0" # sqlalchemy-stubs = "^0.4" -sqlalchemy-stubs = { git = "https://github.com/dropbox/sqlalchemy-stubs.git", rev = "master" } +# sqlalchemy-stubs = { git = "https://github.com/dropbox/sqlalchemy-stubs.git", rev = "master" } +sqlalchemy-stubs = {develop = true, path = "/Users/kevin/projects/github/sqlalchemy-stubs"} [tool.poetry.scripts] spiffworkflow-backend = "spiffworkflow_backend.__main__:main" diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index 3b5a9ea2..a3bf2430 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -684,6 +684,9 @@ def process_instance_delete( """Create_process_instance.""" process_instance = find_process_instance_by_id_or_raise(process_instance_id) + # import pdb; pdb.set_trace() + # (Pdb) db.session.delete + # > db.session.delete(process_instance) db.session.commit() return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") From 68485ab4c4162930dbe370437aeb016d03234e4a Mon Sep 17 00:00:00 2001 From: burnettk Date: Thu, 20 Oct 2022 18:03:55 -0400 Subject: [PATCH 22/24] use fork of sqlalchemy-stubs --- poetry.lock | 10 ++++++---- pyproject.toml | 7 ++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index e2225e63..fc3e8d8a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1906,15 +1906,17 @@ description = "SQLAlchemy stubs and mypy plugin" category = "dev" optional = false python-versions = "*" -develop = true +develop = false [package.dependencies] mypy = ">=0.790" typing-extensions = ">=3.7.4" [package.source] -type = "directory" -url = "../../sqlalchemy-stubs" +type = "git" +url = "https://github.com/burnettk/sqlalchemy-stubs.git" +reference = "scoped-session-delete" +resolved_reference = "d1176931684ce5b327539cc9567d4a1cd8ef1efd" [[package]] name = "stevedore" @@ -2238,7 +2240,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.11" -content-hash = "e13eb91dc5080a58312f8ebe4f7971a4cddb4bb551e1345af2ccc816cf40b181" +content-hash = "ade6f6f6e57ee6d16f141b173a11ec52a77393c897c8007260aa8ae35a24c4d4" [metadata.files] alabaster = [ diff --git a/pyproject.toml b/pyproject.toml index ef047f33..8de32a93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,9 +90,14 @@ Pygments = "^2.10.0" pyupgrade = "^2.37.1" furo = ">=2021.11.12" MonkeyType = "^22.2.0" + +# https://github.com/dropbox/sqlalchemy-stubs/pull/251 +# someday get off github # sqlalchemy-stubs = "^0.4" # sqlalchemy-stubs = { git = "https://github.com/dropbox/sqlalchemy-stubs.git", rev = "master" } -sqlalchemy-stubs = {develop = true, path = "/Users/kevin/projects/github/sqlalchemy-stubs"} +# sqlalchemy-stubs = {develop = true, path = "/Users/kevin/projects/github/sqlalchemy-stubs"} +# for now use my fork +sqlalchemy-stubs = { git = "https://github.com/burnettk/sqlalchemy-stubs.git", rev = "scoped-session-delete" } [tool.poetry.scripts] spiffworkflow-backend = "spiffworkflow_backend.__main__:main" From 5e09e28da8b94b00365ee8fc0f82031d55101afa Mon Sep 17 00:00:00 2001 From: burnettk Date: Thu, 20 Oct 2022 18:44:08 -0400 Subject: [PATCH 23/24] fix mypy --- noxfile.py | 2 +- poetry.lock | 26 +++++++++++++------------- pyproject.toml | 31 ++++++++++++++++++------------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/noxfile.py b/noxfile.py index 3956a19d..5e9f4e9a 100644 --- a/noxfile.py +++ b/noxfile.py @@ -132,7 +132,7 @@ def mypy(session: Session) -> None: """Type-check using mypy.""" args = session.posargs or ["src", "tests", "docs/conf.py"] session.install(".") - session.install("mypy", "pytest", "sqlalchemy-stubs", "types-Werkzeug", "types-PyYAML", "types-Flask", "types-requests", "types-pytz") + session.install("mypy") session.run("mypy", *args) if not session.posargs: session.run("mypy", f"--python-executable={sys.executable}", "noxfile.py") diff --git a/poetry.lock b/poetry.lock index fc3e8d8a..f487a2c1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1061,7 +1061,7 @@ mypy-extensions = "*" name = "mypy" version = "0.982" description = "Optional static typing for Python" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -1079,7 +1079,7 @@ reports = ["lxml"] name = "mypy-extensions" version = "0.4.3" description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "dev" +category = "main" optional = false python-versions = "*" @@ -1903,7 +1903,7 @@ sqlcipher = ["sqlcipher3_binary"] name = "sqlalchemy-stubs" version = "0.4" description = "SQLAlchemy stubs and mypy plugin" -category = "dev" +category = "main" optional = false python-versions = "*" develop = false @@ -1988,7 +1988,7 @@ test = ["mypy", "pytest", "typing-extensions"] name = "types-click" version = "7.1.8" description = "Typing stubs for click" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -1996,7 +1996,7 @@ python-versions = "*" name = "types-Flask" version = "1.1.6" description = "Typing stubs for Flask" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -2009,7 +2009,7 @@ types-Werkzeug = "*" name = "types-Jinja2" version = "2.11.9" description = "Typing stubs for Jinja2" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -2020,7 +2020,7 @@ types-MarkupSafe = "*" name = "types-MarkupSafe" version = "1.1.10" description = "Typing stubs for MarkupSafe" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -2028,7 +2028,7 @@ python-versions = "*" name = "types-pytz" version = "2022.5.0.0" description = "Typing stubs for pytz" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -2036,7 +2036,7 @@ python-versions = "*" name = "types-PyYAML" version = "6.0.12" description = "Typing stubs for PyYAML" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -2044,7 +2044,7 @@ python-versions = "*" name = "types-requests" version = "2.28.11.2" description = "Typing stubs for requests" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -2055,7 +2055,7 @@ types-urllib3 = "<1.27" name = "types-urllib3" version = "1.26.25.1" description = "Typing stubs for urllib3" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -2063,7 +2063,7 @@ python-versions = "*" name = "types-Werkzeug" version = "1.0.9" description = "Typing stubs for Werkzeug" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -2240,7 +2240,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.11" -content-hash = "ade6f6f6e57ee6d16f141b173a11ec52a77393c897c8007260aa8ae35a24c4d4" +content-hash = "29b4ba7f1afdf87ad0a336216ef71d2e2659cd1bd3baecb009efdaac2937737a" [metadata.files] alabaster = [ diff --git a/pyproject.toml b/pyproject.toml index 8de32a93..0ff379e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,6 +54,24 @@ RestrictedPython = "^5.2" Flask-SQLAlchemy = "^3" orjson = "^3.8.0" +# type hinting stuff +# these need to be in the normal (non dev-dependencies) section +# because if not then poetry export won't have them and nox -s mypy --pythons 3.10 +# will fail +types-Werkzeug = "^1.0.9" +types-PyYAML = "^6.0.12" +types-Flask = "^1.1.6" +types-requests = "^2.28.6" +types-pytz = "^2022.1.1" + +# https://github.com/dropbox/sqlalchemy-stubs/pull/251 +# someday get off github +# sqlalchemy-stubs = "^0.4" +# sqlalchemy-stubs = { git = "https://github.com/dropbox/sqlalchemy-stubs.git", rev = "master" } +# sqlalchemy-stubs = {develop = true, path = "/Users/kevin/projects/github/sqlalchemy-stubs"} +# for now use my fork +sqlalchemy-stubs = { git = "https://github.com/burnettk/sqlalchemy-stubs.git", rev = "scoped-session-delete" } + [tool.poetry.dev-dependencies] pytest = "^7.1.2" @@ -68,11 +86,6 @@ pre-commit = "^2.20.0" flake8 = "^4.0.1" black = ">=21.10b0" flake8-bandit = "^2.1.2" -types-Werkzeug = "^1.0.9" -types-PyYAML = "^6.0.12" -types-Flask = "^1.1.6" -types-requests = "^2.28.6" -types-pytz = "^2022.1.1" # 1.7.3 broke us. https://github.com/PyCQA/bandit/issues/841 bandit = "1.7.2" @@ -91,14 +104,6 @@ pyupgrade = "^2.37.1" furo = ">=2021.11.12" MonkeyType = "^22.2.0" -# https://github.com/dropbox/sqlalchemy-stubs/pull/251 -# someday get off github -# sqlalchemy-stubs = "^0.4" -# sqlalchemy-stubs = { git = "https://github.com/dropbox/sqlalchemy-stubs.git", rev = "master" } -# sqlalchemy-stubs = {develop = true, path = "/Users/kevin/projects/github/sqlalchemy-stubs"} -# for now use my fork -sqlalchemy-stubs = { git = "https://github.com/burnettk/sqlalchemy-stubs.git", rev = "scoped-session-delete" } - [tool.poetry.scripts] spiffworkflow-backend = "spiffworkflow_backend.__main__:main" From 7e1ce35c277ff0f9281191ecae1a023bae0b496b Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 21 Oct 2022 09:04:09 -0400 Subject: [PATCH 24/24] remove task_data column --- bin/start_keycloak | 4 ---- migrations/versions/{e6b28d8e3178_.py => e12e98d4e7e4_.py} | 7 +++---- src/spiffworkflow_backend/models/active_task.py | 5 ----- src/spiffworkflow_backend/routes/process_api_blueprint.py | 1 - .../services/process_instance_processor.py | 1 - 5 files changed, 3 insertions(+), 15 deletions(-) rename migrations/versions/{e6b28d8e3178_.py => e12e98d4e7e4_.py} (99%) diff --git a/bin/start_keycloak b/bin/start_keycloak index 3b5ddd1e..4fcaedf8 100755 --- a/bin/start_keycloak +++ b/bin/start_keycloak @@ -23,14 +23,10 @@ docker run \ -Dkeycloak.profile.feature.token_exchange=enabled \ -Dkeycloak.profile.feature.admin_fine_grained_authz=enabled -docker cp bin/finance-realm.json keycloak:/tmp docker cp bin/spiffworkflow-realm.json keycloak:/tmp -docker cp bin/quarkus-realm.json keycloak:/tmp sleep 10 -docker exec keycloak /opt/keycloak/bin/kc.sh import --file /tmp/finance-realm.json || echo '' docker exec keycloak /opt/keycloak/bin/kc.sh import --file /tmp/spiffworkflow-realm.json || echo '' -docker exec keycloak /opt/keycloak/bin/kc.sh import --file /tmp/quarkus-realm.json || echo '' echo 'imported realms' diff --git a/migrations/versions/e6b28d8e3178_.py b/migrations/versions/e12e98d4e7e4_.py similarity index 99% rename from migrations/versions/e6b28d8e3178_.py rename to migrations/versions/e12e98d4e7e4_.py index ee75ee4a..bf7fff66 100644 --- a/migrations/versions/e6b28d8e3178_.py +++ b/migrations/versions/e12e98d4e7e4_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: e6b28d8e3178 +Revision ID: e12e98d4e7e4 Revises: -Create Date: 2022-10-20 13:05:25.896486 +Create Date: 2022-10-21 08:53:52.815491 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'e6b28d8e3178' +revision = 'e12e98d4e7e4' down_revision = None branch_labels = None depends_on = None @@ -176,7 +176,6 @@ def upgrade(): sa.Column('task_type', sa.String(length=50), nullable=True), sa.Column('task_status', sa.String(length=50), nullable=True), sa.Column('process_model_display_name', sa.String(length=255), nullable=True), - sa.Column('task_data', sa.Text(length=4294000000), nullable=True), sa.ForeignKeyConstraint(['assigned_principal_id'], ['principal.id'], ), sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), sa.PrimaryKeyConstraint('id'), diff --git a/src/spiffworkflow_backend/models/active_task.py b/src/spiffworkflow_backend/models/active_task.py index f9daebe8..c4dadc34 100644 --- a/src/spiffworkflow_backend/models/active_task.py +++ b/src/spiffworkflow_backend/models/active_task.py @@ -1,7 +1,6 @@ """Active_task.""" from __future__ import annotations -import json from dataclasses import dataclass from flask_bpmn.models.db import db @@ -46,20 +45,16 @@ class ActiveTaskModel(SpiffworkflowBaseDBModel): task_type = db.Column(db.String(50)) task_status = db.Column(db.String(50)) process_model_display_name = db.Column(db.String(255)) - task_data: str = db.Column(db.Text(4294000000)) @classmethod def to_task(cls, task: ActiveTaskModel) -> Task: """To_task.""" - task_data = json.loads(task.task_data) - new_task = Task( task.task_id, task.task_name, task.task_title, task.task_type, task.task_status, - data=task_data, process_instance_id=task.process_instance_id, ) if hasattr(task, "process_model_display_name"): diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index d7ba2e55..3105d1d8 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -898,7 +898,6 @@ def task_list_my_tasks(page: int = 1, per_page: int = 100) -> flask.wrappers.Res .add_columns( ProcessInstanceModel.process_model_identifier, ProcessInstanceModel.process_group_identifier, - ActiveTaskModel.task_data, ActiveTaskModel.task_name, ActiveTaskModel.task_title, ActiveTaskModel.task_type, diff --git a/src/spiffworkflow_backend/services/process_instance_processor.py b/src/spiffworkflow_backend/services/process_instance_processor.py index 38d4fe37..4030e054 100644 --- a/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/src/spiffworkflow_backend/services/process_instance_processor.py @@ -563,7 +563,6 @@ class ProcessInstanceProcessor: task_title=ready_or_waiting_task.task_spec.description, task_type=ready_or_waiting_task.task_spec.__class__.__name__, task_status=ready_or_waiting_task.get_state_name(), - task_data=json.dumps(ready_or_waiting_task.data), ) db.session.add(active_task)