From 1f25521506a6ed4caef400a13903af86a7eb83d2 Mon Sep 17 00:00:00 2001 From: burnettk Date: Tue, 25 Oct 2022 16:54:06 -0400 Subject: [PATCH] Squashed 'spiffworkflow-backend/' changes from 153061d4..1092ed11 1092ed11 Merge branch 'main' of github.com:sartography/spiffworkflow-backend into main 97b96fa9 Merge commit '999e0f4d2b7a3c3665feb806fd4f00dc50f2de8e' 4d2ebbe2 Refresh token (#6) 2c47d862 Revert "Assure that the Active Task Users table is cleared out before deleting the Active Task Record." 6c1660a3 Merge commit 'eff49e1ddb2e70d62e45866b429a1350443107d0' into main f7949ada Merge commit 'ac929cbae7717affed5fa357a56cf1a0256289d5' into main b70804ea Merge commit '65283df3cbfe95c6cd0d69e1cc0204d91c5d535b' 5b9ad7a7 Merge commit 'a59108db95274bef304cbd9246537206d0b449cf' 038c8e68 Merge commit '835160e5a82c7e0518fa25ed7fa1168c9c435739' aae7966b Merge commit 'a1cce807be1c4ac5f04c42d780df06c216a0de9c' 8527c6be Merge commit '7026fe1f779051f6eb99b872bcd45b14c0cbe88f' 1aa9a8da Merge commit '70480e9f91a1053531ea5db32a13a791c39fd89f' 40383711 Merge commit '9a1e33696a744527a18dbec667140edb4038ac94' 2c7b2e28 Merge commit 'd643de93e5ceaae28a6e65044cc85dd039530d2c' e5e70161 Merge commit '2b702661f3bd9b79de887e82e5a5925d07341eb6' b0b92474 updateing poetry lock file. e5fd3df1 Merge commit 'f0b608789b6cdc3ef4303efac053746c98571a48' dfe4e3e1 Merge commit 'c5a2f8b16c9a614b944a6e4610594eba1dac7b9f' into main 78dcaec3 pointless change git-subtree-dir: spiffworkflow-backend git-subtree-split: 1092ed11187d1b825fa1c5e18f0d96592f0b98e7 --- README.rst | 2 + .../{4ba2ed52a63a_.py => 3bd6b0b1b8ae_.py} | 10 +-- src/spiffworkflow_backend/api.yml | 4 +- .../models/secret_model.py | 4 +- .../routes/process_api_blueprint.py | 2 +- .../services/process_instance_processor.py | 4 -- .../services/secret_service.py | 48 +++++-------- .../services/service_task_service.py | 16 ++++- .../integration/test_secret_service.py | 72 ++----------------- .../unit/test_active_task.py | 49 ------------- 10 files changed, 50 insertions(+), 161 deletions(-) rename migrations/versions/{4ba2ed52a63a_.py => 3bd6b0b1b8ae_.py} (98%) delete mode 100644 tests/spiffworkflow_backend/unit/test_active_task.py diff --git a/README.rst b/README.rst index e9d3a68de..4f769d64f 100644 --- a/README.rst +++ b/README.rst @@ -90,3 +90,5 @@ This project was generated from `@cjolowicz`_'s `Hypermodern Python Cookiecutter .. github-only .. _Contributor Guide: CONTRIBUTING.rst .. _Usage: https://spiffworkflow-backend.readthedocs.io/en/latest/usage.html + +(test) diff --git a/migrations/versions/4ba2ed52a63a_.py b/migrations/versions/3bd6b0b1b8ae_.py similarity index 98% rename from migrations/versions/4ba2ed52a63a_.py rename to migrations/versions/3bd6b0b1b8ae_.py index e0578a3a9..80c47958b 100644 --- a/migrations/versions/4ba2ed52a63a_.py +++ b/migrations/versions/3bd6b0b1b8ae_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 4ba2ed52a63a +Revision ID: 3bd6b0b1b8ae Revises: -Create Date: 2022-10-21 09:31:30.520942 +Create Date: 2022-10-25 12:31:50.177599 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '4ba2ed52a63a' +revision = '3bd6b0b1b8ae' down_revision = None branch_labels = None depends_on = None @@ -146,10 +146,10 @@ def upgrade(): sa.Column('id', sa.Integer(), nullable=False), sa.Column('key', sa.String(length=50), nullable=False), sa.Column('value', sa.Text(), nullable=False), - sa.Column('creator_user_id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['creator_user_id'], ['user.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('key') ) diff --git a/src/spiffworkflow_backend/api.yml b/src/spiffworkflow_backend/api.yml index f4b7fdd01..614d4f26c 100755 --- a/src/spiffworkflow_backend/api.yml +++ b/src/spiffworkflow_backend/api.yml @@ -2117,8 +2117,8 @@ components: type: string example: my_super_secret_value nullable: false - creator_user_id: - description: The id of the logged in user that created this secret + user_id: + description: The id of the logged in user that updated this secret type: number example: 1 nullable: false diff --git a/src/spiffworkflow_backend/models/secret_model.py b/src/spiffworkflow_backend/models/secret_model.py index fed25b1a8..92fd470a3 100644 --- a/src/spiffworkflow_backend/models/secret_model.py +++ b/src/spiffworkflow_backend/models/secret_model.py @@ -17,7 +17,7 @@ class SecretModel(SpiffworkflowBaseDBModel): id: int = db.Column(db.Integer, primary_key=True) key: str = db.Column(db.String(50), unique=True, nullable=False) value: str = db.Column(db.Text(), nullable=False) - creator_user_id: int = db.Column(ForeignKey(UserModel.id), nullable=False) + user_id: int = db.Column(ForeignKey(UserModel.id), nullable=False) updated_at_in_seconds: int = db.Column(db.Integer) created_at_in_seconds: int = db.Column(db.Integer) @@ -29,4 +29,4 @@ class SecretModelSchema(Schema): """Meta.""" model = SecretModel - fields = ["key", "value", "creator_user_id"] + fields = ["key", "value", "user_id"] diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index 1e95c575d..b499b7880 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1408,7 +1408,7 @@ def add_secret(body: Dict) -> Response: def update_secret(key: str, body: dict) -> Response: """Update secret.""" - SecretService().update_secret(key, body["value"], body["creator_user_id"]) + SecretService().update_secret(key, body["value"], body["user_id"]) return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") diff --git a/src/spiffworkflow_backend/services/process_instance_processor.py b/src/spiffworkflow_backend/services/process_instance_processor.py index 42d1abb87..70c38dbae 100644 --- a/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/src/spiffworkflow_backend/services/process_instance_processor.py @@ -522,10 +522,6 @@ class ProcessInstanceProcessor: ).all() if len(active_tasks) > 0: for at in active_tasks: - active_task_users = at.active_task_users - for atu in active_task_users: - # don't trust sqlalchemy to cascade - ran into race condition here. - db.session.delete(atu) db.session.delete(at) db.session.add(self.process_instance_model) diff --git a/src/spiffworkflow_backend/services/secret_service.py b/src/spiffworkflow_backend/services/secret_service.py index 2aaf223df..f18ef7d27 100644 --- a/src/spiffworkflow_backend/services/secret_service.py +++ b/src/spiffworkflow_backend/services/secret_service.py @@ -33,12 +33,12 @@ class SecretService: def add_secret( key: str, value: str, - creator_user_id: int, + user_id: int, ) -> SecretModel: """Add_secret.""" # encrypted_key = self.encrypt_key(key) secret_model = SecretModel( - key=key, value=value, creator_user_id=creator_user_id + key=key, value=value, user_id=user_id ) db.session.add(secret_model) try: @@ -67,29 +67,22 @@ class SecretService: def update_secret( key: str, value: str, - creator_user_id: int, + user_id: int, create_if_not_exists: Optional[bool] = False, ) -> None: """Does this pass pre commit?""" secret_model = SecretModel.query.filter(SecretModel.key == key).first() if secret_model: - if secret_model.creator_user_id == creator_user_id: - secret_model.value = value - db.session.add(secret_model) - try: - db.session.commit() - except Exception as e: - db.session.rollback() - raise e - else: - raise ApiError( - error_code="update_secret_error", - message=f"User: {creator_user_id} cannot update the secret with key : {key}", - status_code=401, - ) + secret_model.value = value + db.session.add(secret_model) + try: + db.session.commit() + except Exception as e: + db.session.rollback() + raise e elif create_if_not_exists: SecretService.add_secret( - key=key, value=value, creator_user_id=creator_user_id + key=key, value=value, user_id=user_id ) else: raise ApiError( @@ -103,21 +96,14 @@ class SecretService: """Delete secret.""" secret_model = SecretModel.query.filter(SecretModel.key == key).first() if secret_model: - if secret_model.creator_user_id == user_id: - db.session.delete(secret_model) - try: - db.session.commit() - except Exception as e: - raise ApiError( - error_code="delete_secret_error", - message=f"Could not delete secret with key: {key}. Original error is: {e}", - ) from e - else: + db.session.delete(secret_model) + try: + db.session.commit() + except Exception as e: raise ApiError( error_code="delete_secret_error", - message=f"User: {user_id} cannot delete the secret with key : {key}", - status_code=401, - ) + message=f"Could not delete secret with key: {key}. Original error is: {e}", + ) from e else: raise ApiError( error_code="delete_secret_error", diff --git a/src/spiffworkflow_backend/services/service_task_service.py b/src/spiffworkflow_backend/services/service_task_service.py index 97c879965..0ec8a9cb8 100644 --- a/src/spiffworkflow_backend/services/service_task_service.py +++ b/src/spiffworkflow_backend/services/service_task_service.py @@ -4,6 +4,7 @@ from typing import Any import requests from flask import current_app +from flask import g from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.secret_service import SecretService @@ -57,7 +58,20 @@ class ServiceTaskDelegate: if proxied_response.status_code != 200: print("got error from connector proxy") - return proxied_response.text + parsed_response = json.loads(proxied_response.text) + + if "refreshed_token_set" not in parsed_response: + return proxied_response.text + + secret_key = parsed_response["auth"] + refreshed_token_set = json.dumps( + parsed_response["refreshed_token_set"] + ) + SecretService().update_secret( + secret_key, refreshed_token_set, g.user.id + ) + + return json.dumps(parsed_response["api_response"]) class ServiceTaskService: diff --git a/tests/spiffworkflow_backend/integration/test_secret_service.py b/tests/spiffworkflow_backend/integration/test_secret_service.py index 3cfc83a7c..3735ebc56 100644 --- a/tests/spiffworkflow_backend/integration/test_secret_service.py +++ b/tests/spiffworkflow_backend/integration/test_secret_service.py @@ -71,7 +71,7 @@ class TestSecretService(SecretServiceTestHelpers): assert test_secret is not None assert test_secret.key == self.test_key assert test_secret.value == self.test_value - assert test_secret.creator_user_id == with_super_admin_user.id + assert test_secret.user_id == with_super_admin_user.id def test_add_secret_duplicate_key_fails( self, @@ -129,24 +129,6 @@ class TestSecretService(SecretServiceTestHelpers): assert new_secret assert new_secret.value == "new_secret_value" # noqa: S105 - def test_update_secret_bad_user_fails( - self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel, - ) -> None: - """Test_update_secret_bad_user.""" - self.add_test_secret(with_super_admin_user) - with pytest.raises(ApiError) as ae: - SecretService.update_secret( - self.test_key, "new_secret_value", with_super_admin_user.id + 1 - ) # noqa: S105 - assert ( - ae.value.message - == f"User: {with_super_admin_user.id+1} cannot update the secret with key : test_key" - ) - def test_update_secret_bad_secret_fails( self, app: Flask, @@ -174,27 +156,11 @@ class TestSecretService(SecretServiceTestHelpers): self.add_test_secret(with_super_admin_user) secrets = SecretModel.query.all() assert len(secrets) == 1 - assert secrets[0].creator_user_id == with_super_admin_user.id + assert secrets[0].user_id == with_super_admin_user.id SecretService.delete_secret(self.test_key, with_super_admin_user.id) secrets = SecretModel.query.all() assert len(secrets) == 0 - def test_delete_secret_bad_user_fails( - self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel, - ) -> None: - """Test_delete_secret_bad_user.""" - self.add_test_secret(with_super_admin_user) - with pytest.raises(ApiError) as ae: - SecretService.delete_secret(self.test_key, with_super_admin_user.id + 1) - assert ( - f"User: {with_super_admin_user.id+1} cannot delete the secret with key" - in ae.value.message - ) - def test_delete_secret_bad_secret_fails( self, app: Flask, @@ -223,7 +189,7 @@ class TestSecretServiceApi(SecretServiceTestHelpers): secret_model = SecretModel( key=self.test_key, value=self.test_value, - creator_user_id=with_super_admin_user.id, + user_id=with_super_admin_user.id, ) data = json.dumps(SecretModelSchema().dump(secret_model)) response: TestResponse = client.post( @@ -234,11 +200,11 @@ class TestSecretServiceApi(SecretServiceTestHelpers): ) assert response.json secret: dict = response.json - for key in ["key", "value", "creator_user_id"]: + for key in ["key", "value", "user_id"]: assert key in secret.keys() assert secret["key"] == self.test_key assert secret["value"] == self.test_value - assert secret["creator_user_id"] == with_super_admin_user.id + assert secret["user_id"] == with_super_admin_user.id def test_get_secret( self, @@ -273,7 +239,7 @@ class TestSecretServiceApi(SecretServiceTestHelpers): secret_model = SecretModel( key=self.test_key, value="new_secret_value", - creator_user_id=with_super_admin_user.id, + user_id=with_super_admin_user.id, ) response = client.put( f"/v1.0/secrets/{self.test_key}", @@ -308,32 +274,6 @@ class TestSecretServiceApi(SecretServiceTestHelpers): with pytest.raises(ApiError): secret = SecretService.get_secret(self.test_key) - def test_delete_secret_bad_user( - self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel, - ) -> None: - """Test_delete_secret_bad_user.""" - user_1 = self.find_or_create_user() - user_2 = self.find_or_create_user("test_user_2") - self.add_test_secret(user_1) - - # ensure user has permissions to delete the given secret - self.add_permissions_to_user( - user_2, - target_uri=f"/v1.0/secrets/{self.test_key}", - permission_names=["delete"], - ) - secret_response = client.delete( - f"/v1.0/secrets/{self.test_key}", - headers=self.logged_in_headers(user_2), - ) - assert secret_response.status_code == 401 - assert secret_response.json - assert secret_response.json["error_code"] == "delete_secret_error" - def test_delete_secret_bad_key( self, app: Flask, diff --git a/tests/spiffworkflow_backend/unit/test_active_task.py b/tests/spiffworkflow_backend/unit/test_active_task.py deleted file mode 100644 index 6381482b4..000000000 --- a/tests/spiffworkflow_backend/unit/test_active_task.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Process Model.""" -from decimal import Decimal - -from flask.app import Flask -from flask_bpmn.models.db import db - -from spiffworkflow_backend.models.active_task import ActiveTaskModel -from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel -from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor -from tests.spiffworkflow_backend.helpers.base_test import BaseTest -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec - -from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel - - -class TestActiveTask(BaseTest): - - def test_can_create_and_delete_an_active_task ( - self, app: Flask, with_db_and_bpmn_file_cleanup: None - ) -> None: - process_model = load_test_spec( - "call_activity_test", - process_model_source_directory="call_activity_same_directory", - ) - - process_instance = self.create_process_instance_from_process_model( - process_model - ) - active_task = ActiveTaskModel( - process_instance_id=process_instance.id, - process_model_display_name="my shorts", - form_file_name="my_file_name", - ui_form_file_name="", - task_id="1234", - task_name="any old thing", - task_title="", - task_type="test type", - task_status="WAITING", - lane_assignment_id=None, - ) - initiator_user = self.find_or_create_user("initiator_user") - db.session.add(active_task) - db.session.commit() - active_task_user = ActiveTaskUserModel(active_task_id=active_task.id, user_id=initiator_user.id) - db.session.add(active_task_user) - db.session.commit() - processor = ProcessInstanceProcessor(process_instance) - processor.save() # This should clear out all active tasks and active task users. - assert(len(db.session.query(ActiveTaskModel).all()) == 0) \ No newline at end of file