From daf920666b39666caee890df9d8dbc5cb349c0de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Feb 2023 19:31:39 +0000 Subject: [PATCH 1/5] Bump actions/upload-artifact from 2 to 3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/backend_tests.yml | 6 +++--- .github/workflows/frontend_tests.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/backend_tests.yml b/.github/workflows/backend_tests.yml index 12e503345..22965a1af 100644 --- a/.github/workflows/backend_tests.yml +++ b/.github/workflows/backend_tests.yml @@ -140,7 +140,7 @@ jobs: - name: Upload coverage data # pin to upload coverage from only one matrix entry, otherwise coverage gets confused later if: always() && matrix.session == 'tests' && matrix.python == '3.11' && matrix.os == 'ubuntu-latest' && matrix.database == 'mysql' - uses: "actions/upload-artifact@v3.0.0" + uses: "actions/upload-artifact@v3" # this action doesn't seem to respect working-directory so include working-directory value in path with: name: coverage-data @@ -148,14 +148,14 @@ jobs: - name: Upload documentation if: matrix.session == 'docs-build' - uses: actions/upload-artifact@v3.0.0 + uses: actions/upload-artifact@v3 with: name: docs path: docs/_build - name: Upload logs if: failure() && matrix.session == 'tests' - uses: "actions/upload-artifact@v3.0.0" + uses: "actions/upload-artifact@v3" with: name: logs-${{matrix.python}}-${{matrix.os}}-${{matrix.database}} path: "./log/*.log" diff --git a/.github/workflows/frontend_tests.yml b/.github/workflows/frontend_tests.yml index 405b359c2..4e80311b1 100644 --- a/.github/workflows/frontend_tests.yml +++ b/.github/workflows/frontend_tests.yml @@ -108,21 +108,21 @@ jobs: run: ./bin/get_logs_from_docker_compose >./log/docker_compose.log - name: Upload logs if: failure() - uses: "actions/upload-artifact@v3.0.0" + uses: "actions/upload-artifact@v3" with: name: spiffworkflow-backend-logs path: "./spiffworkflow-backend/log/*.log" # https://github.com/cypress-io/github-action#artifacts - name: upload_screenshots - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: failure() with: name: cypress-screenshots path: ./spiffworkflow-frontend/cypress/screenshots # Test run video was always captured, so this action uses "always()" condition - name: upload_videos - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: failure() with: name: cypress-videos From ab75215a15e2d9e8dd2b2c8fba04f47980ce86ea Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Thu, 16 Mar 2023 14:38:00 -0400 Subject: [PATCH 2/5] Retry locked user input submissions (#185) --- .../spiffworkflow_backend/config/default.py | 8 +++++++ .../routes/tasks_controller.py | 7 +++++- .../services/process_instance_processor.py | 24 +++++++++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py b/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py index ca808564d..2af3e7df0 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py @@ -129,5 +129,13 @@ SPIFFWORKFLOW_BACKEND_ENGINE_STEP_DEFAULT_STRATEGY_WEB = environ.get( "SPIFFWORKFLOW_BACKEND_ENGINE_STEP_DEFAULT_STRATEGY_WEB", default="greedy" ) +SPIFFWORKFLOW_BACKEND_USER_INPUT_REQUIRED_LOCK_RETRY_TIMES = int( + environ.get("SPIFFWORKFLOW_BACKEND_USER_INPUT_REQUIRED_LOCK_RETRY_TIMES", default="3") +) + +SPIFFWORKFLOW_BACKEND_USER_INPUT_REQUIRED_LOCK_RETRY_INTERVAL_IN_SECONDS = int( + environ.get("SPIFFWORKFLOW_BACKEND_USER_INPUT_REQUIRED_LOCK_RETRY_INTERVAL_IN_SECONDS", default="1") +) + # this is only used in CI. use SPIFFWORKFLOW_BACKEND_DATABASE_URI instead for real configuration SPIFFWORKFLOW_BACKEND_DATABASE_PASSWORD = environ.get("SPIFFWORKFLOW_BACKEND_DATABASE_PASSWORD", default=None) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py index 7ca7c6ebe..ad9868e63 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py @@ -378,8 +378,13 @@ def task_submit_shared( only_tasks_that_can_be_completed=True, ) + retry_times = current_app.config["SPIFFWORKFLOW_BACKEND_USER_INPUT_REQUIRED_LOCK_RETRY_TIMES"] + retry_interval_in_seconds = current_app.config[ + "SPIFFWORKFLOW_BACKEND_USER_INPUT_REQUIRED_LOCK_RETRY_INTERVAL_IN_SECONDS" + ] + with sentry_sdk.start_span(op="task", description="complete_form_task"): - processor.lock_process_instance("Web") + processor.lock_process_instance("Web", retry_times, retry_interval_in_seconds) ProcessInstanceService.complete_form_task( processor=processor, spiff_task=spiff_task, diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 6c8b64fc7..5e771c12a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -91,9 +91,8 @@ from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.process_instance_lock_service import ( ProcessInstanceLockService, ) -from spiffworkflow_backend.services.process_instance_queue_service import ( - ProcessInstanceQueueService, -) +from spiffworkflow_backend.services.process_instance_queue_service import ProcessInstanceIsAlreadyLockedError +from spiffworkflow_backend.services.process_instance_queue_service import ProcessInstanceQueueService from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.service_task_service import ServiceTaskDelegate from spiffworkflow_backend.services.spec_file_service import SpecFileService @@ -1459,8 +1458,23 @@ class ProcessInstanceProcessor: return the_status # TODO: replace with implicit/more granular locking in workflow execution service - def lock_process_instance(self, lock_prefix: str) -> None: - ProcessInstanceQueueService.dequeue(self.process_instance_model) + # TODO: remove the retry logic once all user_input_required's don't need to be locked to check timers + def lock_process_instance( + self, lock_prefix: str, retry_count: int = 0, retry_interval_in_seconds: int = 0 + ) -> None: + try: + ProcessInstanceQueueService.dequeue(self.process_instance_model) + except ProcessInstanceIsAlreadyLockedError as e: + if retry_count > 0: + current_app.logger.info( + f"process_instance_id {self.process_instance_model.id} is locked. " + f"will retry {retry_count} times with delay of {retry_interval_in_seconds}." + ) + if retry_interval_in_seconds > 0: + time.sleep(retry_interval_in_seconds) + self.lock_process_instance(lock_prefix, retry_count - 1, retry_interval_in_seconds) + else: + raise e # TODO: replace with implicit/more granular locking in workflow execution service def unlock_process_instance(self, lock_prefix: str) -> None: From 4379919440200f3d21f0be6bdbbe82d0243b00af Mon Sep 17 00:00:00 2001 From: burnettk Date: Thu, 16 Mar 2023 16:16:02 -0400 Subject: [PATCH 3/5] add finance.project-lead user --- .../realm_exports/spiffworkflow-realm.json | 71 ++++++++++++------- .../keycloak/test_user_lists/status | 1 + 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json b/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json index 1c57944b8..d44353b86 100644 --- a/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json +++ b/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json @@ -1505,6 +1505,29 @@ "realmRoles" : [ "default-roles-spiffworkflow" ], "notBefore" : 0, "groups" : [ ] + }, { + "id" : "d123d384-66a4-4db5-9dbb-d73c12047001", + "createdTimestamp" : 1678997616280, + "username" : "finance.project-lead", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "finance.project-lead@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "128" ] + }, + "credentials" : [ { + "id" : "b680f5c5-c2de-4255-9d23-7e18cff3ac4e", + "type" : "password", + "createdDate" : 1678997616336, + "secretData" : "{\"value\":\"4kasmb11Sv62rInh8eFUhS3rGYNymzsvxzfsEIWGYhnlisYuo1iTS2opv/kET/NyJlsYrfwc7yrIqSHvkUHkkA==\",\"salt\":\"q/ees3a4K+3K11olnfPzCQ==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] }, { "id" : "f6d2488a-446c-493b-bbe8-210ede6f3e42", "createdTimestamp" : 1674148694899, @@ -4578,7 +4601,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-address-mapper" ] + "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper" ] } }, { "id" : "d68e938d-dde6-47d9-bdc8-8e8523eb08cd", @@ -4596,7 +4619,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "saml-user-property-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "saml-role-list-mapper" ] } }, { "id" : "3854361d-3fe5-47fb-9417-a99592e3dc5c", @@ -4686,7 +4709,7 @@ "internationalizationEnabled" : false, "supportedLocales" : [ ], "authenticationFlows" : [ { - "id" : "04b09640-f53c-4c1b-b2b1-8cac25afc2bb", + "id" : "c54f2b16-9254-481a-9997-fb6cafaa2c00", "alias" : "Account verification options", "description" : "Method with which to verity the existing account", "providerId" : "basic-flow", @@ -4708,7 +4731,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "e7c246f4-71c3-4a48-9037-72438bdcfcbb", + "id" : "eae97d77-649e-4475-a0a3-57fea93a6b5a", "alias" : "Authentication Options", "description" : "Authentication options.", "providerId" : "basic-flow", @@ -4737,7 +4760,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "6e9d415e-98f7-4459-b10b-45b08302c681", + "id" : "1fe6063d-c996-44ae-a082-c11d35b4f9ff", "alias" : "Browser - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -4759,7 +4782,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "c86b0fad-f7dd-4c58-974e-25eb83c1dacf", + "id" : "61f58306-7a2f-46ad-994f-04b5eb2a8146", "alias" : "Direct Grant - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -4781,7 +4804,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "cb7f4c87-a8fa-445a-a8d4-53869cdfed12", + "id" : "3b4f8b2f-cf0f-45d8-9105-65b1b3d088d5", "alias" : "First broker login - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -4803,7 +4826,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "8fa87954-bc65-4f1e-bc55-f5bb49f59fbb", + "id" : "211cd18a-4f93-4b60-ba6f-ae55860a0dbc", "alias" : "Handle Existing Account", "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId" : "basic-flow", @@ -4825,7 +4848,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "e617d826-c654-4c35-96ad-8381bd1e2298", + "id" : "7c1d5fb9-14f1-4603-bfec-449f8d98e1ea", "alias" : "Reset - Conditional OTP", "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId" : "basic-flow", @@ -4847,7 +4870,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "2e4a46ae-2813-4b71-9386-c08b2f063fa6", + "id" : "4456ff81-c720-4a78-9096-12c42902da8b", "alias" : "User creation or linking", "description" : "Flow for the existing/non-existing user alternatives", "providerId" : "basic-flow", @@ -4870,7 +4893,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "8fa69de0-13cf-4252-899b-c59a30ebd132", + "id" : "8bb53624-acec-447a-a768-532222ff2e8f", "alias" : "Verify Existing Account by Re-authentication", "description" : "Reauthentication of existing account", "providerId" : "basic-flow", @@ -4892,7 +4915,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "204d20f6-d9a7-49ff-a7a3-45386fb884f4", + "id" : "10a8b52f-b5a1-45ab-aeb6-26963d2c4ec4", "alias" : "browser", "description" : "browser based authentication", "providerId" : "basic-flow", @@ -4928,7 +4951,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "3c0c2987-65db-4920-ae44-34aba220c3fb", + "id" : "6ca05d13-7efc-43a4-8569-e7d45cb6db57", "alias" : "clients", "description" : "Base authentication for clients", "providerId" : "client-flow", @@ -4964,7 +4987,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "68a92113-be75-4e63-a322-8076d6c67650", + "id" : "2439ca39-9c37-4174-9b26-787604440ad6", "alias" : "direct grant", "description" : "OpenID Connect Resource Owner Grant", "providerId" : "basic-flow", @@ -4993,7 +5016,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "a630d78f-4fe1-4350-a19d-d091d1af514d", + "id" : "8c5814fd-61c0-40d3-9176-332b4558afb3", "alias" : "docker auth", "description" : "Used by Docker clients to authenticate against the IDP", "providerId" : "basic-flow", @@ -5008,7 +5031,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "f73b4437-8e82-4788-be69-e437b09b500c", + "id" : "104b06aa-5ce5-490b-9945-0d032d4d521b", "alias" : "first broker login", "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId" : "basic-flow", @@ -5031,7 +5054,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "b7c8cc6d-bc1f-446e-b263-72214b2f5c56", + "id" : "94dc8a72-c455-4fa1-abeb-ca7f248e24a6", "alias" : "forms", "description" : "Username, password, otp and other auth forms.", "providerId" : "basic-flow", @@ -5053,7 +5076,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "a3bdf79f-8c7d-4bff-807d-76fa61093446", + "id" : "3ee49693-4049-48ff-9c4d-7ffe6507779d", "alias" : "http challenge", "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId" : "basic-flow", @@ -5075,7 +5098,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ada41b4e-5a12-496d-aa1e-d31cf8c08226", + "id" : "444e348b-72ed-49e7-949f-b79fc08066d2", "alias" : "registration", "description" : "registration flow", "providerId" : "basic-flow", @@ -5091,7 +5114,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "1c858bcd-2031-4056-bbf0-1fbaecdd7068", + "id" : "b37ea96c-603f-4096-bca4-5f819c243aaf", "alias" : "registration form", "description" : "registration form", "providerId" : "form-flow", @@ -5127,7 +5150,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ff91e251-d85e-450b-bff7-d45be26777d5", + "id" : "6471829e-0771-4bd7-aa62-797eda24d5c2", "alias" : "reset credentials", "description" : "Reset credentials for a user if they forgot their password or something", "providerId" : "basic-flow", @@ -5163,7 +5186,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "7b0680a2-99b9-454c-b145-f286e9d60c58", + "id" : "c743556b-fdfc-4615-8154-a8ad4019dfaa", "alias" : "saml ecp", "description" : "SAML ECP Profile Authentication Flow", "providerId" : "basic-flow", @@ -5179,13 +5202,13 @@ } ] } ], "authenticatorConfig" : [ { - "id" : "aa1e4f55-3e7f-445a-a432-7a972776d719", + "id" : "92245b69-55b4-4bc3-98f1-03ef168f009e", "alias" : "create unique user config", "config" : { "require.password.update.after.registration" : "false" } }, { - "id" : "fd69765e-309b-4c5d-bdd5-51343427cd27", + "id" : "3e657993-a0fc-4073-88d5-882310927e19", "alias" : "review profile config", "config" : { "update.profile.on.first.login" : "missing" diff --git a/spiffworkflow-backend/keycloak/test_user_lists/status b/spiffworkflow-backend/keycloak/test_user_lists/status index 3b57deff1..7b76e707e 100644 --- a/spiffworkflow-backend/keycloak/test_user_lists/status +++ b/spiffworkflow-backend/keycloak/test_user_lists/status @@ -42,6 +42,7 @@ desktop3.sme@status.im,196 desktop4.sme@status.im,197 desktop5.sme@status.im,198 fin@status.im,118 +finance.project-lead@status.im,128 finance_user1@status.im fluffy.project-lead@status.im,162 harmeet@status.im,109 From 3614ec19c62acd2ad565f876742946e476b2c7ca Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 17 Mar 2023 09:42:38 -0400 Subject: [PATCH 4/5] add user --- .../realm_exports/spiffworkflow-realm.json | 73 ++++++++++++------- .../keycloak/test_user_lists/status | 1 + 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json b/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json index d44353b86..99e651b9a 100644 --- a/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json +++ b/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json @@ -396,7 +396,7 @@ "otpPolicyLookAheadWindow" : 1, "otpPolicyPeriod" : 30, "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppFreeOTPName" ], + "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName" ], "webAuthnPolicyRpEntityName" : "keycloak", "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyRpId" : "", @@ -991,6 +991,29 @@ "realmRoles" : [ "default-roles-spiffworkflow" ], "notBefore" : 0, "groups" : [ ] + }, { + "id" : "7b86b997-de98-478c-8550-cfca65e40c33", + "createdTimestamp" : 1679060366901, + "username" : "core18.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core18.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "233" ] + }, + "credentials" : [ { + "id" : "55ca2bd7-6f60-4f04-be21-df6300ca9442", + "type" : "password", + "createdDate" : 1679060366954, + "secretData" : "{\"value\":\"hC/O8LJ8/y/nXLmRFgRazOX9PXMHkowYH1iHUB4Iw9jzc8IMMv8dFrxu7XBklfyz7CPc1bmgl0k29jygRZYHlg==\",\"salt\":\"4R17tmLrHWyFAMvrfLMETQ==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] }, { "id" : "3b81b45e-759b-4d7a-aa90-adf7b447208c", "createdTimestamp" : 1676302140358, @@ -4601,7 +4624,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "saml-user-property-mapper" ] } }, { "id" : "d68e938d-dde6-47d9-bdc8-8e8523eb08cd", @@ -4619,7 +4642,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "saml-role-list-mapper" ] + "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-usermodel-property-mapper" ] } }, { "id" : "3854361d-3fe5-47fb-9417-a99592e3dc5c", @@ -4709,7 +4732,7 @@ "internationalizationEnabled" : false, "supportedLocales" : [ ], "authenticationFlows" : [ { - "id" : "c54f2b16-9254-481a-9997-fb6cafaa2c00", + "id" : "38a6b336-b026-46be-a8be-e8ff7b9da407", "alias" : "Account verification options", "description" : "Method with which to verity the existing account", "providerId" : "basic-flow", @@ -4731,7 +4754,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "eae97d77-649e-4475-a0a3-57fea93a6b5a", + "id" : "eb9fe753-cd35-4e65-bb34-e83ba7059566", "alias" : "Authentication Options", "description" : "Authentication options.", "providerId" : "basic-flow", @@ -4760,7 +4783,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "1fe6063d-c996-44ae-a082-c11d35b4f9ff", + "id" : "aa9c74f7-0426-4440-907f-4aa0f999eb1e", "alias" : "Browser - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -4782,7 +4805,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "61f58306-7a2f-46ad-994f-04b5eb2a8146", + "id" : "eb2a0849-c316-46bc-8b06-fd0cc50e3f32", "alias" : "Direct Grant - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -4804,7 +4827,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "3b4f8b2f-cf0f-45d8-9105-65b1b3d088d5", + "id" : "8f064003-823b-4be1-aa66-7324bf38c741", "alias" : "First broker login - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -4826,7 +4849,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "211cd18a-4f93-4b60-ba6f-ae55860a0dbc", + "id" : "eef22678-b09c-4ca8-bdcf-90ea44ff0120", "alias" : "Handle Existing Account", "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId" : "basic-flow", @@ -4848,7 +4871,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "7c1d5fb9-14f1-4603-bfec-449f8d98e1ea", + "id" : "4367f263-ef2c-426e-b5cd-49fff868ea1a", "alias" : "Reset - Conditional OTP", "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId" : "basic-flow", @@ -4870,7 +4893,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "4456ff81-c720-4a78-9096-12c42902da8b", + "id" : "b2e9c608-1779-4c03-b32a-03c77450abae", "alias" : "User creation or linking", "description" : "Flow for the existing/non-existing user alternatives", "providerId" : "basic-flow", @@ -4893,7 +4916,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "8bb53624-acec-447a-a768-532222ff2e8f", + "id" : "a8c79324-1881-4bb0-a8a2-83dfd54cacd1", "alias" : "Verify Existing Account by Re-authentication", "description" : "Reauthentication of existing account", "providerId" : "basic-flow", @@ -4915,7 +4938,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "10a8b52f-b5a1-45ab-aeb6-26963d2c4ec4", + "id" : "d1aa83c6-da36-4cb6-b6ed-f6ec556df614", "alias" : "browser", "description" : "browser based authentication", "providerId" : "basic-flow", @@ -4951,7 +4974,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "6ca05d13-7efc-43a4-8569-e7d45cb6db57", + "id" : "2afecfef-4bfb-4842-b338-7ed032a618d2", "alias" : "clients", "description" : "Base authentication for clients", "providerId" : "client-flow", @@ -4987,7 +5010,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "2439ca39-9c37-4174-9b26-787604440ad6", + "id" : "34dc1854-4969-4065-90e6-fef38b0dea98", "alias" : "direct grant", "description" : "OpenID Connect Resource Owner Grant", "providerId" : "basic-flow", @@ -5016,7 +5039,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "8c5814fd-61c0-40d3-9176-332b4558afb3", + "id" : "40557323-dbbc-48ee-9ed1-748b11c9628d", "alias" : "docker auth", "description" : "Used by Docker clients to authenticate against the IDP", "providerId" : "basic-flow", @@ -5031,7 +5054,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "104b06aa-5ce5-490b-9945-0d032d4d521b", + "id" : "d18b5c50-39fa-4b11-a7d2-0e6768e275c1", "alias" : "first broker login", "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId" : "basic-flow", @@ -5054,7 +5077,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "94dc8a72-c455-4fa1-abeb-ca7f248e24a6", + "id" : "976be80d-a88b-412c-8ad2-9ebe427793d4", "alias" : "forms", "description" : "Username, password, otp and other auth forms.", "providerId" : "basic-flow", @@ -5076,7 +5099,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "3ee49693-4049-48ff-9c4d-7ffe6507779d", + "id" : "83b3a411-ff7c-4cba-845a-9554c536d6b1", "alias" : "http challenge", "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId" : "basic-flow", @@ -5098,7 +5121,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "444e348b-72ed-49e7-949f-b79fc08066d2", + "id" : "1cb835a6-b38c-4f29-a6d8-d04d0a84d05e", "alias" : "registration", "description" : "registration flow", "providerId" : "basic-flow", @@ -5114,7 +5137,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "b37ea96c-603f-4096-bca4-5f819c243aaf", + "id" : "7ec06c82-6802-4ff4-a3ab-9b6a0b8dbc4b", "alias" : "registration form", "description" : "registration form", "providerId" : "form-flow", @@ -5150,7 +5173,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "6471829e-0771-4bd7-aa62-797eda24d5c2", + "id" : "f3bc2f7b-2074-4d93-9578-3abf648a6681", "alias" : "reset credentials", "description" : "Reset credentials for a user if they forgot their password or something", "providerId" : "basic-flow", @@ -5186,7 +5209,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "c743556b-fdfc-4615-8154-a8ad4019dfaa", + "id" : "e62e031b-9922-4682-b867-bc5c3a4a7e99", "alias" : "saml ecp", "description" : "SAML ECP Profile Authentication Flow", "providerId" : "basic-flow", @@ -5202,13 +5225,13 @@ } ] } ], "authenticatorConfig" : [ { - "id" : "92245b69-55b4-4bc3-98f1-03ef168f009e", + "id" : "c449f0aa-5f3c-4107-9f04-3222fa93a486", "alias" : "create unique user config", "config" : { "require.password.update.after.registration" : "false" } }, { - "id" : "3e657993-a0fc-4073-88d5-882310927e19", + "id" : "f7a6ed54-0ab8-4f29-9877-960bd65bf394", "alias" : "review profile config", "config" : { "update.profile.on.first.login" : "missing" diff --git a/spiffworkflow-backend/keycloak/test_user_lists/status b/spiffworkflow-backend/keycloak/test_user_lists/status index 7b76e707e..5af7736d5 100644 --- a/spiffworkflow-backend/keycloak/test_user_lists/status +++ b/spiffworkflow-backend/keycloak/test_user_lists/status @@ -21,6 +21,7 @@ core14.contributor@status.im,229 core15.contributor@status.im,230 core16.contributor@status.im,231 core17.contributor@status.im,232 +core18.contributor@status.im,233 core2.contributor@status.im,156 core3.contributor@status.im,157 core4.contributor@status.im,158 From 90aed76e214f7181dc4b449888ae5a20204dd403 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:55:11 -0400 Subject: [PATCH 5/5] UAT bug fix: suspended/terminated instances automagically resume (#186) --- .../routes/process_instances_controller.py | 38 +++++++++++++++++-- .../services/process_instance_processor.py | 18 ++++----- .../services/process_instance_service.py | 8 ++++ .../integration/test_process_api.py | 4 +- 4 files changed, 53 insertions(+), 15 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py index 489b710c7..a29727933 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -170,7 +170,17 @@ def process_instance_terminate( """Process_instance_run.""" process_instance = _find_process_instance_by_id_or_raise(process_instance_id) processor = ProcessInstanceProcessor(process_instance) - processor.terminate() + + try: + processor.lock_process_instance("Web") + processor.terminate() + except (ProcessInstanceIsNotEnqueuedError, ProcessInstanceIsAlreadyLockedError) as e: + ErrorHandlingService().handle_error(processor, e) + raise e + finally: + if ProcessInstanceLockService.has_lock(process_instance.id): + processor.unlock_process_instance("Web") + return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") @@ -180,7 +190,18 @@ def process_instance_suspend( ) -> flask.wrappers.Response: """Process_instance_suspend.""" process_instance = _find_process_instance_by_id_or_raise(process_instance_id) - ProcessInstanceProcessor.suspend(process_instance) + processor = ProcessInstanceProcessor(process_instance) + + try: + processor.lock_process_instance("Web") + processor.suspend() + except (ProcessInstanceIsNotEnqueuedError, ProcessInstanceIsAlreadyLockedError) as e: + ErrorHandlingService().handle_error(processor, e) + raise e + finally: + if ProcessInstanceLockService.has_lock(process_instance.id): + processor.unlock_process_instance("Web") + return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") @@ -190,7 +211,18 @@ def process_instance_resume( ) -> flask.wrappers.Response: """Process_instance_resume.""" process_instance = _find_process_instance_by_id_or_raise(process_instance_id) - ProcessInstanceProcessor.resume(process_instance) + processor = ProcessInstanceProcessor(process_instance) + + try: + processor.lock_process_instance("Web") + processor.resume() + except (ProcessInstanceIsNotEnqueuedError, ProcessInstanceIsAlreadyLockedError) as e: + ErrorHandlingService().handle_error(processor, e) + raise e + finally: + if ProcessInstanceLockService.has_lock(process_instance.id): + processor.unlock_process_instance("Web") + return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 5e771c12a..fda3d395b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1249,7 +1249,7 @@ class ProcessInstanceProcessor: self.add_step() self.save() # Saving the workflow seems to reset the status - self.suspend(self.process_instance_model) + self.suspend() def reset_process(self, spiff_step: int) -> None: """Reset a process to an earlier state.""" @@ -1292,7 +1292,7 @@ class ProcessInstanceProcessor: db.session.delete(row) self.save() - self.suspend(self.process_instance_model) + self.suspend() @staticmethod def get_parser() -> MyCustomParser: @@ -1900,16 +1900,14 @@ class ProcessInstanceProcessor: db.session.add(self.process_instance_model) db.session.commit() - @classmethod - def suspend(cls, process_instance: ProcessInstanceModel) -> None: + def suspend(self) -> None: """Suspend.""" - process_instance.status = ProcessInstanceStatus.suspended.value - db.session.add(process_instance) + self.process_instance_model.status = ProcessInstanceStatus.suspended.value + db.session.add(self.process_instance_model) db.session.commit() - @classmethod - def resume(cls, process_instance: ProcessInstanceModel) -> None: + def resume(self) -> None: """Resume.""" - process_instance.status = ProcessInstanceStatus.waiting.value - db.session.add(process_instance) + self.process_instance_model.status = ProcessInstanceStatus.waiting.value + db.session.add(self.process_instance_model) db.session.commit() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py index 5e149965d..d7ea5613b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py @@ -25,6 +25,7 @@ from spiffworkflow_backend.models.process_instance_file_data import ( from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.task import Task from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.services.assertion_service import safe_assertion from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.git_service import GitCommandError from spiffworkflow_backend.services.git_service import GitService @@ -95,6 +96,13 @@ class ProcessInstanceService: ) process_instance_lock_prefix = "Background" for process_instance in records: + with safe_assertion(process_instance.status == status_value) as false_assumption: + if false_assumption: + raise AssertionError( + f"Queue assumed process instance {process_instance.id} has status of {status_value} " + f"when it really is {process_instance.status}" + ) + locked = False processor = None try: diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index 086841c08..f7f644ddb 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1418,7 +1418,7 @@ class TestProcessApi(BaseTest): ) processor.save() - processor.suspend(process_instance) + processor.suspend() payload["description"] = "Message To Suspended" response = client.post( f"/v1.0/messages/{message_model_identifier}", @@ -1430,7 +1430,7 @@ class TestProcessApi(BaseTest): assert response.json assert response.json["error_code"] == "message_not_accepted" - processor.resume(process_instance) + processor.resume() payload["description"] = "Message To Resumed" response = client.post( f"/v1.0/messages/{message_model_identifier}",