From c2ba0ccffc4cf6d236a5c8fc6ae3a678bd584feb Mon Sep 17 00:00:00 2001 From: shuhaib-aot Date: Tue, 27 Feb 2024 15:41:38 +0530 Subject: [PATCH 01/39] fixed application create function --- forms-flow-web/src/components/Form/Item/apiSelectHelper.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/forms-flow-web/src/components/Form/Item/apiSelectHelper.js b/forms-flow-web/src/components/Form/Item/apiSelectHelper.js index 0465e54240..11b4f95adb 100644 --- a/forms-flow-web/src/components/Form/Item/apiSelectHelper.js +++ b/forms-flow-web/src/components/Form/Item/apiSelectHelper.js @@ -25,8 +25,7 @@ const selectApplicationCreateAPI = ( !isAuthenticated && !isDraftEnabled && !isDraftCreated; let usePublicDraftSubmit = !isAuthenticated && isDraftEnabled && isDraftCreated; - let selection = null; - + let selection = isAuthenticated ? applicationCreate : publicApplicationCreate; if (useDraftSubmission) selection = draftSubmit; if (useApplicationCreate) selection = applicationCreate; if (usePublicApplicationCreate) selection = publicApplicationCreate; From 0485bd4a4c003fd57bb3455e88e5e4c7ce3ef60d Mon Sep 17 00:00:00 2001 From: Sumesh Kariyil Date: Mon, 23 Sep 2024 12:56:54 -0700 Subject: [PATCH 02/39] Adding changes for JDK version updates --- .github/workflows/forms-flow-bpm-cd.yml | 21 +++++----- forms-flow-bpm/Dockerfile | 9 ++--- forms-flow-bpm/Dockerfile-ARM64 | 39 ------------------- forms-flow-bpm/forms-flow-bpm-camunda/pom.xml | 2 +- forms-flow-bpm/pom.xml | 9 ++++- 5 files changed, 23 insertions(+), 57 deletions(-) delete mode 100644 forms-flow-bpm/Dockerfile-ARM64 diff --git a/.github/workflows/forms-flow-bpm-cd.yml b/.github/workflows/forms-flow-bpm-cd.yml index 5b17646278..603b7c6897 100644 --- a/.github/workflows/forms-flow-bpm-cd.yml +++ b/.github/workflows/forms-flow-bpm-cd.yml @@ -84,24 +84,25 @@ jobs: platforms: linux/amd64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - - name: Build and push Docker image - amd64 + - name: Build and push Docker image if: ${{ github.ref == 'refs/heads/master' }} uses: docker/build-push-action@v2 with: context: forms-flow-bpm push: true + platforms: linux/amd64,linux/arm64/v8 file: forms-flow-bpm/Dockerfile tags: ${{ steps.meta.outputs.tags }}, formsflow/forms-flow-bpm:latest labels: ${{ steps.meta.outputs.labels }} - - name: Build and push Docker image - arm64 - uses: docker/build-push-action@v4 - with: - context: forms-flow-bpm - file: forms-flow-bpm/Dockerfile-ARM64 - push: true - platforms: linux/arm64/v8 - tags: ${{ steps.meta.outputs.tags }}-arm64 - labels: ${{ steps.meta.outputs.labels }} + # - name: Build and push Docker image - arm64 + # uses: docker/build-push-action@v4 + # with: + # context: forms-flow-bpm + # file: forms-flow-bpm/Dockerfile-ARM64 + # push: true + # platforms: linux/arm64/v8 + # tags: ${{ steps.meta.outputs.tags }}-arm64 + # labels: ${{ steps.meta.outputs.labels }} - name: Scan Docker image 🐳 uses: snyk/actions/docker@master continue-on-error: true diff --git a/forms-flow-bpm/Dockerfile b/forms-flow-bpm/Dockerfile index baaaebd8bf..e16398450b 100644 --- a/forms-flow-bpm/Dockerfile +++ b/forms-flow-bpm/Dockerfile @@ -1,4 +1,3 @@ -# Modified by Yichun Zhao and Walter Moar # Maven build FROM maven:3.8.1-openjdk-17-slim AS MAVEN_TOOL_CHAIN @@ -19,16 +18,14 @@ COPY forms-flow-bpm-utils/src ./forms-flow-bpm-utils/src/ RUN mvn -s /usr/share/maven/ref/settings-docker.xml install -P camunda # Final custom slim java image (for apk command see 17-jdk-alpine-slim) -FROM openjdk:17-jdk-alpine -# Update packages including OpenSSL -RUN apk update && apk upgrade +FROM openjdk:21-ea-jdk # set label for image LABEL Name="formsflow" -ENV JAVA_VERSION=17-ea+14 +ENV JAVA_VERSION=21-ea+14 ENV JAVA_HOME=/opt/java/openjdk-17\ - PATH="/opt/java/openjdk-17/bin:$PATH" + PATH="/opt/java/openjdk-21/bin:$PATH" EXPOSE 8080 # OpenShift has /app in the image, but it's missing when doing local development - Create it when missing diff --git a/forms-flow-bpm/Dockerfile-ARM64 b/forms-flow-bpm/Dockerfile-ARM64 deleted file mode 100644 index 2d70697246..0000000000 --- a/forms-flow-bpm/Dockerfile-ARM64 +++ /dev/null @@ -1,39 +0,0 @@ - -# Maven build -FROM arm64v8/maven:3.8.1-openjdk-17-slim AS MAVEN_TOOL_CHAIN -COPY settings-docker.xml /usr/share/maven/ref/ -WORKDIR /tmp/ - -COPY pom*.xml . -COPY forms-flow-bpm-utils/pom.xml ./forms-flow-bpm-utils/ -COPY forms-flow-bpm-camunda/pom.xml ./forms-flow-bpm-camunda/ - -# COPY src /tmp/src/ -COPY forms-flow-bpm-camunda/src ./forms-flow-bpm-camunda/src/ -COPY forms-flow-bpm-utils/src ./forms-flow-bpm-utils/src/ - -# This allows Docker to cache most of the maven dependencies -#TODO This needs to be fixed, It throws error saying sub modules cannot be found -# RUN mvn -s /usr/share/maven/ref/settings-docker.xml dependency:resolve-plugins dependency:resolve dependency:go-offline -B -P camunda -RUN mvn -s /usr/share/maven/ref/settings-docker.xml install -P camunda - -# Final custom slim java image (for apk command see 17-jdk-alpine-slim) -FROM arm64v8/openjdk:17-ea-16-jdk - -# set label for image -LABEL Name="formsflow" - -ENV JAVA_VERSION=17-ea+14 -ENV JAVA_HOME=/opt/java/openjdk-17\ - PATH="/opt/java/openjdk-17/bin:$PATH" - -EXPOSE 8080 -# OpenShift has /app in the image, but it's missing when doing local development - Create it when missing -RUN test ! -d /app && mkdir /app || : -# Add spring boot application -RUN mkdir -p /app -COPY --from=MAVEN_TOOL_CHAIN /tmp/forms-flow-bpm-camunda/target/forms-flow-bpm.jar ./app -RUN chmod a+rwx -R /app -WORKDIR /app -VOLUME /tmp -ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom", "-Dpolyglot.js.nashorn-compat=true", "-Dpolyglot.engine.WarnInterpreterOnly=false", "-jar","/app/forms-flow-bpm.jar"] \ No newline at end of file diff --git a/forms-flow-bpm/forms-flow-bpm-camunda/pom.xml b/forms-flow-bpm/forms-flow-bpm-camunda/pom.xml index 20301f6573..cc4dff4db7 100644 --- a/forms-flow-bpm/forms-flow-bpm-camunda/pom.xml +++ b/forms-flow-bpm/forms-flow-bpm-camunda/pom.xml @@ -31,7 +31,7 @@ 7.20.0 1.5.4 1.5.0 - 3.1.10 + 3.1.12 2.6.8 2.15.0 diff --git a/forms-flow-bpm/pom.xml b/forms-flow-bpm/pom.xml index 62112a7b45..227c8097a8 100644 --- a/forms-flow-bpm/pom.xml +++ b/forms-flow-bpm/pom.xml @@ -26,7 +26,7 @@ 7.20.0 1.5.4 1.5.0 - 3.1.10 + 3.1.12 2.6.8 2.15.0 @@ -343,6 +343,13 @@ commons-fileupload ${version.commonsFileUpload} + + + org.testng + testng + 7.5.1 + test + From 347e5b345a964cba188e7711c1a2c4037981e56e Mon Sep 17 00:00:00 2001 From: Sumesh Kariyil Date: Mon, 23 Sep 2024 13:30:12 -0700 Subject: [PATCH 03/39] Update forms-flow-bpm-cd.yml --- .github/workflows/forms-flow-bpm-cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/forms-flow-bpm-cd.yml b/.github/workflows/forms-flow-bpm-cd.yml index 603b7c6897..d418016e22 100644 --- a/.github/workflows/forms-flow-bpm-cd.yml +++ b/.github/workflows/forms-flow-bpm-cd.yml @@ -74,14 +74,14 @@ jobs: key: ${{ runner.os }}-buildx-${{ matrix.name }}-${{ github.sha }} restore-keys: | ${{ runner.os }}-buildx-${{ matrix.name }} - - name: Build and push Docker image - amd64 + - name: Build and push Docker image if: ${{ github.ref != 'refs/heads/master' }} uses: docker/build-push-action@v4 with: context: forms-flow-bpm push: true file: forms-flow-bpm/Dockerfile - platforms: linux/amd64 + platforms: linux/amd64,linux/arm64/v8 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - name: Build and push Docker image From 58470060fa3895e7234867da0d219867d984e1cc Mon Sep 17 00:00:00 2001 From: Sumesh Kariyil Date: Mon, 23 Sep 2024 14:27:17 -0700 Subject: [PATCH 04/39] Adding changes for JDK version updates --- forms-flow-bpm/forms-flow-bpm-camunda/pom.xml | 2 +- forms-flow-bpm/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/forms-flow-bpm/forms-flow-bpm-camunda/pom.xml b/forms-flow-bpm/forms-flow-bpm-camunda/pom.xml index cc4dff4db7..20301f6573 100644 --- a/forms-flow-bpm/forms-flow-bpm-camunda/pom.xml +++ b/forms-flow-bpm/forms-flow-bpm-camunda/pom.xml @@ -31,7 +31,7 @@ 7.20.0 1.5.4 1.5.0 - 3.1.12 + 3.1.10 2.6.8 2.15.0 diff --git a/forms-flow-bpm/pom.xml b/forms-flow-bpm/pom.xml index 227c8097a8..3eb21ff142 100644 --- a/forms-flow-bpm/pom.xml +++ b/forms-flow-bpm/pom.xml @@ -26,7 +26,7 @@ 7.20.0 1.5.4 1.5.0 - 3.1.12 + 3.1.10 2.6.8 2.15.0 From 110eae5b99ad85dafd812de6462c3646d9cf7e42 Mon Sep 17 00:00:00 2001 From: abilpraju-aot Date: Wed, 25 Sep 2024 13:55:14 +0530 Subject: [PATCH 05/39] updated simplified relm --- .../keycloak/imports/formsflow-ai-realm.json | 2300 +++------------ .../realm-exports/Group based auth.json | 2556 ++++------------- .../realm-exports/Multi tenant realm.json | 442 ++- 3 files changed, 1247 insertions(+), 4051 deletions(-) diff --git a/forms-flow-idm/keycloak/imports/formsflow-ai-realm.json b/forms-flow-idm/keycloak/imports/formsflow-ai-realm.json index 0b4743f669..86b7bebd51 100644 --- a/forms-flow-idm/keycloak/imports/formsflow-ai-realm.json +++ b/forms-flow-idm/keycloak/imports/formsflow-ai-realm.json @@ -1,287 +1,14 @@ { "id": "forms-flow-ai", "realm": "forms-flow-ai", - "notBefore": 0, - "revokeRefreshToken": false, - "refreshTokenMaxReuse": 0, - "accessTokenLifespan": 300, - "accessTokenLifespanForImplicitFlow": 900, - "ssoSessionIdleTimeout": 1800, - "ssoSessionMaxLifespan": 36000, - "ssoSessionIdleTimeoutRememberMe": 0, - "ssoSessionMaxLifespanRememberMe": 0, - "offlineSessionIdleTimeout": 2592000, - "offlineSessionMaxLifespanEnabled": false, - "offlineSessionMaxLifespan": 5184000, - "clientSessionIdleTimeout": 0, - "clientSessionMaxLifespan": 0, - "clientOfflineSessionIdleTimeout": 0, - "clientOfflineSessionMaxLifespan": 0, - "accessCodeLifespan": 60, - "accessCodeLifespanUserAction": 300, - "accessCodeLifespanLogin": 1800, - "actionTokenGeneratedByAdminLifespan": 43200, - "actionTokenGeneratedByUserLifespan": 300, + "loginTheme": "formsflow", "enabled": true, - "sslRequired": "external", - "registrationAllowed": false, - "registrationEmailAsUsername": false, - "rememberMe": false, - "verifyEmail": false, - "loginWithEmailAllowed": true, - "duplicateEmailsAllowed": false, - "resetPasswordAllowed": false, - "editUsernameAllowed": false, - "bruteForceProtected": false, - "permanentLockout": false, - "maxFailureWaitSeconds": 900, - "minimumQuickLoginWaitSeconds": 60, - "waitIncrementSeconds": 60, - "quickLoginCheckMilliSeconds": 1000, - "maxDeltaTimeSeconds": 43200, - "failureFactor": 30, "roles": { - "realm": [ - { - "name": "offline_access", - "description": "${role_offline-access}", - "composite": false, - "clientRole": false, - "containerId": "forms-flow-ai", - "attributes": {} - }, - { - "name": "default-roles-forms-flow-ai", - "description": "${role_default-roles}", - "composite": true, - "composites": { - "realm": ["offline_access", "uma_authorization"], - "client": { - "account": ["view-profile", "manage-account"] - } - }, - "clientRole": false, - "containerId": "forms-flow-ai", - "attributes": {} - }, - { - "name": "uma_authorization", - "description": "${role_uma_authorization}", - "composite": false, - "clientRole": false, - "containerId": "forms-flow-ai", - "attributes": {} - } - ], "client": { - "realm-management": [ - { - "name": "manage-identity-providers", - "description": "${role_manage-identity-providers}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "query-clients", - "description": "${role_query-clients}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "manage-authorization", - "description": "${role_manage-authorization}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "view-clients", - "description": "${role_view-clients}", - "composite": true, - "composites": { - "client": { - "realm-management": ["query-clients"] - } - }, - "clientRole": true, - "attributes": {} - }, - { - "name": "view-identity-providers", - "description": "${role_view-identity-providers}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "query-realms", - "description": "${role_query-realms}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "view-realm", - "description": "${role_view-realm}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "impersonation", - "description": "${role_impersonation}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "query-groups", - "description": "${role_query-groups}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "view-authorization", - "description": "${role_view-authorization}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "view-users", - "description": "${role_view-users}", - "composite": true, - "composites": { - "client": { - "realm-management": ["query-groups", "query-users"] - } - }, - "clientRole": true, - "attributes": {} - }, - { - "name": "create-client", - "description": "${role_create-client}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "realm-admin", - "description": "${role_realm-admin}", - "composite": true, - "composites": { - "client": { - "realm-management": [ - "manage-identity-providers", - "query-clients", - "manage-authorization", - "view-identity-providers", - "view-clients", - "query-realms", - "view-realm", - "impersonation", - "query-groups", - "view-authorization", - "view-users", - "create-client", - "view-events", - "manage-clients", - "manage-users", - "query-users", - "manage-events", - "manage-realm" - ] - } - }, - "clientRole": true, - "attributes": {} - }, - { - "name": "view-events", - "description": "${role_view-events}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "manage-clients", - "description": "${role_manage-clients}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "manage-events", - "description": "${role_manage-events}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "manage-users", - "description": "${role_manage-users}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "query-users", - "description": "${role_query-users}", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "manage-realm", - "description": "${role_manage-realm}", - "composite": false, - "clientRole": true, - "attributes": {} - } - ], "forms-flow-web": [ { - "name": "admin", - "description": "Administrator Role", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "manage_tasks", - "description": "Can claim and work on tasks", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "view_tasks", - "description": "Access to tasks", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "view_submissions", - "description": "Access to submissions", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "create_submissions", - "description": "Create submissions", - "composite": false, - "clientRole": true, - "attributes": {} - }, - { - "name": "manage_all_filters", - "description": "Manage all filters", + "name": "manage_users", + "description": "Manage Users", "composite": false, "clientRole": true, "attributes": {} @@ -294,8 +21,8 @@ "attributes": {} }, { - "name": "manage_dashboard_authorizations", - "description": "Manage Dashboard Authorization", + "name": "create_designs", + "description": "Create Form, workflow designs", "composite": false, "clientRole": true, "attributes": {} @@ -322,116 +49,71 @@ "attributes": {} }, { - "name": "create_filters", - "description": "Access to create filters", + "name": "view_dashboards", + "description": "Access to dashboards", "composite": false, "clientRole": true, "attributes": {} }, { - "name": "manage_users", - "description": "Manage Users", + "name": "manage_tasks", + "description": "Can claim and work on tasks", "composite": false, "clientRole": true, "attributes": {} }, { - "name": "create_designs", - "description": "Create Form, workflow designs", + "name": "create_submissions", + "description": "Create submissions", "composite": false, "clientRole": true, "attributes": {} }, { - "name": "view_dashboards", - "description": "Access to dashboards", - "composite": false, - "clientRole": true, - "attributes": {} - } - ], - "security-admin-console": [], - "admin-cli": [], - "forms-flow-bpm": [], - "account-console": [], - "broker": [ - { - "name": "read-token", - "description": "${role_read-token}", - "composite": false, - "clientRole": true, - "attributes": {} - } - ], - "forms-flow-analytics": [], - "account": [ - { - "name": "view-applications", - "description": "${role_view-applications}", + "name": "create_filters", + "description": "Access to create filters", "composite": false, "clientRole": true, "attributes": {} }, { - "name": "view-groups", - "description": "${role_view-groups}", + "name": "view_tasks", + "description": "Access to tasks", "composite": false, "clientRole": true, "attributes": {} }, { - "name": "manage-account-links", - "description": "${role_manage-account-links}", + "name": "manage_dashboard_authorizations", + "description": "Manage Dashboard Authorization", "composite": false, "clientRole": true, "attributes": {} }, { - "name": "view-consent", - "description": "${role_view-consent}", + "name": "view_submissions", + "description": "Access to submissions", "composite": false, "clientRole": true, "attributes": {} }, { - "name": "manage-consent", - "description": "${role_manage-consent}", - "composite": true, - "composites": { - "client": { - "account": ["view-consent"] - } - }, - "clientRole": true, - "attributes": {} - }, - { - "name": "manage-account", - "description": "${role_manage-account}", - "composite": true, - "composites": { - "client": { - "account": ["manage-account-links"] - } - }, - "clientRole": true, - "attributes": {} - }, - { - "name": "view-profile", - "description": "${role_view-profile}", + "name": "admin", + "description": "Administrator Role", "composite": false, "clientRole": true, "attributes": {} }, { - "name": "delete-account", - "description": "${role_delete-account}", + "name": "manage_all_filters", + "description": "Manage all filters", "composite": false, "clientRole": true, "attributes": {} } - ] + ], + "forms-flow-bpm": [], + "forms-flow-analytics": [] } }, "groups": [ @@ -439,7 +121,9 @@ "name": "camunda-admin", "path": "/camunda-admin", "subGroups": [], - "attributes": {"description": ["Camunda Administrator Role."]}, + "attributes": { + "description": ["Camunda Administrator Role."] + }, "realmRoles": [], "clientRoles": {} }, @@ -451,25 +135,27 @@ "name": "formsflow-admin", "path": "/formsflow/formsflow-admin", "subGroups": [], - "attributes": {"description": ["Administrator Role."]}, + "attributes": { + "description": ["Administrator Role."] + }, "realmRoles": [], "clientRoles": { "realm-management": [ - "query-clients", - "view-clients", "query-groups", - "view-authorization", "view-users", - "create-client", "manage-clients", + "query-clients", + "view-authorization", + "query-users", "manage-users", - "query-users" + "create-client", + "view-clients" ], "forms-flow-web": [ - "admin", - "manage_dashboard_authorizations", + "manage_users", "manage_roles", - "manage_users" + "manage_dashboard_authorizations", + "admin" ] } }, @@ -477,23 +163,27 @@ "name": "formsflow-client", "path": "/formsflow/formsflow-client", "subGroups": [], - "attributes": {"description": ["Client role to create & view submissions."]}, + "attributes": { + "description": ["Client role to create & view submissions."] + }, "realmRoles": [], "clientRoles": { - "forms-flow-web": ["view_submissions", "create_submissions"] + "forms-flow-web": ["create_submissions", "view_submissions"] } }, { "name": "formsflow-designer", "path": "/formsflow/formsflow-designer", "subGroups": [], - "attributes": {"description": ["Designer role to create forms and workflows."]}, + "attributes": { + "description": ["Designer role to create forms and workflows."] + }, "realmRoles": [], "clientRoles": { "forms-flow-web": [ - "create_designs", + "manage_integrations", "view_designs", - "manage_integrations" + "create_designs" ] } }, @@ -505,39 +195,39 @@ "name": "approver", "path": "/formsflow/formsflow-reviewer/approver", "subGroups": [], - "attributes": {"description": ["Staff role for reviewing tasks."]}, + "attributes": { + "description": ["Staff role for reviewing tasks."] + }, "realmRoles": [], "clientRoles": { - "forms-flow-web": [ - "manage_tasks", - "view_tasks", - "view_filters" - ] + "forms-flow-web": ["manage_tasks", "view_tasks", "view_filters"] } }, { "name": "clerk", "path": "/formsflow/formsflow-reviewer/clerk", "subGroups": [], - "attributes": {"description": ["Staff role for reviewing tasks."]}, + "attributes": { + "description": ["Staff role for reviewing tasks."] + }, "realmRoles": [], "clientRoles": { - "forms-flow-web": [ - "manage_tasks", - "view_tasks", - "view_filters" - ] + "forms-flow-web": ["manage_tasks", "view_tasks", "view_filters"] } } ], - "attributes": {"description": ["Staff role for monitoring submissions and performing tasks."]}, + "attributes": { + "description": [ + "Staff role for monitoring submissions and performing tasks." + ] + }, "realmRoles": [], "clientRoles": { "forms-flow-web": [ + "view_dashboards", "manage_tasks", "view_tasks", "create_filters", - "view_dashboards", "view_submissions", "view_filters" ] @@ -556,81 +246,48 @@ "name": "group1", "path": "/formsflow-analytics/group1", "subGroups": [], - "attributes": {"description": ["Role with dashboard authorization."]}, + "attributes": { + "description": ["Role with dashboard authorization."] + }, "realmRoles": [], - "clientRoles": {"forms-flow-web": ["view_dashboards"]} + "clientRoles": { + "forms-flow-web": ["view_dashboards"] + } }, { "name": "group2", "path": "/formsflow-analytics/group2", "subGroups": [], - "attributes": {"description": ["Role with dashboard authorization."]}, + "attributes": { + "description": ["Role with dashboard authorization."] + }, "realmRoles": [], - "clientRoles": {"forms-flow-web": ["view_dashboards"]} + "clientRoles": { + "forms-flow-web": ["view_dashboards"] + } } ], - "attributes": {"description": ["Role with dashboard authorization."]}, + "attributes": { + "description": ["Role with dashboard authorization."] + }, "realmRoles": [], - "clientRoles": {"forms-flow-web": ["view_dashboards"]} + "clientRoles": { + "forms-flow-web": ["view_dashboards"] + } } ], - "defaultRole": { - "name": "default-roles-forms-flow-ai", - "description": "${role_default-roles}", - "composite": true, - "clientRole": false, - "containerId": "forms-flow-ai" - }, - "defaultGroups": ["/camunda-admin", "/formsflow", "/formsflow-analytics"], - "requiredCredentials": ["password"], - "otpPolicyType": "totp", - "otpPolicyAlgorithm": "HmacSHA1", - "otpPolicyInitialCounter": 0, - "otpPolicyDigits": 6, - "otpPolicyLookAheadWindow": 1, - "otpPolicyPeriod": 30, - "otpPolicyCodeReusable": false, - "otpSupportedApplications": [ - "totpAppFreeOTPName", - "totpAppGoogleName", - "totpAppMicrosoftAuthenticatorName" - ], - "localizationTexts": {}, - "webAuthnPolicyRpEntityName": "keycloak", - "webAuthnPolicySignatureAlgorithms": ["ES256"], - "webAuthnPolicyRpId": "", - "webAuthnPolicyAttestationConveyancePreference": "not specified", - "webAuthnPolicyAuthenticatorAttachment": "not specified", - "webAuthnPolicyRequireResidentKey": "not specified", - "webAuthnPolicyUserVerificationRequirement": "not specified", - "webAuthnPolicyCreateTimeout": 0, - "webAuthnPolicyAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyAcceptableAaguids": [], - "webAuthnPolicyExtraOrigins": [], - "webAuthnPolicyPasswordlessRpEntityName": "keycloak", - "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"], - "webAuthnPolicyPasswordlessRpId": "", - "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", - "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", - "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", - "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", - "webAuthnPolicyPasswordlessCreateTimeout": 0, - "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyPasswordlessAcceptableAaguids": [], - "webAuthnPolicyPasswordlessExtraOrigins": [], "users": [ { - "createdTimestamp": 1718023583532, "username": "formsflow-admin", - "enabled": true, - "totp": false, - "emailVerified": false, "firstName": "ff", "lastName": "admin", "email": "formsflow-admin@aot-technologies.com", + "emailVerified": false, + "createdTimestamp": 1718023583532, + "enabled": true, + "totp": false, "credentials": [ { - "id": "d4d8336d-ba33-47f7-9a7e-91269f8b4cc9", "type": "password", "userLabel": "My password", "createdDate": 1718087465595, @@ -649,168 +306,24 @@ ] }, { - "createdTimestamp": 1625009614956, - "username": "formsflow-approver", + "username": "service-account-forms-flow-bpm", + "emailVerified": false, + "createdTimestamp": 1621585233480, "enabled": true, "totp": false, - "emailVerified": false, - "firstName": "Approver", - "lastName": "FFA", - "email": "formsflow-approver@aot-technologies.com", - "credentials": [ - { - "id": "c6aac3fc-3483-4afd-b46f-223641f3fe56", - "type": "password", - "userLabel": "My password", - "createdDate": 1718087451516, - "secretData": "{\"value\":\"vfZT65WYqEjeUdOBL61DMke/qy6YE1zJFKyoMNdxvUo=\",\"salt\":\"+7nUoa1MU7YgyyKI4x+/FA==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": ["UPDATE_PASSWORD"], - "notBefore": 0, - "groups": [ - "/formsflow/formsflow-reviewer/approver", - "/camunda-admin", - "/formsflow-analytics/group1" - ] - }, - { - "createdTimestamp": 1625009564217, - "username": "formsflow-clerk", - "enabled": true, - "totp": false, - "emailVerified": false, - "firstName": "Clerk", - "lastName": "FFA", - "email": "formsflow-clerk@aot-technologies.com", - "credentials": [ - { - "type": "password", - "userLabel": "My password", - "createdDate": 1718087400076, - "secretData": "{\"value\":\"XU5BFgu3R34Lj3jmJRnNddDUTusSjPATqzEAzObH8Qs=\",\"salt\":\"kJazPy8a3ds+akjIAj2v5w==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": ["UPDATE_PASSWORD"], - "notBefore": 0, - "groups": [ - "/camunda-admin", - "/formsflow/formsflow-reviewer/clerk", - "/formsflow-analytics/group2" - ] - }, - { - "createdTimestamp": 1621862607660, - "username": "formsflow-client", - "enabled": true, - "totp": false, - "emailVerified": false, - "firstName": "Client", - "lastName": "FFA", - "email": "formsflow-client@example.com", - "credentials": [ - { - "type": "password", - "userLabel": "My password", - "createdDate": 1718087389853, - "secretData": "{\"value\":\"Fehk9NWRH+UjlGacI2YqEoIr3u87E0QS5Wt1S4HNiGk=\",\"salt\":\"uTEVXddLwHNPWPhCUgqXbw==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": ["UPDATE_PASSWORD"], - "realmRoles": ["offline_access", "uma_authorization"], - "clientRoles": { - "account": ["view-profile", "manage-account"] - }, - "notBefore": 0, - "groups": ["/camunda-admin", "/formsflow/formsflow-client"] - }, - { - "createdTimestamp": 1621862546931, - "username": "formsflow-designer", - "enabled": true, - "totp": false, - "emailVerified": false, - "firstName": "Designer", - "lastName": "FFA", - "email": "formsflow-designer@example.com", - "credentials": [ - { - "type": "password", - "userLabel": "My password", - "createdDate": 1718087380963, - "secretData": "{\"value\":\"X3Q2p9qUom86nW5MzPcVWSqRqOA79aGoWiXxPNa5CXg=\",\"salt\":\"axfCJmXlZ+fT5weWx7Ug7A==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": ["UPDATE_PASSWORD"], - "realmRoles": ["offline_access", "uma_authorization"], - "clientRoles": { - "forms-flow-web": ["create_designs", "view_designs"], - "account": ["view-profile", "manage-account"] - }, - "notBefore": 0, - "groups": ["/camunda-admin", "/formsflow/formsflow-designer"] - }, - { - "createdTimestamp": 1621862578318, - "username": "formsflow-reviewer", - "enabled": true, - "totp": false, - "emailVerified": false, - "firstName": "Reviewer", - "lastName": "FFA", - "email": "formsflow-reviewer@example.com", - "attributes": { - "locale": ["en"] - }, - "credentials": [ - { - "type": "password", - "userLabel": "My password", - "createdDate": 1718087367262, - "secretData": "{\"value\":\"1/Cx4BVLFu8pXA1pAwfLj/Ph01zlcTiWZS8dPfCavJA=\",\"salt\":\"XHQ4JeY44l/QChf27jiYNw==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": ["UPDATE_PASSWORD"], - "realmRoles": ["offline_access", "uma_authorization"], - "clientRoles": { - "account": ["view-profile", "manage-account"] - }, - "notBefore": 0, - "groups": [ - "/camunda-admin", - "/formsflow/formsflow-reviewer", - "/formsflow-analytics/group1" - ] - }, - { - "createdTimestamp": 1621585233480, - "username": "service-account-forms-flow-bpm", - "enabled": true, - "totp": false, - "emailVerified": false, - "serviceAccountClientId": "forms-flow-bpm", - "credentials": [], + "serviceAccountClientId": "forms-flow-bpm", + "credentials": [], "disableableCredentialTypes": [], "requiredActions": [], "realmRoles": ["offline_access", "uma_authorization"], "clientRoles": { "realm-management": [ + "query-users", "query-groups", "view-users", - "realm-admin", - "manage-clients", "manage-users", - "query-users" + "manage-clients", + "realm-admin" ], "account": ["view-profile", "manage-account"] }, @@ -818,172 +331,7 @@ "groups": ["/camunda-admin"] } ], - "scopeMappings": [ - { - "clientScope": "offline_access", - "roles": ["offline_access"] - } - ], - "clientScopeMappings": { - "account": [ - { - "client": "account-console", - "roles": ["manage-account", "view-groups"] - } - ] - }, "clients": [ - { - "clientId": "account", - "name": "${client_account}", - "rootUrl": "${authBaseUrl}", - "baseUrl": "/realms/forms-flow-ai/account/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": ["/realms/forms-flow-ai/account/*"], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "post.logout.redirect.uris": "+" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "roles", "profile", "email"], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "clientId": "account-console", - "name": "${client_account-console}", - "rootUrl": "${authBaseUrl}", - "baseUrl": "/realms/forms-flow-ai/account/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": ["/realms/forms-flow-ai/account/*"], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "post.logout.redirect.uris": "+", - "pkce.code.challenge.method": "S256" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "26fa11e0-b0c1-496d-83bb-f3c557fd0dce", - "name": "audience resolve", - "protocol": "openid-connect", - "protocolMapper": "oidc-audience-resolve-mapper", - "consentRequired": false, - "config": {} - } - ], - "defaultClientScopes": ["web-origins", "roles", "profile", "email"], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "clientId": "admin-cli", - "name": "${client_admin-cli}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": false, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "post.logout.redirect.uris": "+" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "roles", "profile", "email"], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "clientId": "broker", - "name": "${client_broker}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "post.logout.redirect.uris": "+" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "roles", "profile", "email"], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, { "clientId": "forms-flow-analytics", "description": "Redash-Analytics", @@ -992,7 +340,6 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "secret": "**********", "redirectUris": ["http://localhost:7000/*", "*"], "webOrigins": [], "notBefore": 0, @@ -1030,25 +377,25 @@ "nodeReRegistrationTimeout": -1, "protocolMappers": [ { - "name": "X500 givenName", + "name": "X500 surname", "protocol": "saml", "protocolMapper": "saml-user-property-mapper", "consentRequired": false, "config": { - "user.attribute": "firstName", - "friendly.name": "FirstName", - "attribute.name": "urn:oid:2.5.4.42" + "user.attribute": "lastName", + "friendly.name": "LastName", + "attribute.name": "urn:oid:2.5.4.4" } }, { - "name": "X500 surname", + "name": "X500 givenName", "protocol": "saml", "protocolMapper": "saml-user-property-mapper", "consentRequired": false, "config": { - "user.attribute": "lastName", - "friendly.name": "LastName", - "attribute.name": "urn:oid:2.5.4.4" + "user.attribute": "firstName", + "friendly.name": "FirstName", + "attribute.name": "urn:oid:2.5.4.42" } } ], @@ -1095,6 +442,18 @@ "fullScopeAllowed": true, "nodeReRegistrationTimeout": -1, "protocolMappers": [ + { + "name": "formsflow-web-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "forms-flow-web", + "id.token.claim": "false", + "access.token.claim": "true", + "userinfo.token.claim": "false" + } + }, { "name": "Client Host", "protocol": "openid-connect", @@ -1102,76 +461,64 @@ "consentRequired": false, "config": { "user.session.note": "clientHost", - "userinfo.token.claim": "true", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "clientHost", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "Client IP Address", + "name": "username", "protocol": "openid-connect", - "protocolMapper": "oidc-usersessionmodel-note-mapper", + "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "user.session.note": "clientAddress", - "userinfo.token.claim": "true", + "user.attribute": "username", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "clientAddress", - "jsonType.label": "String" + "claim.name": "preferred_username", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "formsflow-web-mapper", + "name": "Client IP Address", "protocol": "openid-connect", - "protocolMapper": "oidc-audience-mapper", + "protocolMapper": "oidc-usersessionmodel-note-mapper", "consentRequired": false, "config": { - "included.client.audience": "forms-flow-web", - "id.token.claim": "false", - "access.token.claim": "true", - "userinfo.token.claim": "false" - } - }, - { - "name": "camunda-rest-api", - "protocol": "openid-connect", - "protocolMapper": "oidc-audience-mapper", - "consentRequired": false, - "config": { - "id.token.claim": "false", + "user.session.note": "clientAddress", + "id.token.claim": "true", "access.token.claim": "true", - "included.custom.audience": "camunda-rest-api", - "userinfo.token.claim": "false" + "claim.name": "clientAddress", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "username", + "name": "groups", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", + "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", + "full.path": "true", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" + "claim.name": "groups", + "userinfo.token.claim": "true" } }, { - "name": "groups", + "name": "camunda-rest-api", "protocol": "openid-connect", - "protocolMapper": "oidc-group-membership-mapper", + "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { - "full.path": "true", - "id.token.claim": "true", + "id.token.claim": "false", "access.token.claim": "true", - "claim.name": "groups", - "userinfo.token.claim": "true" + "included.custom.audience": "camunda-rest-api", + "userinfo.token.claim": "false" } }, { @@ -1181,11 +528,11 @@ "consentRequired": false, "config": { "user.session.note": "clientId", - "userinfo.token.claim": "true", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "clientId", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } } ], @@ -1193,6 +540,7 @@ "web-origins", "roles", "profile", + "basic", "camunda-rest-api", "email" ], @@ -1210,7 +558,6 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "secret": "**********", "redirectUris": ["http://localhost:3000/*", "*"], "webOrigins": ["*"], "notBefore": 0, @@ -1244,16 +591,15 @@ "nodeReRegistrationTimeout": -1, "protocolMappers": [ { - "name": "groups", + "name": "camunda-rest-api", "protocol": "openid-connect", - "protocolMapper": "oidc-group-membership-mapper", + "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { - "full.path": "true", - "id.token.claim": "true", + "id.token.claim": "false", "access.token.claim": "true", - "claim.name": "groups", - "userinfo.token.claim": "true" + "included.custom.audience": "camunda-rest-api", + "userinfo.token.claim": "false" } }, { @@ -1269,30 +615,31 @@ } }, { - "name": "dashboard-mapper", + "name": "groups", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { - "aggregate.attrs": "true", - "userinfo.token.claim": "true", - "multivalued": "true", - "user.attribute": "dashboards", + "full.path": "true", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "dashboards" + "claim.name": "groups", + "userinfo.token.claim": "true" } }, { - "name": "camunda-rest-api", + "name": "dashboard-mapper", "protocol": "openid-connect", - "protocolMapper": "oidc-audience-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "id.token.claim": "false", + "aggregate.attrs": "true", + "userinfo.token.claim": "true", + "multivalued": "true", + "user.attribute": "dashboards", + "id.token.claim": "true", "access.token.claim": "true", - "included.custom.audience": "camunda-rest-api", - "userinfo.token.claim": "false" + "claim.name": "dashboards" } } ], @@ -1300,6 +647,7 @@ "web-origins", "roles", "profile", + "basic", "camunda-rest-api", "email" ], @@ -1309,95 +657,62 @@ "offline_access", "microprofile-jwt" ] - }, + } + ], + "clientScopes": [ { - "clientId": "realm-management", - "name": "${client_realm-management}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": true, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, + "name": "address", + "description": "OpenID Connect built-in scope: address", "protocol": "openid-connect", "attributes": { - "post.logout.redirect.uris": "+" + "include.in.token.scope": "true", + "consent.screen.text": "${addressScopeConsentText}", + "display.on.consent.screen": "true" }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "roles", "profile", "email"], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" + "protocolMappers": [ + { + "id": "5d9b4833-6472-4de5-8a32-ac8111d77aa1", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } ] }, { - "name": "${client_security-admin-console}", - "rootUrl": "${authAdminUrl}", - "baseUrl": "/admin/forms-flow-ai/console/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": ["/admin/forms-flow-ai/console/*"], - "webOrigins": ["+"], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", "protocol": "openid-connect", "attributes": { - "post.logout.redirect.uris": "+", - "pkce.code.challenge.method": "S256" + "include.in.token.scope": "false", + "display.on.consent.screen": "false" }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, "protocolMappers": [ { - "name": "locale", + "name": "acr loa level", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-acr-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "locale", "id.token.claim": "true", + "introspection.token.claim": "true", "access.token.claim": "true", - "claim.name": "locale", - "jsonType.label": "String" + "userinfo.token.claim": "true" } } - ], - "defaultClientScopes": ["web-origins", "roles", "profile", "email"], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" ] - } - ], - "clientScopes": [ + }, { "name": "microprofile-jwt", "description": "Microprofile - JWT built-in scope", @@ -1413,12 +728,12 @@ "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "username", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "upn", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1439,58 +754,29 @@ ] }, { - "name": "acr", - "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", "protocol": "openid-connect", "attributes": { "include.in.token.scope": "false", - "display.on.consent.screen": "false" + "consent.screen.text": "${rolesScopeConsentText}", + "display.on.consent.screen": "true" }, "protocolMappers": [ { - "id": "4d629437-3940-47c4-9180-d88b4c1126f6", - "name": "acr loa level", + "name": "Role", "protocol": "openid-connect", - "protocolMapper": "oidc-acr-mapper", + "protocolMapper": "oidc-usermodel-client-role-mapper", "consentRequired": false, "config": { "id.token.claim": "true", - "introspection.token.claim": "true", - "access.token.claim": "true" + "access.token.claim": "true", + "claim.name": "role", + "usermodel.clientRoleMapping.clientId": "forms-flow-web", + "multivalued": "true", + "userinfo.token.claim": "true" } - } - ] - }, - { - "name": "web-origins", - "description": "OpenID Connect scope for add allowed web origins to the access token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "false", - "consent.screen.text": "" - }, - "protocolMappers": [ - { - "id": "ae97ed8c-ec33-4a7e-ab86-7c122ef8bbc3", - "name": "allowed web origins", - "protocol": "openid-connect", - "protocolMapper": "oidc-allowed-origins-mapper", - "consentRequired": false, - "config": {} - } - ] - }, - { - "name": "roles", - "description": "OpenID Connect scope for add user roles to the access token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "true", - "consent.screen.text": "${rolesScopeConsentText}" - }, - "protocolMappers": [ + }, { "name": "client roles", "protocol": "openid-connect", @@ -1517,20 +803,6 @@ "multivalued": "true" } }, - { - "name": "Role", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-client-role-mapper", - "consentRequired": false, - "config": { - "multivalued": "true", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "role", - "usermodel.clientRoleMapping.clientId": "forms-flow-web" - } - }, { "name": "audience resolve", "protocol": "openid-connect", @@ -1541,192 +813,272 @@ ] }, { - "name": "profile", - "description": "OpenID Connect built-in scope: profile", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", "protocol": "openid-connect", "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${profileScopeConsentText}" + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "name": "basic", + "description": "OpenID Connect scope for add all basic claims to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" }, "protocolMappers": [ { - "name": "zoneinfo", + "name": "sub", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-sub-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "zoneinfo", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "zoneinfo", - "jsonType.label": "String" + "introspection.token.claim": "true", + "access.token.claim": "true" } }, { - "name": "picture", + "name": "auth_time", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-usersessionmodel-note-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "picture", + "user.session.note": "AUTH_TIME", "id.token.claim": "true", + "introspection.token.claim": "true", "access.token.claim": "true", - "claim.name": "picture", - "jsonType.label": "String" + "claim.name": "auth_time", + "jsonType.label": "long" } - }, + } + ] + }, + { + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${emailScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ { - "name": "username", + "name": "email", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", + "user.attribute": "email", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" + "claim.name": "email", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "website", + "name": "email verified", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "website", + "user.attribute": "emailVerified", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "website", - "jsonType.label": "String" + "claim.name": "email_verified", + "jsonType.label": "boolean", + "userinfo.token.claim": "true" } - }, + } + ] + }, + { + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ { - "name": "locale", + "name": "allowed web origins", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "locale", - "id.token.claim": "true", + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "name": "camunda-rest-api", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "name": "camunda-rest-api", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "false", "access.token.claim": "true", - "claim.name": "locale", - "jsonType.label": "String" + "included.custom.audience": "camunda-rest-api", + "userinfo.token.claim": "false" } - }, + } + ] + }, + { + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${profileScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ { - "name": "full name", + "name": "zoneinfo", "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { + "user.attribute": "zoneinfo", "id.token.claim": "true", "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String", "userinfo.token.claim": "true" } }, { - "name": "middle name", + "name": "gender", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "middleName", + "user.attribute": "gender", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "middle_name", - "jsonType.label": "String" + "claim.name": "gender", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "updated at", + "name": "picture", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "updatedAt", + "user.attribute": "picture", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "updated_at", - "jsonType.label": "String" + "claim.name": "picture", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "given name", + "name": "username", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", + "user.attribute": "username", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" + "claim.name": "preferred_username", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "nickname", + "name": "website", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "nickname", + "user.attribute": "website", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "nickname", - "jsonType.label": "String" + "claim.name": "website", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "birthdate", + "name": "locale", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "birthdate", + "user.attribute": "locale", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "birthdate", - "jsonType.label": "String" + "claim.name": "locale", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "family name", + "name": "middle name", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", + "user.attribute": "middleName", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" + "claim.name": "middle_name", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "gender", + "name": "nickname", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "gender", + "user.attribute": "nickname", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "gender", - "jsonType.label": "String" + "claim.name": "nickname", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1735,134 +1087,79 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "profile", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "profile", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } - } - ] - }, - { - "name": "offline_access", - "description": "OpenID Connect built-in scope: offline_access", - "protocol": "openid-connect", - "attributes": { - "consent.screen.text": "${offlineAccessScopeConsentText}", - "display.on.consent.screen": "true" - } - }, - { - "name": "role_list", - "description": "SAML role list", - "protocol": "saml", - "attributes": { - "consent.screen.text": "${samlRoleListScopeConsentText}", - "display.on.consent.screen": "true" - }, - "protocolMappers": [ + }, { - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String", + "userinfo.token.claim": "true" } - } - ] - }, - { - "name": "email", - "description": "OpenID Connect built-in scope: email", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${emailScopeConsentText}" - }, - "protocolMappers": [ + }, { - "name": "email verified", + "name": "full name", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", + "protocolMapper": "oidc-full-name-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "emailVerified", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "email_verified", - "jsonType.label": "boolean" + "userinfo.token.claim": "true" } }, { - "name": "email", + "name": "updated at", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", + "user.attribute": "updatedAt", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" + "claim.name": "updated_at", + "jsonType.label": "String", + "userinfo.token.claim": "true" } - } - ] - }, - { - "name": "address", - "description": "OpenID Connect built-in scope: address", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${addressScopeConsentText}" - }, - "protocolMappers": [ + }, { - "name": "address", + "name": "family name", "protocol": "openid-connect", - "protocolMapper": "oidc-address-mapper", + "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "user.attribute.formatted": "formatted", - "user.attribute.country": "country", - "user.attribute.postal_code": "postal_code", - "userinfo.token.claim": "true", - "user.attribute.street": "street", + "user.attribute": "lastName", "id.token.claim": "true", - "user.attribute.region": "region", "access.token.claim": "true", - "user.attribute.locality": "locality" + "claim.name": "family_name", + "jsonType.label": "String", + "userinfo.token.claim": "true" } - } - ] - }, - { - "name": "camunda-rest-api", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true" - }, - "protocolMappers": [ + }, { - "name": "camunda-rest-api", + "name": "given name", "protocol": "openid-connect", - "protocolMapper": "oidc-audience-mapper", + "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "id.token.claim": "false", + "user.attribute": "firstName", + "id.token.claim": "true", "access.token.claim": "true", - "included.custom.audience": "camunda-rest-api", - "userinfo.token.claim": "false" + "claim.name": "given_name", + "jsonType.label": "String", + "userinfo.token.claim": "true" } } ] @@ -1873,36 +1170,36 @@ "protocol": "openid-connect", "attributes": { "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${phoneScopeConsentText}" + "consent.screen.text": "${phoneScopeConsentText}", + "display.on.consent.screen": "true" }, "protocolMappers": [ { - "name": "phone number verified", + "name": "phone number", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "phoneNumberVerified", + "user.attribute": "phoneNumber", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "phone_number_verified", - "jsonType.label": "boolean" + "claim.name": "phone_number", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "phone number", + "name": "phone number verified", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "phoneNumber", + "user.attribute": "phoneNumberVerified", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "phone_number", - "jsonType.label": "String" + "claim.name": "phone_number_verified", + "jsonType.label": "boolean", + "userinfo.token.claim": "true" } } ] @@ -1915,770 +1212,13 @@ "roles", "web-origins", "camunda-rest-api", - "acr" + "acr", + "basic" ], "defaultOptionalClientScopes": [ "offline_access", "address", "phone", "microprofile-jwt" - ], - "browserSecurityHeaders": { - "contentSecurityPolicyReportOnly": "", - "xContentTypeOptions": "nosniff", - "referrerPolicy": "no-referrer", - "xRobotsTag": "none", - "xFrameOptions": "SAMEORIGIN", - "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", - "xXSSProtection": "1; mode=block", - "strictTransportSecurity": "max-age=31536000; includeSubDomains" - }, - "smtpServer": {}, - "loginTheme": "formsflow", - "accountTheme": "", - "adminTheme": "", - "emailTheme": "", - "eventsEnabled": false, - "eventsListeners": ["jboss-logging"], - "enabledEventTypes": [], - "adminEventsEnabled": false, - "adminEventsDetailsEnabled": false, - "identityProviders": [], - "identityProviderMappers": [], - "components": { - "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ - { - "id": "d0d1c5d3-3dcb-44bb-982b-d426cd1486d7", - "name": "Max Clients Limit", - "providerId": "max-clients", - "subType": "anonymous", - "subComponents": {}, - "config": { - "max-clients": ["200"] - } - }, - { - "name": "Allowed Client Scopes", - "providerId": "allowed-client-templates", - "subType": "authenticated", - "subComponents": {}, - "config": { - "allow-default-scopes": ["true"] - } - }, - { - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "anonymous", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "oidc-sha256-pairwise-sub-mapper", - "oidc-full-name-mapper", - "saml-role-list-mapper", - "oidc-usermodel-property-mapper", - "saml-user-attribute-mapper", - "oidc-usermodel-attribute-mapper", - "oidc-address-mapper", - "saml-user-property-mapper" - ] - } - }, - { - "name": "Full Scope Disabled", - "providerId": "scope", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "name": "Consent Required", - "providerId": "consent-required", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "name": "Allowed Client Scopes", - "providerId": "allowed-client-templates", - "subType": "anonymous", - "subComponents": {}, - "config": { - "allow-default-scopes": ["true"] - } - }, - { - "name": "Trusted Hosts", - "providerId": "trusted-hosts", - "subType": "anonymous", - "subComponents": {}, - "config": { - "host-sending-registration-request-must-match": ["true"], - "client-uris-must-match": ["true"] - } - }, - { - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "authenticated", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "oidc-sha256-pairwise-sub-mapper", - "saml-user-property-mapper", - "saml-user-attribute-mapper", - "oidc-usermodel-attribute-mapper", - "oidc-usermodel-property-mapper", - "oidc-full-name-mapper", - "oidc-address-mapper", - "saml-role-list-mapper" - ] - } - } - ], - "org.keycloak.keys.KeyProvider": [ - { - "id": "49016060-904b-4d79-9d1b-57cb5d6a1a52", - "name": "aes-generated", - "providerId": "aes-generated", - "subComponents": {}, - "config": { - "kid": ["70affbd9-d904-4dcc-8838-70beb8f73aa3"], - "secret": ["62t32DLzCqO31BzHJ7KKgQ"], - "priority": ["100"] - } - }, - { - "name": "rsa-generated", - "providerId": "rsa-generated", - "subComponents": {}, - "config": { - "privateKey": [ - "MIIEpAIBAAKCAQEA6bYyvyeConvAkWBbiXypa4UWFMLJhiutcm2C3GElTSQjsoDorz7/Kj3v5svKe2/NXly6lvNcbV63Ot5dUfr2LvNgetfMH8bXf96kZeOyZxPFGhPvI4IjQWVcxqObMsD3ZtYOLM1Q1YoNv20Rs8UhZnqwYrqsq+6eGEaN/UAMN4+O5T1ZxStKNSDIPETqZJOvozKhgY7uYANxHSRDLC/FYS+y3NaERNDeu1eDN8Qi8Z+p1BBZo8A1A5rz0puzrK3b2K/W4bPayGc7EHIA8LiGfiJXzhX2/V57dzW1qMy6OjUdt63UjPRvxNcgekbP1DOpdLQkBlCO8F6DxNY4VIDXGQIDAQABAoIBAAKpMTpi7pup/AnLsTnHV73NJfC2PQl63X6EMlgO/8KOlZzL10UI2xU136be4snKqT8YoC6QIRkaWcy2SRViCEPa6oTLiIaYws2xERRwkLtCtYeepXuKg5s+1l0T8h7ppTIgYB3wzSmj6OnT3z6O5oVJAqbFHaqBd38AQqVDrD/vrBccUZ0HnDXWT4gEyTNsUAoZ8SIFHK1ab4C2cse+FfszyrxxQRmDyckJ9/0c7YObbuk1NDm4KLZyyVhZ2QB20RqwZj3C3YlOs0So9BrkooZI+PmCmq4ikZGJRwVqAdH3t5HpaXxnt9Ytq5+e+gLyy+QO5Audd7vblAVi4eJeJ5kCgYEA+eQaSBYGLAUfUm3akVxLZF0UfYxrOn/tUA56anNbyhDHkWsJDGHMQWtXMhZruevVGTCNrMt6rAtCcHcObwR4uXiK/rxrYMGNqV/Y6jZPwkaTwbc6KJyyoNynBMRJBXlNUAtikq7bB72VC2BIThw9L/x95dkdEdoIOpI2aJZa8G0CgYEA72zXHOLufBjXE69Qe9dUqHOVSGUhZ6ou4Xpo1YLzwTvI9Z7dWaj134fHsrXfmZ1W4Z2yGephZ9NK6uw2D23GAgJO9Bv7sdVdOzkDaXb3sL7n3w7wyJPR+yk4U+hV/sEtuRnNUoNIYiyYUodKSJAsZO8qmX4qRvB9jcUnz1BYzd0CgYB3CJMEV4llGqOK7k05BM/c5QHHtEW3vgxbICpr3ruQi2GlAWlz/nn/h5QnhcqW66G8uAYkk2DB5zMkw3GarHSdVLBRG4vCsTA7yC5Zkrl3f4sOsasAgXQNcE4W1TE7bEbJpEK2QJiRHVuL6bdHjegPnaSTAlL4l/VngRJty4FSGQKBgQDQpXDw43IhAySrKuRgh85m3hMB+9+Yj147cIlbR7tUcd027zLGp89c9N66hnRNUigchWhn3TD1YdSv1RlnaKpdpdVYNeqplt6gdZbbmalQhY1bfZGnueoLjmhf9uC6GW7XF/0uIuo5Y9N6WBwMEFgfvazKBoChQYKowV9n8wTwoQKBgQC6gjg7X2bCLwFQS6yY/smv3PrG9moB5PxyDcFp8DJty23jSx4vuNfQDB3P97Z/rZW8S/QuSsqLN37vb1gwSR+BiI5A6+8dVSL8m8XMDOOA5IVschphECxmbucoefIWEBiAquNsVz9+4AmWDsMJ81I35mc/SBZf5eC84UcDZXN/kg==" - ], - "certificate": [ - "MIICqTCCAZECBgGPw87+UDANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1mb3Jtcy1mbG93LWFpMB4XDTI0MDUyOTEwMDI1M1oXDTM0MDUyOTEwMDQzM1owGDEWMBQGA1UEAwwNZm9ybXMtZmxvdy1haTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOm2Mr8ngqJ7wJFgW4l8qWuFFhTCyYYrrXJtgtxhJU0kI7KA6K8+/yo97+bLyntvzV5cupbzXG1etzreXVH69i7zYHrXzB/G13/epGXjsmcTxRoT7yOCI0FlXMajmzLA92bWDizNUNWKDb9tEbPFIWZ6sGK6rKvunhhGjf1ADDePjuU9WcUrSjUgyDxE6mSTr6MyoYGO7mADcR0kQywvxWEvstzWhETQ3rtXgzfEIvGfqdQQWaPANQOa89Kbs6yt29iv1uGz2shnOxByAPC4hn4iV84V9v1ee3c1tajMujo1Hbet1Iz0b8TXIHpGz9QzqXS0JAZQjvBeg8TWOFSA1xkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAIXZ1wxy9msKuXQRzLXZ6obmymetOliWjAU7rQpXktUDj6X33JLNxYr6MFFd4jb5E6nKPXmZEXRMq2Q/iOAFun8DUIqVd1ZoYUvYMVsSgrafT25A0v0kvuENBTI/Jz9no+2z3mHw34s4BbhZRxO0lue7ms8OFs+2lR2jSLTzlxzlY/vG9fCG7t3Q/WAm7kfLa7+mDOn9ELHG0T+xZtmhuZ2QVSNmriFK6KEBpXA7GdUJBXWKmuQeBBqFDE7Jo+mt2FSK8JpKCw4MP1IVf5Ft/OU2DFJkI4GTJagQjWYcQ1IIs0fFy3TtQv3ToJHfrr81sDFX6Ua3pEnRwfXhz3a+k2Q==" - ], - "priority": ["100"] - } - }, - { - "name": "hmac-generated", - "providerId": "hmac-generated", - "subComponents": {}, - "config": { - "kid": ["927e4878-e36e-45b3-a979-dc53a7ee0fde"], - "secret": [ - "tOFkTA2NWJ9cZhqAyf1dNar3zKTuKMtMY7_5d0ED7md22CZqNMAjd3MWwawjk1FR6EVL-KkiTJ7g90Y5Jp094A" - ], - "priority": ["100"], - "algorithm": ["HS256"] - } - } - ] - }, - "internationalizationEnabled": false, - "supportedLocales": [], - "authenticationFlows": [ - { - "alias": "Account verification options", - "description": "Method with which to verity the existing account", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-email-verification", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Verify Existing Account by Re-authentication", - "userSetupAllowed": false - } - ] - }, - { - "alias": "Browser - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-otp-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "alias": "Direct Grant - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "direct-grant-validate-otp", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "alias": "First broker login - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-otp-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "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", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-confirm-link", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Account verification options", - "userSetupAllowed": false - } - ] - }, - { - "alias": "Reset - Conditional OTP", - "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-otp", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "alias": "User creation or linking", - "description": "Flow for the existing/non-existing user alternatives", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticatorConfig": "create unique user config", - "authenticator": "idp-create-user-if-unique", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Handle Existing Account", - "userSetupAllowed": false - } - ] - }, - { - "alias": "Verify Existing Account by Re-authentication", - "description": "Reauthentication of existing account", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-username-password-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "First broker login - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "alias": "browser", - "description": "browser based authentication", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-cookie", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-spnego", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "identity-provider-redirector", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 25, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 30, - "autheticatorFlow": true, - "flowAlias": "forms", - "userSetupAllowed": false - } - ] - }, - { - "alias": "clients", - "description": "Base authentication for clients", - "providerId": "client-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "client-secret", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-jwt", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-secret-jwt", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 30, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-x509", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 40, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "alias": "direct grant", - "description": "OpenID Connect Resource Owner Grant", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "direct-grant-validate-username", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "direct-grant-validate-password", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 30, - "autheticatorFlow": true, - "flowAlias": "Direct Grant - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "alias": "docker auth", - "description": "Used by Docker clients to authenticate against the IDP", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "docker-http-basic-authenticator", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "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", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticatorConfig": "review profile config", - "authenticator": "idp-review-profile", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "User creation or linking", - "userSetupAllowed": false - } - ] - }, - { - "alias": "forms", - "description": "Username, password, otp and other auth forms.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-username-password-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Browser - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "alias": "registration", - "description": "registration flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-page-form", - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": true, - "flowAlias": "registration form", - "userSetupAllowed": false - } - ] - }, - { - "alias": "registration form", - "description": "registration form", - "providerId": "form-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-user-creation", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "registration-password-action", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 50, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "registration-recaptcha-action", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 60, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "alias": "reset credentials", - "description": "Reset credentials for a user if they forgot their password or something", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "reset-credentials-choose-user", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-credential-email", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-password", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 30, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 40, - "autheticatorFlow": true, - "flowAlias": "Reset - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "alias": "saml ecp", - "description": "SAML ECP Profile Authentication Flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "http-basic-authenticator", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - } - ], - "authenticatorConfig": [ - { - "alias": "create unique user config", - "config": { - "require.password.update.after.registration": "false" - } - }, - { - "alias": "review profile config", - "config": { - "update.profile.on.first.login": "missing" - } - } - ], - "requiredActions": [ - { - "alias": "CONFIGURE_TOTP", - "name": "Configure OTP", - "providerId": "CONFIGURE_TOTP", - "enabled": true, - "defaultAction": false, - "priority": 10, - "config": {} - }, - { - "alias": "TERMS_AND_CONDITIONS", - "name": "Terms and Conditions", - "providerId": "TERMS_AND_CONDITIONS", - "enabled": false, - "defaultAction": false, - "priority": 20, - "config": {} - }, - { - "alias": "UPDATE_PASSWORD", - "name": "Update Password", - "providerId": "UPDATE_PASSWORD", - "enabled": true, - "defaultAction": false, - "priority": 30, - "config": {} - }, - { - "alias": "UPDATE_PROFILE", - "name": "Update Profile", - "providerId": "UPDATE_PROFILE", - "enabled": true, - "defaultAction": false, - "priority": 40, - "config": {} - }, - { - "alias": "VERIFY_EMAIL", - "name": "Verify Email", - "providerId": "VERIFY_EMAIL", - "enabled": true, - "defaultAction": false, - "priority": 50, - "config": {} - }, - { - "alias": "delete_account", - "name": "Delete Account", - "providerId": "delete_account", - "enabled": false, - "defaultAction": false, - "priority": 60, - "config": {} - }, - { - "alias": "update_user_locale", - "name": "Update User Locale", - "providerId": "update_user_locale", - "enabled": true, - "defaultAction": false, - "priority": 1000, - "config": {} - } - ], - "browserFlow": "browser", - "registrationFlow": "registration", - "directGrantFlow": "direct grant", - "resetCredentialsFlow": "reset credentials", - "clientAuthenticationFlow": "clients", - "dockerAuthenticationFlow": "docker auth", - "attributes": { - "cibaBackchannelTokenDeliveryMode": "poll", - "cibaExpiresIn": "120", - "cibaAuthRequestedUserHint": "login_hint", - "oauth2DeviceCodeLifespan": "600", - "clientOfflineSessionMaxLifespan": "0", - "oauth2DevicePollingInterval": "5", - "clientSessionIdleTimeout": "0", - "parRequestUriLifespan": "60", - "clientSessionMaxLifespan": "0", - "clientOfflineSessionIdleTimeout": "0", - "cibaInterval": "5", - "realmReusableOtpCode": "false" - }, - "keycloakVersion": "23.0.7", - "userManagedAccessAllowed": false, - "clientProfiles": { - "profiles": [] - }, - "clientPolicies": { - "policies": [] - } + ] } diff --git a/forms-flow-idm/realm-exports/Group based auth.json b/forms-flow-idm/realm-exports/Group based auth.json index 7e07cb54ad..86b7bebd51 100644 --- a/forms-flow-idm/realm-exports/Group based auth.json +++ b/forms-flow-idm/realm-exports/Group based auth.json @@ -1,966 +1,459 @@ { "id": "forms-flow-ai", "realm": "forms-flow-ai", - "notBefore": 0, - "revokeRefreshToken": false, - "refreshTokenMaxReuse": 0, - "accessTokenLifespan": 300, - "accessTokenLifespanForImplicitFlow": 900, - "ssoSessionIdleTimeout": 1800, - "ssoSessionMaxLifespan": 36000, - "ssoSessionIdleTimeoutRememberMe": 0, - "ssoSessionMaxLifespanRememberMe": 0, - "offlineSessionIdleTimeout": 2592000, - "offlineSessionMaxLifespanEnabled": false, - "offlineSessionMaxLifespan": 5184000, - "clientSessionIdleTimeout": 0, - "clientSessionMaxLifespan": 0, - "clientOfflineSessionIdleTimeout": 0, - "clientOfflineSessionMaxLifespan": 0, - "accessCodeLifespan": 60, - "accessCodeLifespanUserAction": 300, - "accessCodeLifespanLogin": 1800, - "actionTokenGeneratedByAdminLifespan": 43200, - "actionTokenGeneratedByUserLifespan": 300, + "loginTheme": "formsflow", "enabled": true, - "sslRequired": "external", - "registrationAllowed": false, - "registrationEmailAsUsername": false, - "rememberMe": false, - "verifyEmail": false, - "loginWithEmailAllowed": true, - "duplicateEmailsAllowed": false, - "resetPasswordAllowed": false, - "editUsernameAllowed": false, - "bruteForceProtected": false, - "permanentLockout": false, - "maxFailureWaitSeconds": 900, - "minimumQuickLoginWaitSeconds": 60, - "waitIncrementSeconds": 60, - "quickLoginCheckMilliSeconds": 1000, - "maxDeltaTimeSeconds": 43200, - "failureFactor": 30, "roles": { - "realm": [ - { - "name": "offline_access", - "description": "${role_offline-access}", - "composite": false, - "clientRole": false, - "containerId": "forms-flow-ai", - "attributes": {} - }, - { - "name": "uma_authorization", - "description": "${role_uma_authorization}", - "composite": false, - "clientRole": false, - "containerId": "forms-flow-ai", - "attributes": {} - } - ], "client": { - "realm-management": [ - { - "name": "query-groups", - "description": "${role_query-groups}", - "composite": false, - "clientRole": true, - - "attributes": {} - }, - { - "name": "query-clients", - "description": "${role_query-clients}", - "composite": false, - "clientRole": true, - - "attributes": {} - }, - { - "name": "view-events", - "description": "${role_view-events}", - "composite": false, - "clientRole": true, - - "attributes": {} - }, - { - "name": "impersonation", - "description": "${role_impersonation}", - "composite": false, - "clientRole": true, - - "attributes": {} - }, + "forms-flow-web": [ { - "name": "manage-events", - "description": "${role_manage-events}", + "name": "manage_users", + "description": "Manage Users", "composite": false, "clientRole": true, - "attributes": {} }, { - "name": "query-realms", - "description": "${role_query-realms}", + "name": "view_designs", + "description": "Access to design", "composite": false, "clientRole": true, - "attributes": {} }, { - "name": "query-users", - "description": "${role_query-users}", + "name": "create_designs", + "description": "Create Form, workflow designs", "composite": false, "clientRole": true, - "attributes": {} }, { - "name": "create-client", - "description": "${role_create-client}", + "name": "view_filters", + "description": "Access to view filters", "composite": false, "clientRole": true, - - "attributes": {} - }, - { - "name": "view-clients", - "description": "${role_view-clients}", - "composite": true, - "composites": { - "client": { - "realm-management": [ - "query-clients" - ] - } - }, - "clientRole": true, - "attributes": {} }, { - "name": "manage-authorization", - "description": "${role_manage-authorization}", + "name": "manage_roles", + "description": "Manage Roles", "composite": false, "clientRole": true, - "attributes": {} }, { - "name": "view-identity-providers", - "description": "${role_view-identity-providers}", + "name": "manage_integrations", + "description": "Access to Integrations", "composite": false, "clientRole": true, - - "attributes": {} - }, - { - "name": "view-users", - "description": "${role_view-users}", - "composite": true, - "composites": { - "client": { - "realm-management": [ - "query-users", - "query-groups" - ] - } - }, - "clientRole": true, - - "attributes": {} - }, - { - "name": "realm-admin", - "description": "${role_realm-admin}", - "composite": true, - "composites": { - "client": { - "realm-management": [ - "query-groups", - "query-clients", - "view-events", - "impersonation", - "manage-events", - "query-realms", - "query-users", - "create-client", - "view-clients", - "manage-authorization", - "view-identity-providers", - "view-users", - "view-realm", - "view-authorization", - "manage-identity-providers", - "manage-users", - "manage-realm", - "manage-clients" - ] - } - }, - "clientRole": true, - "attributes": {} }, { - "name": "view-realm", - "description": "${role_view-realm}", + "name": "view_dashboards", + "description": "Access to dashboards", "composite": false, "clientRole": true, - "attributes": {} }, { - "name": "view-authorization", - "description": "${role_view-authorization}", + "name": "manage_tasks", + "description": "Can claim and work on tasks", "composite": false, "clientRole": true, - "attributes": {} }, { - "name": "manage-identity-providers", - "description": "${role_manage-identity-providers}", + "name": "create_submissions", + "description": "Create submissions", "composite": false, "clientRole": true, - "attributes": {} }, { - "name": "manage-users", - "description": "${role_manage-users}", + "name": "create_filters", + "description": "Access to create filters", "composite": false, "clientRole": true, "attributes": {} }, { - "name": "manage-realm", - "description": "${role_manage-realm}", + "name": "view_tasks", + "description": "Access to tasks", "composite": false, "clientRole": true, - "attributes": {} }, { - "name": "manage-clients", - "description": "${role_manage-clients}", - "composite": false, - "clientRole": true, - - "attributes": {} - } - ], - "forms-flow-web": [ - { - "name": "formsflow-client", - "description": "Provides access to use the formsflow.ai solution. Required to access and submit forms.", + "name": "manage_dashboard_authorizations", + "description": "Manage Dashboard Authorization", "composite": false, "clientRole": true, - "attributes": {} }, { - "name": "formsflow-designer", - "description": "Provides access to use the formsflow.ai solution. Access to wok on form designer studio.", + "name": "view_submissions", + "description": "Access to submissions", "composite": false, "clientRole": true, - "attributes": {} }, { - "name": "formsflow-reviewer", - "description": "Provides access to use the formsflow.ai solution. Identifies the staff to work on applications and forms submissions.", + "name": "admin", + "description": "Administrator Role", "composite": false, "clientRole": true, - "attributes": {} }, { - "name": "formsflow-admin", - "description": "Provides access to use the formsflow.ai solution. Identifies the admin to work on admin panel.", + "name": "manage_all_filters", + "description": "Manage all filters", "composite": false, "clientRole": true, - "attributes": {} } ], - "security-admin-console": [], - "admin-cli": [], "forms-flow-bpm": [], - "account-console": [], - "broker": [ - { - "name": "read-token", - "description": "${role_read-token}", - "composite": false, - "clientRole": true, - - "attributes": {} - } - ], - "forms-flow-analytics": [], - "account": [ - { - "name": "view-profile", - "description": "${role_view-profile}", - "composite": false, - "clientRole": true, - - "attributes": {} - }, - { - "name": "manage-account", - "description": "${role_manage-account}", - "composite": true, - "composites": { - "client": { - "account": [ - "manage-account-links" - ] - } - }, - "clientRole": true, - - "attributes": {} - }, - { - "name": "manage-account-links", - "description": "${role_manage-account-links}", - "composite": false, - "clientRole": true, - - "attributes": {} - }, - { - "name": "view-consent", - "description": "${role_view-consent}", - "composite": false, - "clientRole": true, - - "attributes": {} - }, - { - "name": "manage-consent", - "description": "${role_manage-consent}", - "composite": true, - "composites": { - "client": { - "account": [ - "view-consent" - ] - } - }, - "clientRole": true, - - "attributes": {} - }, - { - "name": "view-applications", - "description": "${role_view-applications}", - "composite": false, - "clientRole": true, - - "attributes": {} - } - ] + "forms-flow-analytics": [] } }, "groups": [ { "name": "camunda-admin", "path": "/camunda-admin", - "attributes": {}, + "subGroups": [], + "attributes": { + "description": ["Camunda Administrator Role."] + }, "realmRoles": [], - "clientRoles": {}, - "subGroups": [] + "clientRoles": {} }, { "name": "formsflow", "path": "/formsflow", - "attributes": {}, - "realmRoles": [], - "clientRoles": {}, "subGroups": [ { - "name": "formsflow-client", - "path": "/formsflow/formsflow-client", - "attributes": {}, + "name": "formsflow-admin", + "path": "/formsflow/formsflow-admin", + "subGroups": [], + "attributes": { + "description": ["Administrator Role."] + }, "realmRoles": [], "clientRoles": { + "realm-management": [ + "query-groups", + "view-users", + "manage-clients", + "query-clients", + "view-authorization", + "query-users", + "manage-users", + "create-client", + "view-clients" + ], "forms-flow-web": [ - "formsflow-client" + "manage_users", + "manage_roles", + "manage_dashboard_authorizations", + "admin" ] - }, - "subGroups": [] + } }, { - "name": "formsflow-designer", - "path": "/formsflow/formsflow-designer", - "attributes": {}, + "name": "formsflow-client", + "path": "/formsflow/formsflow-client", + "subGroups": [], + "attributes": { + "description": ["Client role to create & view submissions."] + }, "realmRoles": [], "clientRoles": { - "forms-flow-web": [ - "formsflow-designer" - ] - }, - "subGroups": [] + "forms-flow-web": ["create_submissions", "view_submissions"] + } }, { - "name": "formsflow-admin", - "path": "/formsflow/formsflow-admin", - "attributes": {}, + "name": "formsflow-designer", + "path": "/formsflow/formsflow-designer", + "subGroups": [], + "attributes": { + "description": ["Designer role to create forms and workflows."] + }, "realmRoles": [], "clientRoles": { "forms-flow-web": [ - "formsflow-admin" + "manage_integrations", + "view_designs", + "create_designs" ] - }, - "subGroups": [] + } }, { "name": "formsflow-reviewer", "path": "/formsflow/formsflow-reviewer", - "attributes": {}, + "subGroups": [ + { + "name": "approver", + "path": "/formsflow/formsflow-reviewer/approver", + "subGroups": [], + "attributes": { + "description": ["Staff role for reviewing tasks."] + }, + "realmRoles": [], + "clientRoles": { + "forms-flow-web": ["manage_tasks", "view_tasks", "view_filters"] + } + }, + { + "name": "clerk", + "path": "/formsflow/formsflow-reviewer/clerk", + "subGroups": [], + "attributes": { + "description": ["Staff role for reviewing tasks."] + }, + "realmRoles": [], + "clientRoles": { + "forms-flow-web": ["manage_tasks", "view_tasks", "view_filters"] + } + } + ], + "attributes": { + "description": [ + "Staff role for monitoring submissions and performing tasks." + ] + }, "realmRoles": [], "clientRoles": { "forms-flow-web": [ - "formsflow-reviewer" + "view_dashboards", + "manage_tasks", + "view_tasks", + "create_filters", + "view_submissions", + "view_filters" ] - }, - - "subGroups": [ - { - "name": "clerk", - "path": "/formsflow/formsflow-reviewer/clerk", - "attributes": {}, - "realmRoles": [], - "clientRoles": {}, - "subGroups": [] - }, - { - "name": "approver", - "path": "/formsflow/formsflow-reviewer/approver", - "attributes": {}, - "realmRoles": [], - "clientRoles": {}, - "subGroups": [] - } - ] + } } - ] + ], + "attributes": {}, + "realmRoles": [], + "clientRoles": {} }, { "name": "formsflow-analytics", "path": "/formsflow-analytics", - "attributes": {}, - "realmRoles": [], - "clientRoles": {}, "subGroups": [ { - "name": "group2", - "path": "/formsflow-analytics/group2", - "attributes": {}, + "name": "group1", + "path": "/formsflow-analytics/group1", + "subGroups": [], + "attributes": { + "description": ["Role with dashboard authorization."] + }, "realmRoles": [], - "clientRoles": {}, - "subGroups": [] + "clientRoles": { + "forms-flow-web": ["view_dashboards"] + } }, { - "name": "group1", - "path": "/formsflow-analytics/group1", - "attributes": {}, + "name": "group2", + "path": "/formsflow-analytics/group2", + "subGroups": [], + "attributes": { + "description": ["Role with dashboard authorization."] + }, "realmRoles": [], - "clientRoles": {}, - "subGroups": [] + "clientRoles": { + "forms-flow-web": ["view_dashboards"] + } } - ] + ], + "attributes": { + "description": ["Role with dashboard authorization."] + }, + "realmRoles": [], + "clientRoles": { + "forms-flow-web": ["view_dashboards"] + } } ], - "defaultRoles": [ - "offline_access", - "uma_authorization" - ], - "defaultGroups": [ - "/camunda-admin", - "/formsflow", - "/formsflow-analytics" - ], - "requiredCredentials": [ - "password" - ], - "otpPolicyType": "totp", - "otpPolicyAlgorithm": "HmacSHA1", - "otpPolicyInitialCounter": 0, - "otpPolicyDigits": 6, - "otpPolicyLookAheadWindow": 1, - "otpPolicyPeriod": 30, - "otpSupportedApplications": [ - "FreeOTP", - "Google Authenticator" - ], - "webAuthnPolicyRpEntityName": "keycloak", - "webAuthnPolicySignatureAlgorithms": [ - "ES256" - ], - "webAuthnPolicyRpId": "", - "webAuthnPolicyAttestationConveyancePreference": "not specified", - "webAuthnPolicyAuthenticatorAttachment": "not specified", - "webAuthnPolicyRequireResidentKey": "not specified", - "webAuthnPolicyUserVerificationRequirement": "not specified", - "webAuthnPolicyCreateTimeout": 0, - "webAuthnPolicyAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyAcceptableAaguids": [], - "webAuthnPolicyPasswordlessRpEntityName": "keycloak", - "webAuthnPolicyPasswordlessSignatureAlgorithms": [ - "ES256" - ], - "webAuthnPolicyPasswordlessRpId": "", - "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", - "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", - "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", - "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", - "webAuthnPolicyPasswordlessCreateTimeout": 0, - "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyPasswordlessAcceptableAaguids": [], - "users" : [ { - "createdTimestamp" : 1621862607660, - "username" : "formsflow-client", - "enabled" : true, - "totp" : false, - "emailVerified" : false, - "firstName" : "Client", - "lastName" : "FFA", - "email" : "formsflow-client@example.com", - "credentials" : [ { - "type" : "password", - "createdDate" : 1621863987325, - "secretData" : "{\"value\":\"9R9a8Onha7JcZt59SIq8ngfqwDJwPKiKb8mJ2WO6p2eI3S9qhzR1GPDFtKjOWq8qpm8vsGfp/a/DyHWQuIvmlA==\",\"salt\":\"R/OTBeSXHzsKtJOV/bufEA==\"}", - "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" - } ], - "disableableCredentialTypes" : [ ], - "requiredActions" : [ "UPDATE_PASSWORD" ], - "realmRoles" : [ "uma_authorization", "offline_access" ], - "clientRoles" : { - "account" : [ "view-profile", "manage-account" ] - }, - "notBefore" : 0, - "groups" : [ "/camunda-admin", "/formsflow/formsflow-client" ] - }, { - "createdTimestamp" : 1621862546931, - "username" : "formsflow-designer", - "enabled" : true, - "totp" : false, - "emailVerified" : false, - "firstName" : "Designer", - "lastName" : "FFA", - "email" : "formsflow-designer@example.com", - "credentials" : [ { - "type" : "password", - "createdDate" : 1621864001408, - "secretData" : "{\"value\":\"XlcFXNSJAfv5YzTh5vd4NtyEjWm4B47CS9MA3aHmEjLNjdRMbnGFVFZwlZx3alXYBCg4Evs3md25DQ6Xvl+nZg==\",\"salt\":\"TefHE1L0xpqlAMg/h6w6BA==\"}", - "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" - } ], - "disableableCredentialTypes" : [ ], - "requiredActions" : [ "UPDATE_PASSWORD" ], - "realmRoles" : [ "uma_authorization", "offline_access" ], - "clientRoles" : { - "account" : [ "view-profile", "manage-account" ] - }, - "notBefore" : 0, - "groups" : [ "/camunda-admin", "/formsflow/formsflow-designer" ] - }, { - "createdTimestamp" : 1625009614956, - "username" : "formsflow-approver", - "enabled" : true, - "totp" : false, - "emailVerified" : false, - "firstName" : "Approver", - "lastName" : "FFA", - "email" : "formsflow-approver@aot-technologies.com", - "credentials" : [ { - "type" : "password", - "createdDate" : 1625009625540, - "secretData" : "{\"value\":\"Ej6BGTe5D+jLChY9zmoty3Jzt8i+KoV+UTPK6+1Vi+GaUpVfdJ0RFJ/7M4+1Y1jNGBcvMgc8knQT2AJDtixxRQ==\",\"salt\":\"MDP6nouKEx0l7hdJA+lIJw==\"}", - "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" - } ], - "disableableCredentialTypes" : [ ], - "requiredActions" : [ "UPDATE_PASSWORD" ], - "notBefore" : 0, - "groups" : [ "/formsflow/formsflow-reviewer/approver", "/camunda-admin", "/formsflow" , "/formsflow-analytics/group1"] - }, { - "createdTimestamp" : 1625009564217, - "username" : "formsflow-clerk", - "enabled" : true, - "totp" : false, - "emailVerified" : false, - "firstName" : "Clerk", - "lastName" : "FFA", - "email" : "formsflow-clerk@aot-technologies.com", - "credentials" : [ { - "type" : "password", - "createdDate" : 1625009575561, - "secretData" : "{\"value\":\"iWFPywh7ck8FesufjXu81lxpJ0XKSvPd9ladBcJrE4TTXLeQOhvqBOC5e+bwVg20Y61EwtkTta0L9MWtGpSraw==\",\"salt\":\"noE1kDJ7Lo20VYuTOHOBTw==\"}", - "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" - } ], - "disableableCredentialTypes" : [ ], - "requiredActions" : [ "UPDATE_PASSWORD" ], - "notBefore" : 0, - "groups" : [ "/camunda-admin", "/formsflow/formsflow-reviewer/clerk", "/formsflow", "/formsflow-analytics/group2" ] - }, { - "createdTimestamp" : 1621862578318, - "username" : "formsflow-reviewer", - "enabled" : true, - "totp" : false, - "emailVerified" : false, - "firstName" : "Reviewer", - "lastName" : "FFA", - "email" : "formsflow-reviewer@example.com", - "credentials" : [ { - "type" : "password", - "createdDate" : 1621864014317, - "secretData" : "{\"value\":\"bXzhJ0BrMJBWMzRRjO2khWgCRgDAA6vfTrE0UNNO1DNRzp1aMrGCz5kF20H76PjyuqNDZaKF1nKApEjccg+KRA==\",\"salt\":\"gmoZwO3i7Y6B+jpgnsnixw==\"}", - "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" - } ], - "disableableCredentialTypes" : [ ], - "requiredActions" : [ "UPDATE_PASSWORD" ], - "realmRoles" : [ "uma_authorization", "offline_access" ], - "clientRoles" : { - "account" : [ "view-profile", "manage-account" ] - }, - "notBefore" : 0, - "groups" : [ "/camunda-admin", "/formsflow/formsflow-reviewer", "/formsflow-analytics/group1" ] - }, { - "createdTimestamp" : 1621585233480, - "username" : "service-account-forms-flow-bpm", - "enabled" : true, - "totp" : false, - "emailVerified" : false, - "serviceAccountClientId" : "forms-flow-bpm", - "credentials" : [ ], - "disableableCredentialTypes" : [ ], - "requiredActions" : [ ], - "realmRoles" : [ "uma_authorization", "offline_access" ], - "clientRoles" : { - "realm-management" : [ "manage-users", "query-users", "query-groups", "view-users" , "query-clients", "realm-admin"], - "account" : [ "view-profile", "manage-account" ] + "users": [ + { + "username": "formsflow-admin", + "firstName": "ff", + "lastName": "admin", + "email": "formsflow-admin@aot-technologies.com", + "emailVerified": false, + "createdTimestamp": 1718023583532, + "enabled": true, + "totp": false, + "credentials": [ + { + "type": "password", + "userLabel": "My password", + "createdDate": 1718087465595, + "secretData": "{\"value\":\"EpHuQHA+nMza4usSNWSDxl6bZ9pbKFe4vOjuMMEZijQ=\",\"salt\":\"Ht1p7xh28sqw6+FZ1ICcOg==\",\"additionalParameters\":{}}", + "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } + ], + "disableableCredentialTypes": [], + "requiredActions": ["UPDATE_PASSWORD"], + "realmRoles": ["default-roles-forms-flow-ai"], + "notBefore": 0, + "groups": [ + "/camunda-admin", + "/formsflow/formsflow-admin", + "/formsflow-analytics" + ] }, - "notBefore" : 0, - "groups" : [ "/camunda-admin" ] - } ], - "scopeMappings" : [ { - "clientScope" : "offline_access", - "roles" : [ "offline_access" ] - } ], - "clientScopeMappings" : { - "account" : [ { - "client" : "account-console", - "roles" : [ "manage-account" ] - } ] - }, + { + "username": "service-account-forms-flow-bpm", + "emailVerified": false, + "createdTimestamp": 1621585233480, + "enabled": true, + "totp": false, + "serviceAccountClientId": "forms-flow-bpm", + "credentials": [], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": ["offline_access", "uma_authorization"], + "clientRoles": { + "realm-management": [ + "query-users", + "query-groups", + "view-users", + "manage-users", + "manage-clients", + "realm-admin" + ], + "account": ["view-profile", "manage-account"] + }, + "notBefore": 0, + "groups": ["/camunda-admin"] + } + ], "clients": [ { - "clientId": "account", - "name": "${client_account}", - "rootUrl": "${authBaseUrl}", - "baseUrl": "/realms/forms-flow-ai/account/", + "clientId": "forms-flow-analytics", + "description": "Redash-Analytics", + "adminUrl": "http://localhost:7000/saml/callback?org_slug=default", "surrogateAuthRequired": false, "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "secret": "**********", - "defaultRoles": [ - "view-profile", - "manage-account" - ], - "redirectUris": [ - "/realms/forms-flow-ai/account/*" - ], + "redirectUris": ["http://localhost:7000/*", "*"], "webOrigins": [], "notBefore": 0, "bearerOnly": false, "consentRequired": false, "standardFlowEnabled": true, "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, + "directAccessGrantsEnabled": true, "serviceAccountsEnabled": false, - "publicClient": false, + "publicClient": true, "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, + "protocol": "saml", + "attributes": { + "saml.assertion.signature": "true", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "post.logout.redirect.uris": "+", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml.artifact.binding.identifier": "OOFH7REqnhW7gnm7DkWoK9smSR4=", + "saml.signature.algorithm": "RSA_SHA256", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "true", + "display.on.consent.screen": "false", + "saml_name_id_format": "email", + "saml.onetimeuse.condition": "false", + "saml_signature_canonicalization_method": "http://www.w3.org/2001/10/xml-exc-c14n#WithComments" + }, "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [ - "web-origins", - "role_list", - "profile", - "roles", - "email" + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "name": "X500 surname", + "protocol": "saml", + "protocolMapper": "saml-user-property-mapper", + "consentRequired": false, + "config": { + "user.attribute": "lastName", + "friendly.name": "LastName", + "attribute.name": "urn:oid:2.5.4.4" + } + }, + { + "name": "X500 givenName", + "protocol": "saml", + "protocolMapper": "saml-user-property-mapper", + "consentRequired": false, + "config": { + "user.attribute": "firstName", + "friendly.name": "FirstName", + "attribute.name": "urn:oid:2.5.4.42" + } + } ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] + "defaultClientScopes": ["role_list"], + "optionalClientScopes": [] }, { - "clientId": "account-console", - "name": "${client_account-console}", - "rootUrl": "${authBaseUrl}", - "baseUrl": "/realms/forms-flow-ai/account/", + "clientId": "forms-flow-bpm", + "description": "Camunda Process Engine Components", "surrogateAuthRequired": false, "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": [ - "/realms/forms-flow-ai/account/*" - ], - "webOrigins": [], + "secret": "e4bdbd25-1467-4f7f-b993-bc4b1944c943", + "redirectUris": ["http://localhost:8000/camunda/*", "*"], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, "standardFlowEnabled": true, "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, "frontchannelLogout": false, "protocol": "openid-connect", "attributes": { - "pkce.code.challenge.method": "S256" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "name": "audience resolve", - "protocol": "openid-connect", - "protocolMapper": "oidc-audience-resolve-mapper", - "consentRequired": false, - "config": {} - } - ], - "defaultClientScopes": [ - "web-origins", - "role_list", - "roles", - "profile", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "clientId": "admin-cli", - "name": "${client_admin-cli}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": false, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [ - "web-origins", - "role_list", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "clientId": "broker", - "name": "${client_broker}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [ - "web-origins", - "role_list", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "clientId": "forms-flow-analytics", - "description": "Redash-Analytics", - "adminUrl": "http://localhost:7000/saml/callback?org_slug=default", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": [ - "http://localhost:7000/*", - "*" - ], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "saml", - "attributes": { - "saml.assertion.signature": "true", + "saml.assertion.signature": "false", "saml.force.post.binding": "false", "saml.multivalued.roles": "false", "saml.encrypt": "false", + "post.logout.redirect.uris": "+", "saml.server.signature": "false", "saml.server.signature.keyinfo.ext": "false", "exclude.session.state.from.auth.response": "false", - "saml.signature.algorithm": "RSA_SHA256", "saml_force_name_id_format": "false", "saml.client.signature": "false", "tls.client.certificate.bound.access.tokens": "false", - "saml.authnstatement": "true", + "saml.authnstatement": "false", "display.on.consent.screen": "false", - "saml_name_id_format": "email", - "saml.onetimeuse.condition": "false", - "saml_signature_canonicalization_method": "http://www.w3.org/2001/10/xml-exc-c14n#WithComments" + "saml.onetimeuse.condition": "false" }, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": true, "nodeReRegistrationTimeout": -1, "protocolMappers": [ { - "name": "X500 surname", - "protocol": "saml", - "protocolMapper": "saml-user-property-mapper", + "name": "formsflow-web-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { - "user.attribute": "lastName", - "friendly.name": "LastName", - "attribute.name": "urn:oid:2.5.4.4" + "included.client.audience": "forms-flow-web", + "id.token.claim": "false", + "access.token.claim": "true", + "userinfo.token.claim": "false" } }, - { - "name": "X500 givenName", - "protocol": "saml", - "protocolMapper": "saml-user-property-mapper", - "consentRequired": false, - "config": { - "user.attribute": "firstName", - "friendly.name": "FirstName", - "attribute.name": "urn:oid:2.5.4.42" - } - } - ], - "defaultClientScopes": [ - "web-origins", - "role_list", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "clientId": "forms-flow-bpm", - "description": "Camunda Process Engine Components", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "e4bdbd25-1467-4f7f-b993-bc4b1944c943", - "redirectUris": [ - "http://localhost:8000/camunda/*", - "*" - ], - "webOrigins": [ - "*" - ], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": true, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "saml.assertion.signature": "false", - "saml.force.post.binding": "false", - "saml.multivalued.roles": "false", - "saml.encrypt": "false", - "saml.server.signature": "false", - "saml.server.signature.keyinfo.ext": "false", - "exclude.session.state.from.auth.response": "false", - "saml_force_name_id_format": "false", - "saml.client.signature": "false", - "tls.client.certificate.bound.access.tokens": "false", - "saml.authnstatement": "false", - "display.on.consent.screen": "false", - "saml.onetimeuse.condition": "false" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": true, - "nodeReRegistrationTimeout": -1, - "protocolMappers": [ { "name": "Client Host", "protocol": "openid-connect", @@ -968,23 +461,25 @@ "consentRequired": false, "config": { "user.session.note": "clientHost", - "userinfo.token.claim": "true", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "clientHost", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "formsflow-web-mapper", + "name": "username", "protocol": "openid-connect", - "protocolMapper": "oidc-audience-mapper", + "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "included.client.audience": "forms-flow-web", - "id.token.claim": "false", + "user.attribute": "username", + "id.token.claim": "true", "access.token.claim": "true", - "userinfo.token.claim": "false" + "claim.name": "preferred_username", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -994,36 +489,36 @@ "consentRequired": false, "config": { "user.session.note": "clientAddress", - "userinfo.token.claim": "true", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "clientAddress", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, - { - "name": "camunda-rest-api", + { + "name": "groups", "protocol": "openid-connect", - "protocolMapper": "oidc-audience-mapper", + "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { - "id.token.claim": "false", + "full.path": "true", + "id.token.claim": "true", "access.token.claim": "true", - "included.custom.audience": "camunda-rest-api" + "claim.name": "groups", + "userinfo.token.claim": "true" } }, { - "name": "username", + "name": "camunda-rest-api", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", + "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", + "id.token.claim": "false", "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" + "included.custom.audience": "camunda-rest-api", + "userinfo.token.claim": "false" } }, { @@ -1033,32 +528,19 @@ "consentRequired": false, "config": { "user.session.note": "clientId", - "userinfo.token.claim": "true", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "clientId", - "jsonType.label": "String" - } - }, - { - "name": "groups", - "protocol": "openid-connect", - "protocolMapper": "oidc-group-membership-mapper", - "consentRequired": false, - "config": { - "full.path": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "groups", + "jsonType.label": "String", "userinfo.token.claim": "true" } } ], "defaultClientScopes": [ "web-origins", - "role_list", - "profile", "roles", + "profile", + "basic", "camunda-rest-api", "email" ], @@ -1076,14 +558,8 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": [ - "http://localhost:3000/*", - "*" - ], - "webOrigins": [ - "*" - ], + "redirectUris": ["http://localhost:3000/*", "*"], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -1099,6 +575,7 @@ "saml.force.post.binding": "false", "saml.multivalued.roles": "false", "saml.encrypt": "false", + "post.logout.redirect.uris": "+", "saml.server.signature": "false", "saml.server.signature.keyinfo.ext": "false", "exclude.session.state.from.auth.response": "false", @@ -1114,26 +591,27 @@ "nodeReRegistrationTimeout": -1, "protocolMappers": [ { - "name": "formsflow-web-mapper", + "name": "camunda-rest-api", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { - "included.client.audience": "forms-flow-web", "id.token.claim": "false", "access.token.claim": "true", + "included.custom.audience": "camunda-rest-api", "userinfo.token.claim": "false" } }, { - "name": "camunda-rest-api", + "name": "formsflow-web-mapper", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { + "included.client.audience": "forms-flow-web", "id.token.claim": "false", "access.token.claim": "true", - "included.custom.audience": "camunda-rest-api" + "userinfo.token.claim": "false" } }, { @@ -1167,9 +645,9 @@ ], "defaultClientScopes": [ "web-origins", - "role_list", - "profile", "roles", + "profile", + "basic", "camunda-rest-api", "email" ], @@ -1179,327 +657,344 @@ "offline_access", "microprofile-jwt" ] - }, - { - "clientId": "realm-management", - "name": "${client_realm-management}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": true, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [ - "web-origins", - "role_list", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, + } + ], + "clientScopes": [ { - "clientId": "security-admin-console", - "name": "${client_security-admin-console}", - "rootUrl": "${authAdminUrl}", - "baseUrl": "/admin/forms-flow-ai/console/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "**********", - "redirectUris": [ - "/admin/forms-flow-ai/console/*" - ], - "webOrigins": [ - "+" - ], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, + "name": "address", + "description": "OpenID Connect built-in scope: address", "protocol": "openid-connect", "attributes": { - "pkce.code.challenge.method": "S256" + "include.in.token.scope": "true", + "consent.screen.text": "${addressScopeConsentText}", + "display.on.consent.screen": "true" }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, "protocolMappers": [ { - "name": "locale", + "id": "5d9b4833-6472-4de5-8a32-ac8111d77aa1", + "name": "address", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-address-mapper", "consentRequired": false, "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", "userinfo.token.claim": "true", - "user.attribute": "locale", + "user.attribute.street": "street", "id.token.claim": "true", + "user.attribute.region": "region", "access.token.claim": "true", - "claim.name": "locale", - "jsonType.label": "String" + "user.attribute.locality": "locality" } } - ], - "defaultClientScopes": [ - "web-origins", - "role_list", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" ] - } - ], - "clientScopes": [ + }, { - "name": "role_list", - "description": "SAML role list", - "protocol": "saml", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", "attributes": { - "consent.screen.text": "${samlRoleListScopeConsentText}", - "display.on.consent.screen": "true" + "include.in.token.scope": "false", + "display.on.consent.screen": "false" }, "protocolMappers": [ { - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", "consentRequired": false, "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" } } ] }, { - "name": "offline_access", - "description": "OpenID Connect built-in scope: offline_access", - "protocol": "openid-connect", - "attributes": { - "consent.screen.text": "${offlineAccessScopeConsentText}", - "display.on.consent.screen": "true" - } - }, - { - "name": "profile", - "description": "OpenID Connect built-in scope: profile", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", "protocol": "openid-connect", "attributes": { "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${profileScopeConsentText}" + "display.on.consent.screen": "false" }, "protocolMappers": [ { - "name": "full name", + "name": "upn", "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", + "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { + "user.attribute": "username", "id.token.claim": "true", "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String", "userinfo.token.claim": "true" } }, { - "name": "profile", + "name": "groups", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-usermodel-realm-role-mapper", "consentRequired": false, "config": { + "multivalued": "true", "userinfo.token.claim": "true", - "user.attribute": "profile", + "user.attribute": "foo", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "profile", + "claim.name": "groups", "jsonType.label": "String" } - }, + } + ] + }, + { + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "${rolesScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ { - "name": "gender", + "name": "Role", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-usermodel-client-role-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "gender", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "gender", - "jsonType.label": "String" + "claim.name": "role", + "usermodel.clientRoleMapping.clientId": "forms-flow-web", + "multivalued": "true", + "userinfo.token.claim": "true" } }, { - "name": "website", + "name": "client roles", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-usermodel-client-role-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "website", - "id.token.claim": "true", + "user.attribute": "foo", "access.token.claim": "true", - "claim.name": "website", - "jsonType.label": "String" + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" } }, { - "name": "zoneinfo", + "name": "realm roles", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-usermodel-realm-role-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "zoneinfo", - "id.token.claim": "true", + "user.attribute": "foo", "access.token.claim": "true", - "claim.name": "zoneinfo", - "jsonType.label": "String" + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" } }, { - "name": "given name", + "name": "audience resolve", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", + "protocolMapper": "oidc-audience-resolve-mapper", "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" - } - }, + "config": {} + } + ] + }, + { + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "name": "basic", + "description": "OpenID Connect scope for add all basic claims to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ { - "name": "nickname", + "name": "sub", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-sub-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "nickname", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "nickname", - "jsonType.label": "String" + "introspection.token.claim": "true", + "access.token.claim": "true" } }, { - "name": "locale", + "name": "auth_time", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-usersessionmodel-note-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "locale", + "user.session.note": "AUTH_TIME", "id.token.claim": "true", + "introspection.token.claim": "true", "access.token.claim": "true", - "claim.name": "locale", - "jsonType.label": "String" + "claim.name": "auth_time", + "jsonType.label": "long" } - }, + } + ] + }, + { + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${emailScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ { - "name": "updated at", + "name": "email", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "updatedAt", + "user.attribute": "email", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "updated_at", - "jsonType.label": "String" + "claim.name": "email", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "birthdate", + "name": "email verified", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", + "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "birthdate", + "user.attribute": "emailVerified", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "birthdate", - "jsonType.label": "String" + "claim.name": "email_verified", + "jsonType.label": "boolean", + "userinfo.token.claim": "true" } - }, + } + ] + }, + { + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ { - "name": "family name", + "name": "allowed web origins", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", - "id.token.claim": "true", + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "name": "camunda-rest-api", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "name": "camunda-rest-api", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "false", "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" + "included.custom.audience": "camunda-rest-api", + "userinfo.token.claim": "false" } - }, + } + ] + }, + { + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${profileScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ { - "name": "middle name", + "name": "zoneinfo", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "middleName", + "user.attribute": "zoneinfo", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "middle_name", - "jsonType.label": "String" + "claim.name": "zoneinfo", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "username", + "name": "gender", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", + "user.attribute": "gender", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" + "claim.name": "gender", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1508,261 +1003,203 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "picture", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "picture", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } - } - ] - }, - { - "name": "email", - "description": "OpenID Connect built-in scope: email", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${emailScopeConsentText}" - }, - "protocolMappers": [ + }, { - "name": "email verified", + "name": "username", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "emailVerified", + "user.attribute": "username", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "email_verified", - "jsonType.label": "boolean" + "claim.name": "preferred_username", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "email", + "name": "website", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", + "user.attribute": "website", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" + "claim.name": "website", + "jsonType.label": "String", + "userinfo.token.claim": "true" } - } - ] - }, - { - "name": "address", - "description": "OpenID Connect built-in scope: address", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${addressScopeConsentText}" - }, - "protocolMappers": [ + }, { - "name": "address", + "name": "locale", "protocol": "openid-connect", - "protocolMapper": "oidc-address-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "user.attribute.formatted": "formatted", - "user.attribute.country": "country", - "user.attribute.postal_code": "postal_code", - "userinfo.token.claim": "true", - "user.attribute.street": "street", + "user.attribute": "locale", "id.token.claim": "true", - "user.attribute.region": "region", "access.token.claim": "true", - "user.attribute.locality": "locality" + "claim.name": "locale", + "jsonType.label": "String", + "userinfo.token.claim": "true" } - } - ] - }, - { - "name": "phone", - "description": "OpenID Connect built-in scope: phone", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${phoneScopeConsentText}" - }, - "protocolMappers": [ + }, { - "name": "phone number verified", + "name": "middle name", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "phoneNumberVerified", + "user.attribute": "middleName", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "phone_number_verified", - "jsonType.label": "boolean" + "claim.name": "middle_name", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "phone number", + "name": "nickname", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "phoneNumber", + "user.attribute": "nickname", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "phone_number", - "jsonType.label": "String" + "claim.name": "nickname", + "jsonType.label": "String", + "userinfo.token.claim": "true" } - } - ] - }, - { - "name": "roles", - "description": "OpenID Connect scope for add user roles to the access token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "true", - "consent.screen.text": "${rolesScopeConsentText}" - }, - "protocolMappers": [ + }, { - "name": "realm roles", + "name": "profile", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-realm-role-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "user.attribute": "foo", + "user.attribute": "profile", + "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "realm_access.roles", + "claim.name": "profile", "jsonType.label": "String", - "multivalued": "true" + "userinfo.token.claim": "true" } }, { - "name": "client roles", + "name": "birthdate", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-client-role-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "user.attribute": "foo", + "user.attribute": "birthdate", + "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "resource_access.${client_id}.roles", + "claim.name": "birthdate", "jsonType.label": "String", - "multivalued": "true" + "userinfo.token.claim": "true" } }, { - "name": "Role", + "name": "full name", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-client-role-mapper", + "protocolMapper": "oidc-full-name-mapper", "consentRequired": false, "config": { - "multivalued": "true", - "userinfo.token.claim": "true", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "role", - "usermodel.clientRoleMapping.clientId": "forms-flow-web" + "userinfo.token.claim": "true" } }, { - "name": "audience resolve", + "name": "updated at", "protocol": "openid-connect", - "protocolMapper": "oidc-audience-resolve-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, - "config": {} - } - ] - }, - { - "name": "web-origins", - "description": "OpenID Connect scope for add allowed web origins to the access token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "false", - "consent.screen.text": "" - }, - "protocolMappers": [ - { - "name": "allowed web origins", - "protocol": "openid-connect", - "protocolMapper": "oidc-allowed-origins-mapper", - "consentRequired": false, - "config": {} - } - ] - }, - { - "name": "microprofile-jwt", - "description": "Microprofile - JWT built-in scope", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "false" - }, - "protocolMappers": [ + "config": { + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String", + "userinfo.token.claim": "true" + } + }, { - "name": "groups", + "name": "family name", "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-realm-role-mapper", + "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "multivalued": "true", - "userinfo.token.claim": "true", - "user.attribute": "foo", + "user.attribute": "lastName", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "groups", - "jsonType.label": "String" + "claim.name": "family_name", + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { - "name": "upn", + "name": "given name", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", + "user.attribute": "firstName", "id.token.claim": "true", "access.token.claim": "true", - "claim.name": "upn", - "jsonType.label": "String" + "claim.name": "given_name", + "jsonType.label": "String", + "userinfo.token.claim": "true" } } ] }, { - "name": "camunda-rest-api", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", "protocol": "openid-connect", "attributes": { "include.in.token.scope": "true", + "consent.screen.text": "${phoneScopeConsentText}", "display.on.consent.screen": "true" }, "protocolMappers": [ { - "name": "camunda-rest-api", + "name": "phone number", "protocol": "openid-connect", - "protocolMapper": "oidc-audience-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "id.token.claim": "false", + "user.attribute": "phoneNumber", + "id.token.claim": "true", "access.token.claim": "true", - "included.custom.audience": "camunda-rest-api", - "userinfo.token.claim": "false" + "claim.name": "phone_number", + "jsonType.label": "String", + "userinfo.token.claim": "true" + } + }, + { + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean", + "userinfo.token.claim": "true" } } ] @@ -1774,765 +1211,14 @@ "email", "roles", "web-origins", - "camunda-rest-api" + "camunda-rest-api", + "acr", + "basic" ], "defaultOptionalClientScopes": [ "offline_access", "address", "phone", "microprofile-jwt" - ], - "browserSecurityHeaders": { - "contentSecurityPolicyReportOnly": "", - "xContentTypeOptions": "nosniff", - "xRobotsTag": "none", - "xFrameOptions": "SAMEORIGIN", - "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", - "xXSSProtection": "1; mode=block", - "strictTransportSecurity": "max-age=31536000; includeSubDomains" - }, - "smtpServer": {}, - "eventsEnabled": false, - "eventsListeners": [ - "jboss-logging" - ], - "enabledEventTypes": [], - "adminEventsEnabled": false, - "adminEventsDetailsEnabled": false, - "components": { - "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ - { - "name": "Consent Required", - "providerId": "consent-required", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "name": "Allowed Client Scopes", - "providerId": "allowed-client-templates", - "subType": "authenticated", - "subComponents": {}, - "config": { - "allow-default-scopes": [ - "true" - ] - } - }, - { - "name": "Allowed Client Scopes", - "providerId": "allowed-client-templates", - "subType": "anonymous", - "subComponents": {}, - "config": { - "allow-default-scopes": [ - "true" - ] - } - }, - { - "name": "Full Scope Disabled", - "providerId": "scope", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "name": "Trusted Hosts", - "providerId": "trusted-hosts", - "subType": "anonymous", - "subComponents": {}, - "config": { - "host-sending-registration-request-must-match": [ - "true" - ], - "client-uris-must-match": [ - "true" - ] - } - }, - { - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "authenticated", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "oidc-usermodel-attribute-mapper", - "oidc-full-name-mapper", - "saml-user-attribute-mapper", - "saml-user-property-mapper", - "oidc-address-mapper", - "saml-role-list-mapper", - "oidc-sha256-pairwise-sub-mapper", - "oidc-usermodel-property-mapper" - ] - } - }, - { - "name": "Max Clients Limit", - "providerId": "max-clients", - "subType": "anonymous", - "subComponents": {}, - "config": { - "max-clients": [ - "200" - ] - } - }, - { - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "anonymous", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "saml-role-list-mapper", - "oidc-usermodel-attribute-mapper", - "oidc-address-mapper", - "oidc-sha256-pairwise-sub-mapper", - "oidc-full-name-mapper", - "saml-user-attribute-mapper", - "oidc-usermodel-property-mapper", - "saml-user-property-mapper" - ] - } - } - ], - "org.keycloak.keys.KeyProvider": [ - { - "name": "hmac-generated", - "providerId": "hmac-generated", - "subComponents": {}, - "config": { - "priority": [ - "100" - ], - "algorithm": [ - "HS256" - ] - } - }, - { - "name": "rsa-generated", - "providerId": "rsa-generated", - "subComponents": {}, - "config": { - "priority": [ - "100" - ] - } - }, - { - "name": "aes-generated", - "providerId": "aes-generated", - "subComponents": {}, - "config": { - "priority": [ - "100" - ] - } - } - ] - }, - "internationalizationEnabled": false, - "supportedLocales": [], - "authenticationFlows": [ - { - "alias": "Account verification options", - "description": "Method with which to verity the existing account", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-email-verification", - "requirement": "ALTERNATIVE", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "ALTERNATIVE", - "priority": 20, - "flowAlias": "Verify Existing Account by Re-authentication", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "alias": "Authentication Options", - "description": "Authentication options.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "basic-auth", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "basic-auth-otp", - "requirement": "DISABLED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "auth-spnego", - "requirement": "DISABLED", - "priority": 30, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "alias": "Browser - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "auth-otp-form", - "requirement": "REQUIRED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "alias": "Direct Grant - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "direct-grant-validate-otp", - "requirement": "REQUIRED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "alias": "First broker login - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "auth-otp-form", - "requirement": "REQUIRED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "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", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-confirm-link", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "REQUIRED", - "priority": 20, - "flowAlias": "Account verification options", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "alias": "Reset - Conditional OTP", - "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "reset-otp", - "requirement": "REQUIRED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "alias": "User creation or linking", - "description": "Flow for the existing/non-existing user alternatives", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticatorConfig": "create unique user config", - "authenticator": "idp-create-user-if-unique", - "requirement": "ALTERNATIVE", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "ALTERNATIVE", - "priority": 20, - "flowAlias": "Handle Existing Account", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "alias": "Verify Existing Account by Re-authentication", - "description": "Reauthentication of existing account", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-username-password-form", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "CONDITIONAL", - "priority": 20, - "flowAlias": "First broker login - Conditional OTP", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "alias": "browser", - "description": "browser based authentication", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-cookie", - "requirement": "ALTERNATIVE", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "auth-spnego", - "requirement": "DISABLED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "identity-provider-redirector", - "requirement": "ALTERNATIVE", - "priority": 25, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "ALTERNATIVE", - "priority": 30, - "flowAlias": "forms", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "alias": "clients", - "description": "Base authentication for clients", - "providerId": "client-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "client-secret", - "requirement": "ALTERNATIVE", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "client-jwt", - "requirement": "ALTERNATIVE", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "client-secret-jwt", - "requirement": "ALTERNATIVE", - "priority": 30, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "client-x509", - "requirement": "ALTERNATIVE", - "priority": 40, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "alias": "direct grant", - "description": "OpenID Connect Resource Owner Grant", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "direct-grant-validate-username", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "direct-grant-validate-password", - "requirement": "REQUIRED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "CONDITIONAL", - "priority": 30, - "flowAlias": "Direct Grant - Conditional OTP", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "alias": "docker auth", - "description": "Used by Docker clients to authenticate against the IDP", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "docker-http-basic-authenticator", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "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", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticatorConfig": "review profile config", - "authenticator": "idp-review-profile", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "REQUIRED", - "priority": 20, - "flowAlias": "User creation or linking", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "alias": "forms", - "description": "Username, password, otp and other auth forms.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-username-password-form", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "CONDITIONAL", - "priority": 20, - "flowAlias": "Browser - Conditional OTP", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "alias": "http challenge", - "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "no-cookie-redirect", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "REQUIRED", - "priority": 20, - "flowAlias": "Authentication Options", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "alias": "registration", - "description": "registration flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-page-form", - "requirement": "REQUIRED", - "priority": 10, - "flowAlias": "registration form", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "alias": "registration form", - "description": "registration form", - "providerId": "form-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-user-creation", - "requirement": "REQUIRED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "registration-profile-action", - "requirement": "REQUIRED", - "priority": 40, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "registration-password-action", - "requirement": "REQUIRED", - "priority": 50, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "registration-recaptcha-action", - "requirement": "DISABLED", - "priority": 60, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "alias": "reset credentials", - "description": "Reset credentials for a user if they forgot their password or something", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "reset-credentials-choose-user", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "reset-credential-email", - "requirement": "REQUIRED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "reset-password", - "requirement": "REQUIRED", - "priority": 30, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "CONDITIONAL", - "priority": 40, - "flowAlias": "Reset - Conditional OTP", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "alias": "saml ecp", - "description": "SAML ECP Profile Authentication Flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "http-basic-authenticator", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - } - ], - "authenticatorConfig": [ - { - "alias": "create unique user config", - "config": { - "require.password.update.after.registration": "false" - } - }, - { - "alias": "review profile config", - "config": { - "update.profile.on.first.login": "missing" - } - } - ], - "requiredActions": [ - { - "alias": "CONFIGURE_TOTP", - "name": "Configure OTP", - "providerId": "CONFIGURE_TOTP", - "enabled": true, - "defaultAction": false, - "priority": 10, - "config": {} - }, - { - "alias": "terms_and_conditions", - "name": "Terms and Conditions", - "providerId": "terms_and_conditions", - "enabled": false, - "defaultAction": false, - "priority": 20, - "config": {} - }, - { - "alias": "UPDATE_PASSWORD", - "name": "Update Password", - "providerId": "UPDATE_PASSWORD", - "enabled": true, - "defaultAction": false, - "priority": 30, - "config": {} - }, - { - "alias": "UPDATE_PROFILE", - "name": "Update Profile", - "providerId": "UPDATE_PROFILE", - "enabled": true, - "defaultAction": false, - "priority": 40, - "config": {} - }, - { - "alias": "VERIFY_EMAIL", - "name": "Verify Email", - "providerId": "VERIFY_EMAIL", - "enabled": true, - "defaultAction": false, - "priority": 50, - "config": {} - }, - { - "alias": "update_user_locale", - "name": "Update User Locale", - "providerId": "update_user_locale", - "enabled": true, - "defaultAction": false, - "priority": 1000, - "config": {} - } - ], - "browserFlow": "browser", - "registrationFlow": "registration", - "directGrantFlow": "direct grant", - "resetCredentialsFlow": "reset credentials", - "clientAuthenticationFlow": "clients", - "dockerAuthenticationFlow": "docker auth", - "attributes": { - "clientOfflineSessionMaxLifespan": "0", - "clientSessionIdleTimeout": "0", - "clientSessionMaxLifespan": "0", - "clientOfflineSessionIdleTimeout": "0" - }, - "keycloakVersion": "11.0.0", - "userManagedAccessAllowed": false + ] } diff --git a/forms-flow-idm/realm-exports/Multi tenant realm.json b/forms-flow-idm/realm-exports/Multi tenant realm.json index 45f3a51891..a01dc26c0e 100644 --- a/forms-flow-idm/realm-exports/Multi tenant realm.json +++ b/forms-flow-idm/realm-exports/Multi tenant realm.json @@ -1,245 +1,215 @@ { - "roles":{ - "client":{ - "forms-flow-bpm":[ - - ], - "forms-flow-web":[ - { - "name":"camunda-admin", - "composite":false, - "clientRole":true, - "attributes":{ - - } - } - ] - } + "id": "multitenant", + "realm": "multitenant", + "loginTheme": "formsflow", + "roles": { + "client": { + "forms-flow-bpm": [], + "forms-flow-web": [ + { + "name": "camunda-admin", + "composite": false, + "clientRole": true, + "attributes": {} + } + ] + } }, - "groups":[ - { - "name":"camunda-admin", - "path":"/camunda-admin", - "attributes":{ - - }, - "realmRoles":[ - - ], - "clientRoles":{ - - }, - "subGroups":[ - - ] - } + "groups": [ + { + "name": "camunda-admin", + "path": "/camunda-admin", + "attributes": {}, + "realmRoles": [], + "clientRoles": {}, + "subGroups": [] + } ], - "users":[ - { - "username":"service-account-forms-flow-bpm", - "enabled":true, - "totp":false, - "emailVerified":false, - "serviceAccountClientId":"forms-flow-bpm", - "disableableCredentialTypes":[ - - ], - "requiredActions":[ - - ], - "clientRoles":{ - "realm-management":[ - "query-clients", - "realm-admin" - ], - "forms-flow-web":[ - "camunda-admin" - ] - }, - "notBefore":0, - "groups":[ - "camunda-admin" - ] - } + "users": [ + { + "username": "service-account-forms-flow-bpm", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "forms-flow-bpm", + "disableableCredentialTypes": [], + "requiredActions": [], + "clientRoles": { + "realm-management": ["query-clients", "realm-admin"], + "forms-flow-web": ["camunda-admin"] + }, + "notBefore": 0, + "groups": ["camunda-admin"] + } ], - "clients":[ - { - "clientId":"forms-flow-bpm", - "surrogateAuthRequired":false, - "enabled":true, - "alwaysDisplayInConsole":false, - "clientAuthenticatorType":"client-secret", - "secret":"786001d6-68a8-4519-903c-bc5b5a870d68", - "redirectUris":[ - "https://*" - ], - "webOrigins":[ - "*" - ], - "notBefore":0, - "bearerOnly":false, - "consentRequired":false, - "standardFlowEnabled":true, - "implicitFlowEnabled":false, - "directAccessGrantsEnabled":true, - "serviceAccountsEnabled":true, - "publicClient":false, - "frontchannelLogout":false, - "protocol":"openid-connect", - "attributes":{ - "saml.assertion.signature":"false", - "id.token.as.detached.signature":"false", - "saml.multivalued.roles":"false", - "saml.force.post.binding":"false", - "saml.encrypt":"false", - "oauth2.device.authorization.grant.enabled":"false", - "backchannel.logout.revoke.offline.tokens":"false", - "saml.server.signature":"false", - "saml.server.signature.keyinfo.ext":"false", - "use.refresh.tokens":"true", - "exclude.session.state.from.auth.response":"false", - "oidc.ciba.grant.enabled":"false", - "saml.artifact.binding":"false", - "backchannel.logout.session.required":"true", - "client_credentials.use_refresh_token":"false", - "saml_force_name_id_format":"false", - "saml.client.signature":"false", - "tls.client.certificate.bound.access.tokens":"false", - "saml.authnstatement":"false", - "display.on.consent.screen":"false", - "saml.onetimeuse.condition":"false" + "clients": [ + { + "clientId": "forms-flow-bpm", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "786001d6-68a8-4519-903c-bc5b5a870d68", + "redirectUris": ["https://*"], + "webOrigins": ["*"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "id.token.as.detached.signature": "false", + "saml.multivalued.roles": "false", + "saml.force.post.binding": "false", + "saml.encrypt": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "exclude.session.state.from.auth.response": "false", + "oidc.ciba.grant.enabled": "false", + "saml.artifact.binding": "false", + "backchannel.logout.session.required": "true", + "client_credentials.use_refresh_token": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } }, - "authenticationFlowBindingOverrides":{ - + { + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } }, - "fullScopeAllowed":true, - "nodeReRegistrationTimeout":-1, - "protocolMappers":[ - { - "name":"Client ID", - "protocol":"openid-connect", - "protocolMapper":"oidc-usersessionmodel-note-mapper", - "consentRequired":false, - "config":{ - "user.session.note":"clientId", - "userinfo.token.claim":"true", - "id.token.claim":"true", - "access.token.claim":"true", - "claim.name":"clientId", - "jsonType.label":"String" - } - }, - { - "name":"Client Host", - "protocol":"openid-connect", - "protocolMapper":"oidc-usersessionmodel-note-mapper", - "consentRequired":false, - "config":{ - "user.session.note":"clientHost", - "userinfo.token.claim":"true", - "id.token.claim":"true", - "access.token.claim":"true", - "claim.name":"clientHost", - "jsonType.label":"String" - } - }, - { - "name":"forms-flow-bpm aud", - "protocol":"openid-connect", - "protocolMapper":"oidc-audience-mapper", - "consentRequired":false, - "config":{ - "id.token.claim":"true", - "access.token.claim":"true", - "included.custom.audience":"forms-flow-bpm", - "userinfo.token.claim":"true" - } - }, - { - "name":"forms-flow-web aud", - "protocol":"openid-connect", - "protocolMapper":"oidc-audience-mapper", - "consentRequired":false, - "config":{ - "id.token.claim":"true", - "access.token.claim":"true", - "included.custom.audience":"forms-flow-web", - "userinfo.token.claim":"true" - } - }, - { - "name":"Client IP Address", - "protocol":"openid-connect", - "protocolMapper":"oidc-usersessionmodel-note-mapper", - "consentRequired":false, - "config":{ - "user.session.note":"clientAddress", - "userinfo.token.claim":"true", - "id.token.claim":"true", - "access.token.claim":"true", - "claim.name":"clientAddress", - "jsonType.label":"String" - } - } - ], - "defaultClientScopes":[ - "web-origins", - "roles", - "profile", - "email" - ], - "optionalClientScopes":[ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "clientId":"forms-flow-web", - "surrogateAuthRequired":false, - "enabled":true, - "alwaysDisplayInConsole":false, - "clientAuthenticatorType":"client-secret", - "redirectUris":[ - - ], - "webOrigins":[ - - ], - "notBefore":0, - "bearerOnly":false, - "consentRequired":false, - "standardFlowEnabled":true, - "implicitFlowEnabled":false, - "directAccessGrantsEnabled":true, - "serviceAccountsEnabled":false, - "publicClient":true, - "frontchannelLogout":false, - "protocol":"openid-connect", - "attributes":{ - "backchannel.logout.session.required":"true", - "backchannel.logout.revoke.offline.tokens":"false" + { + "name": "forms-flow-bpm aud", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "included.custom.audience": "forms-flow-bpm", + "userinfo.token.claim": "true" + } }, - "authenticationFlowBindingOverrides":{ - + { + "name": "forms-flow-web aud", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "included.custom.audience": "forms-flow-web", + "userinfo.token.claim": "true" + } }, - "fullScopeAllowed":true, - "nodeReRegistrationTimeout":-1, - "defaultClientScopes":[ - "web-origins", - "roles", - "profile", - "email" - ], - "optionalClientScopes":[ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - } - ] - -} \ No newline at end of file + { + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": ["web-origins", "roles", "profile", "email"], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "clientId": "forms-flow-web", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "backchannel.logout.session.required": "true", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": ["web-origins", "roles", "profile", "email"], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "components": { + "org.keycloak.userprofile.UserProfileProvider": [ + { + "providerId": "declarative-user-profile", + "subComponents": {}, + "config": { + "kc.user.profile.config": [ + "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false}],\"groups\":[{\"name\":\"user-metadata\",\"displayHeader\":\"User metadata\",\"displayDescription\":\"Attributes, which refer to user metadata\"}],\"unmanagedAttributePolicy\":\"ENABLED\"}" + ] + } + } + ] + } +} From 9eb745fa42193fb254bae2857de000d335d503f1 Mon Sep 17 00:00:00 2001 From: abilpraju-aot Date: Thu, 3 Oct 2024 01:45:05 -0700 Subject: [PATCH 06/39] bruteforce enabled --- .../keycloak/imports/formsflow-ai-realm.json | 106 +++++++++++++++++- .../realm-exports/Group based auth.json | 106 +++++++++++++++++- 2 files changed, 210 insertions(+), 2 deletions(-) diff --git a/forms-flow-idm/keycloak/imports/formsflow-ai-realm.json b/forms-flow-idm/keycloak/imports/formsflow-ai-realm.json index 86b7bebd51..92bf69bf68 100644 --- a/forms-flow-idm/keycloak/imports/formsflow-ai-realm.json +++ b/forms-flow-idm/keycloak/imports/formsflow-ai-realm.json @@ -3,6 +3,16 @@ "realm": "forms-flow-ai", "loginTheme": "formsflow", "enabled": true, + "registrationAllowed": true, + "bruteForceProtected": true, + "permanentLockout": false, + "maxTemporaryLockouts": 0, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 1800, + "failureFactor": 5, "roles": { "client": { "forms-flow-web": [ @@ -1220,5 +1230,99 @@ "address", "phone", "microprofile-jwt" - ] + ], + "eventsEnabled": true, + "eventsExpiration": 1800, + "eventsListeners": ["jboss-logging"], + "enabledEventTypes": [ + "UPDATE_CONSENT_ERROR", + "SEND_RESET_PASSWORD", + "GRANT_CONSENT", + "VERIFY_PROFILE_ERROR", + "UPDATE_TOTP", + "REMOVE_TOTP", + "REVOKE_GRANT", + "LOGIN_ERROR", + "CLIENT_LOGIN", + "RESET_PASSWORD_ERROR", + "IMPERSONATE_ERROR", + "CODE_TO_TOKEN_ERROR", + "CUSTOM_REQUIRED_ACTION", + "OAUTH2_DEVICE_CODE_TO_TOKEN_ERROR", + "RESTART_AUTHENTICATION", + "UPDATE_PROFILE_ERROR", + "IMPERSONATE", + "LOGIN", + "UPDATE_PASSWORD_ERROR", + "OAUTH2_DEVICE_VERIFY_USER_CODE", + "CLIENT_INITIATED_ACCOUNT_LINKING", + "USER_DISABLED_BY_PERMANENT_LOCKOUT", + "OAUTH2_EXTENSION_GRANT", + "TOKEN_EXCHANGE", + "REGISTER", + "LOGOUT", + "AUTHREQID_TO_TOKEN", + "DELETE_ACCOUNT_ERROR", + "CLIENT_REGISTER", + "IDENTITY_PROVIDER_LINK_ACCOUNT", + "USER_DISABLED_BY_TEMPORARY_LOCKOUT", + "UPDATE_PASSWORD", + "DELETE_ACCOUNT", + "FEDERATED_IDENTITY_LINK_ERROR", + "CLIENT_DELETE", + "IDENTITY_PROVIDER_FIRST_LOGIN", + "VERIFY_EMAIL", + "CLIENT_DELETE_ERROR", + "CLIENT_LOGIN_ERROR", + "RESTART_AUTHENTICATION_ERROR", + "REMOVE_FEDERATED_IDENTITY_ERROR", + "EXECUTE_ACTIONS", + "TOKEN_EXCHANGE_ERROR", + "PERMISSION_TOKEN", + "FEDERATED_IDENTITY_OVERRIDE_LINK", + "SEND_IDENTITY_PROVIDER_LINK_ERROR", + "EXECUTE_ACTION_TOKEN_ERROR", + "SEND_VERIFY_EMAIL", + "OAUTH2_EXTENSION_GRANT_ERROR", + "OAUTH2_DEVICE_AUTH", + "EXECUTE_ACTIONS_ERROR", + "REMOVE_FEDERATED_IDENTITY", + "OAUTH2_DEVICE_CODE_TO_TOKEN", + "IDENTITY_PROVIDER_POST_LOGIN", + "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", + "FEDERATED_IDENTITY_OVERRIDE_LINK_ERROR", + "UPDATE_EMAIL", + "OAUTH2_DEVICE_VERIFY_USER_CODE_ERROR", + "REGISTER_ERROR", + "REVOKE_GRANT_ERROR", + "LOGOUT_ERROR", + "UPDATE_EMAIL_ERROR", + "EXECUTE_ACTION_TOKEN", + "CLIENT_UPDATE_ERROR", + "UPDATE_PROFILE", + "AUTHREQID_TO_TOKEN_ERROR", + "INVITE_ORG_ERROR", + "FEDERATED_IDENTITY_LINK", + "CLIENT_REGISTER_ERROR", + "INVITE_ORG", + "SEND_VERIFY_EMAIL_ERROR", + "SEND_IDENTITY_PROVIDER_LINK", + "RESET_PASSWORD", + "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", + "OAUTH2_DEVICE_AUTH_ERROR", + "UPDATE_CONSENT", + "REMOVE_TOTP_ERROR", + "VERIFY_EMAIL_ERROR", + "SEND_RESET_PASSWORD_ERROR", + "CLIENT_UPDATE", + "IDENTITY_PROVIDER_POST_LOGIN_ERROR", + "CUSTOM_REQUIRED_ACTION_ERROR", + "UPDATE_TOTP_ERROR", + "CODE_TO_TOKEN", + "VERIFY_PROFILE", + "GRANT_CONSENT_ERROR", + "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR" + ], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false } diff --git a/forms-flow-idm/realm-exports/Group based auth.json b/forms-flow-idm/realm-exports/Group based auth.json index 86b7bebd51..92bf69bf68 100644 --- a/forms-flow-idm/realm-exports/Group based auth.json +++ b/forms-flow-idm/realm-exports/Group based auth.json @@ -3,6 +3,16 @@ "realm": "forms-flow-ai", "loginTheme": "formsflow", "enabled": true, + "registrationAllowed": true, + "bruteForceProtected": true, + "permanentLockout": false, + "maxTemporaryLockouts": 0, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 1800, + "failureFactor": 5, "roles": { "client": { "forms-flow-web": [ @@ -1220,5 +1230,99 @@ "address", "phone", "microprofile-jwt" - ] + ], + "eventsEnabled": true, + "eventsExpiration": 1800, + "eventsListeners": ["jboss-logging"], + "enabledEventTypes": [ + "UPDATE_CONSENT_ERROR", + "SEND_RESET_PASSWORD", + "GRANT_CONSENT", + "VERIFY_PROFILE_ERROR", + "UPDATE_TOTP", + "REMOVE_TOTP", + "REVOKE_GRANT", + "LOGIN_ERROR", + "CLIENT_LOGIN", + "RESET_PASSWORD_ERROR", + "IMPERSONATE_ERROR", + "CODE_TO_TOKEN_ERROR", + "CUSTOM_REQUIRED_ACTION", + "OAUTH2_DEVICE_CODE_TO_TOKEN_ERROR", + "RESTART_AUTHENTICATION", + "UPDATE_PROFILE_ERROR", + "IMPERSONATE", + "LOGIN", + "UPDATE_PASSWORD_ERROR", + "OAUTH2_DEVICE_VERIFY_USER_CODE", + "CLIENT_INITIATED_ACCOUNT_LINKING", + "USER_DISABLED_BY_PERMANENT_LOCKOUT", + "OAUTH2_EXTENSION_GRANT", + "TOKEN_EXCHANGE", + "REGISTER", + "LOGOUT", + "AUTHREQID_TO_TOKEN", + "DELETE_ACCOUNT_ERROR", + "CLIENT_REGISTER", + "IDENTITY_PROVIDER_LINK_ACCOUNT", + "USER_DISABLED_BY_TEMPORARY_LOCKOUT", + "UPDATE_PASSWORD", + "DELETE_ACCOUNT", + "FEDERATED_IDENTITY_LINK_ERROR", + "CLIENT_DELETE", + "IDENTITY_PROVIDER_FIRST_LOGIN", + "VERIFY_EMAIL", + "CLIENT_DELETE_ERROR", + "CLIENT_LOGIN_ERROR", + "RESTART_AUTHENTICATION_ERROR", + "REMOVE_FEDERATED_IDENTITY_ERROR", + "EXECUTE_ACTIONS", + "TOKEN_EXCHANGE_ERROR", + "PERMISSION_TOKEN", + "FEDERATED_IDENTITY_OVERRIDE_LINK", + "SEND_IDENTITY_PROVIDER_LINK_ERROR", + "EXECUTE_ACTION_TOKEN_ERROR", + "SEND_VERIFY_EMAIL", + "OAUTH2_EXTENSION_GRANT_ERROR", + "OAUTH2_DEVICE_AUTH", + "EXECUTE_ACTIONS_ERROR", + "REMOVE_FEDERATED_IDENTITY", + "OAUTH2_DEVICE_CODE_TO_TOKEN", + "IDENTITY_PROVIDER_POST_LOGIN", + "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", + "FEDERATED_IDENTITY_OVERRIDE_LINK_ERROR", + "UPDATE_EMAIL", + "OAUTH2_DEVICE_VERIFY_USER_CODE_ERROR", + "REGISTER_ERROR", + "REVOKE_GRANT_ERROR", + "LOGOUT_ERROR", + "UPDATE_EMAIL_ERROR", + "EXECUTE_ACTION_TOKEN", + "CLIENT_UPDATE_ERROR", + "UPDATE_PROFILE", + "AUTHREQID_TO_TOKEN_ERROR", + "INVITE_ORG_ERROR", + "FEDERATED_IDENTITY_LINK", + "CLIENT_REGISTER_ERROR", + "INVITE_ORG", + "SEND_VERIFY_EMAIL_ERROR", + "SEND_IDENTITY_PROVIDER_LINK", + "RESET_PASSWORD", + "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", + "OAUTH2_DEVICE_AUTH_ERROR", + "UPDATE_CONSENT", + "REMOVE_TOTP_ERROR", + "VERIFY_EMAIL_ERROR", + "SEND_RESET_PASSWORD_ERROR", + "CLIENT_UPDATE", + "IDENTITY_PROVIDER_POST_LOGIN_ERROR", + "CUSTOM_REQUIRED_ACTION_ERROR", + "UPDATE_TOTP_ERROR", + "CODE_TO_TOKEN", + "VERIFY_PROFILE", + "GRANT_CONSENT_ERROR", + "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR" + ], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false } From 2a71508b3dd9faed3e387f2f33a366b1a458c604 Mon Sep 17 00:00:00 2001 From: Sumesh Kariyil Date: Thu, 3 Oct 2024 14:21:29 -0700 Subject: [PATCH 07/39] Update Dockerfile --- forms-flow-bpm/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forms-flow-bpm/Dockerfile b/forms-flow-bpm/Dockerfile index e16398450b..5e5c91a374 100644 --- a/forms-flow-bpm/Dockerfile +++ b/forms-flow-bpm/Dockerfile @@ -17,14 +17,14 @@ COPY forms-flow-bpm-utils/src ./forms-flow-bpm-utils/src/ # RUN mvn -s /usr/share/maven/ref/settings-docker.xml dependency:resolve-plugins dependency:resolve dependency:go-offline -B -P camunda RUN mvn -s /usr/share/maven/ref/settings-docker.xml install -P camunda -# Final custom slim java image (for apk command see 17-jdk-alpine-slim) + FROM openjdk:21-ea-jdk # set label for image LABEL Name="formsflow" ENV JAVA_VERSION=21-ea+14 -ENV JAVA_HOME=/opt/java/openjdk-17\ +ENV JAVA_HOME=/opt/java/openjdk-21\ PATH="/opt/java/openjdk-21/bin:$PATH" EXPOSE 8080 From daf5b79abcf635737a7a465a33e93361fb6badf4 Mon Sep 17 00:00:00 2001 From: abilpraju-aot Date: Mon, 7 Oct 2024 03:00:44 -0700 Subject: [PATCH 08/39] validation api on form create and duplicate --- .../src/apiManager/endpoints/index.js | 1 + .../src/apiManager/services/FormServices.js | 12 +- .../src/components/Form/Item/Edit.js | 379 +++++++++++++++--- forms-flow-web/src/components/Form/List.js | 36 +- .../src/components/Modals/ActionModal.js | 11 +- 5 files changed, 382 insertions(+), 57 deletions(-) diff --git a/forms-flow-web/src/apiManager/endpoints/index.js b/forms-flow-web/src/apiManager/endpoints/index.js index 05751f8d80..0018f90624 100644 --- a/forms-flow-web/src/apiManager/endpoints/index.js +++ b/forms-flow-web/src/apiManager/endpoints/index.js @@ -80,6 +80,7 @@ const API = { GET_FILTERS : `${WEB_BASE_URL}/filter`, GET_BPM_TASK_FILTERS : `${BPM_BASE_URL_EXT}/v1/task-filters`, VALIDATE_TENANT: `${MT_ADMIN_BASE_URL}/${MT_ADMIN_BASE_URL_VERSION}/tenants//validate`, + VALIDATE_FORM_NAME: `${WEB_BASE_URL}/form/validate` }; export default API; diff --git a/forms-flow-web/src/apiManager/services/FormServices.js b/forms-flow-web/src/apiManager/services/FormServices.js index 64c5ed8577..6b48962f71 100644 --- a/forms-flow-web/src/apiManager/services/FormServices.js +++ b/forms-flow-web/src/apiManager/services/FormServices.js @@ -8,7 +8,6 @@ export const formCreate = (formData) => { }; export const formImport = (importData, data) => { - console.log("reached back", importData, data); return RequestService.httpMultipartPOSTRequest(API.FORM_IMPORT, importData, data); }; @@ -72,3 +71,14 @@ export const getCustomSubmission = (submissionId, formId, ...rest) => { }); }; }; + +export const validateFormName = (title, name, id) => { + let url = `${API.VALIDATE_FORM_NAME}?title=${title}`; + if (name) { + url += `&name=${encodeURIComponent(name)}`; + } + if (id) { + url += `&id=${encodeURIComponent(id)}`; + } + return RequestService.httpGETRequest(url); +}; diff --git a/forms-flow-web/src/components/Form/Item/Edit.js b/forms-flow-web/src/components/Form/Item/Edit.js index 4ca98bb431..f7d1fe5f1f 100644 --- a/forms-flow-web/src/components/Form/Item/Edit.js +++ b/forms-flow-web/src/components/Form/Item/Edit.js @@ -1,6 +1,6 @@ import React, { useReducer, useState, useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; -import { Card } from 'react-bootstrap'; +import { Card } from "react-bootstrap"; import { Errors, FormBuilder, Formio } from "@aot-technologies/formio-react"; import { BackToPrevIcon } from "@formsflow/components"; import ProcessDiagram from "../../BPMN/ProcessDiagramHook"; @@ -15,13 +15,15 @@ import { listProcess } from "../../../apiManager/services/formatterService"; import { push } from "connected-react-router"; import { HistoryIcon, PreviewIcon } from "@formsflow/components"; import ActionModal from "../../Modals/ActionModal.js"; -import { - MULTITENANCY_ENABLED, -} from "../../../constants/constants"; +import { MULTITENANCY_ENABLED } from "../../../constants/constants"; //for save form import { manipulatingFormData } from "../../../apiManager/services/formFormatterService"; -import { formUpdate } from "../../../apiManager/services/FormServices"; +import { handleAuthorization } from "../../../apiManager/services/authorizationService"; +import { formUpdate,validateFormName } from "../../../apiManager/services/FormServices"; import { INACTIVE } from "../constants/formListConstants"; +import { + formCreate +} from "../../../apiManager/services/FormServices"; import utils from "@aot-technologies/formiojs/lib/utils"; import { setFormFailureErrorData, @@ -36,7 +38,8 @@ import { import _isEquial from "lodash/isEqual"; import { toast } from "react-toastify"; - +import {FormBuilderModal} from "@formsflow/components"; +import _ from "lodash"; const reducer = (form, { type, value }) => { const formCopy = _cloneDeep(form); @@ -70,35 +73,48 @@ const Edit = React.memo(() => { const lang = useSelector((state) => state.user.lang); const { t } = useTranslation(); const errors = useSelector((state) => state.form?.error); - const processListData = useSelector((state) => state.process?.formProcessList); + const processListData = useSelector( + (state) => state.process?.formProcessList + ); const formData = useSelector((state) => state.form?.form); const [form, dispatchFormAction] = useReducer(reducer, _cloneDeep(formData)); - const publisText = processListData.status == "active" ? "Unpublish" : "Publish"; + const publisText = + processListData.status == "active" ? "Unpublish" : "Publish"; const [showFlow, setShowFlow] = useState(false); const [showLayout, setShowLayout] = useState(true); const tenantKey = useSelector((state) => state.tenants?.tenantId); const redirectUrl = MULTITENANCY_ENABLED ? `/tenant/${tenantKey}/` : "/"; - - //for save form + const [nameError, setNameError] = useState(""); + const formProcessList = useSelector(state => state.process?.formProcessList); + + //for save form const [formSubmitted, setFormSubmitted] = useState(false); const formAccess = useSelector((state) => state.user?.formAccess || []); - const submissionAccess = useSelector((state) => state.user?.submissionAccess || []); + const submissionAccess = useSelector( + (state) => state.user?.submissionAccess || [] + ); const previousData = useSelector((state) => state.process?.formPreviousData); const formDescription = form?.description; - const restoredFormData = useSelector((state) => state.formRestore?.restoredFormData); - const restoredFormId = useSelector((state) => state.formRestore?.restoredFormId); - const applicationCount = useSelector((state) => state.process?.applicationCount); + const restoredFormData = useSelector( + (state) => state.formRestore?.restoredFormData + ); + const restoredFormId = useSelector( + (state) => state.formRestore?.restoredFormId + ); + const applicationCount = useSelector( + (state) => state.process?.applicationCount + ); const [showSaveModal, setShowSaveModal] = useState(false); const [hasRendered, setHasRendered] = useState(false); - - //action modal + const [showBuildForm, setShowBuildForm] = useState(false); + const [newActionModal, setNewActionModal] = useState(false); const onCloseActionModal = () => { - setNewActionModal(false); + setNewActionModal(false); }; const CategoryType = { FORM: "FORM", - WORKFLOW: "WORKFLOW" + WORKFLOW: "WORKFLOW", }; useEffect(() => { @@ -107,7 +123,6 @@ const Edit = React.memo(() => { } }, [showFlow]); - const handleShowLayout = () => { setShowFlow(false); setShowLayout(true); @@ -116,8 +131,29 @@ const Edit = React.memo(() => { setShowFlow(true); setShowLayout(false); }; - - //for save farm + + const validateFormNameOnBlur = () => { + if (!form.title || form.title.trim() === "") { + setNameError("This field is required"); + return; + } + + validateFormName(form.title) + .then((response) => { + const data = response?.data; + if (data && data.code === "FORM_EXISTS") { + setNameError(data.message); // Set exact error message + } else { + setNameError(""); + } + }) + .catch((error) => { + const errorMessage = error.response?.data?.message || "An error occurred while validating the form name."; + setNameError(errorMessage); // Set the error message from the server + console.error("Error validating form name:", errorMessage); + }); + }; + //for save farm const isMapperSaveNeeded = (newData) => { // checks if the updates need to save to form_process_mapper too return ( @@ -281,6 +317,11 @@ const Edit = React.memo(() => { console.log("saveFlow"); }; + const onCloseBuildModal = () => { + setShowBuildForm(false); + setNameError(""); + setFormSubmitted(false); + }; const saveLayout = () => { setShowSaveModal(true); }; @@ -299,19 +340,172 @@ const Edit = React.memo(() => { const handlePublish = () => { console.log("publish"); }; + const ActionType = { + DUPLICATE: "DUPLICATE", + SAVE_AS_TEMPLATE: "SAVE_AS_TEMPLATE", + IMPORT: "IMPORT", + EXPORT: "EXPORT", + DELETE: "DELETE", + }; + // const handleImport = async (fileContent, UploadActionType) => { + // setImportLoader(true); + // let data = {}; + // switch (UploadActionType) { + // case "validate": + // data = { + // importType: "new", + // action: "validate", + // }; + // break; + // case "import": + // setFormSubmitted(true); + // data = { + // importType: "new", + // action: "import", + // }; + // break; + // default: + // console.error("Invalid UploadActionType provided"); + // return; + // } + + // const dataString = JSON.stringify(data); + // formImport(fileContent, dataString) + // .then((res) => { + // console.log(res); + // setImportLoader(false); + // setFormSubmitted(false); + + // if (data.action == "validate") { + // FileService.extractFormDetails(fileContent, (formExtracted) => { + // if (formExtracted) { + // setFormTitle(formExtracted.formTitle); + // setUploadFormDescription(formExtracted.formDescription); + // } else { + // console.log("No valid form found."); + // } + // }); + // } else { + // dispatch(push(`${redirectUrl}formflow/${form._id}/edit/`)); + // } + // }) + // .catch((err) => { + // setImportLoader(false); + // setFormSubmitted(false); + // setImportError(err?.response?.data?.message); + // }); + // }; + + const handleChange = (path, event) => { + setFormSubmitted(false); + const { target } = event; + const value = target.type === "checkbox" ? target.checked : target.value; + value == "" ? setNameError("This field is required") : setNameError(""); + + dispatchFormAction({ type: path, value }); + }; + const handleAction = (actionType) => { + switch (actionType) { + case ActionType.DUPLICATE: + setShowBuildForm(true); + break; + // case ActionType.SAVE_AS_TEMPLATE: + // setImportFormModal(true); + // break; + // case ActionType.IMPORT: + // setImportFormModal(true); + // break; + // case ActionType.EXPORT: + // setImportFormModal(true); + // break; + // case ActionType.DELETE: + // setImportFormModal(true); + // break; + default: + break; + } + }; + const handlePublishAsNewVersion = ()=>{ + const newFormData = manipulatingFormData(_.cloneDeep(form), + MULTITENANCY_ENABLED, + tenantKey, + formAccess, + submissionAccess + ); + const newPathAndName = "duplicate-version-" + Math.random().toString(16).slice(9); + newFormData.path = newPathAndName; + newFormData.title = form.title; + newFormData.name = form.title; + newFormData.componentChanged = true; + delete newFormData.machineName; + delete newFormData.parentFormId; + newFormData.newVersion = true; + delete newFormData._id; + formCreate(newFormData) + .then((res) => { + const form = res.data; + const columnsToPick = [ + "anonymous", + "status", + "taskVariable", + "tags", + "components", + "processKey", + "processName", + ]; + const data = _.pick(formProcessList, columnsToPick); + data.parentFormId = form._id; + data.formId = form._id; + data.formName = form.title; + data.status = data.status || INACTIVE; + data.formType = form.type; + data.formRevisionNumber = "V1"; + data.formTypeChanged = true; + data.titleChanged = true; + data.anonymousChanged = true; + + Formio.cache = {}; + const payload = { + resourceId:data.formId, + resourceDetails: {}, + roles : [] + }; + + handleAuthorization( { application: payload, designer: payload, form: payload } + ,data.formId).catch((err)=>console.error(err)); + + dispatch(setFormSuccessData("form", form)); + dispatch( + // eslint-disable-next-line no-unused-vars + saveFormProcessMapperPost(data, (err, res) => { + if (!err) { + toast.success(t("Duplicate form created successfully")); + dispatch(push(`${redirectUrl}formflow/${form._id}/view-edit/`)); + } else { + toast.error(t("Error in creating form process mapper")); + } + }) + ); + }) + .catch((err) => { + let error = ""; + if (err.response?.data) { + error = err.response.data; + } else { + error = err.message; + } + dispatch(setFormFailureErrorData("form", error)); + }); + }; const formChange = (newForm) => dispatchFormAction({ type: "formChange", value: newForm }); return (
- + @@ -320,7 +514,10 @@ const Edit = React.memo(() => {
{form.title}
- + {processListData.status == "active" ? ( <>
@@ -328,7 +525,9 @@ const Edit = React.memo(() => { ) : (
)} - {processListData.status == "active" ? t("Live") : t("Draft")} + {processListData.status == "active" + ? t("Live") + : t("Draft")}
@@ -362,10 +561,15 @@ const Edit = React.memo(() => {
-
+
-
+
Layout
@@ -395,7 +599,9 @@ const Edit = React.memo(() => { variant="primary" size="md" className="mx-2" - label={{(t) => t("Save Layout")}} + label={ + {(t) => t("Save Layout")} + } onClick={saveLayout} dataTestid="save-form-layout" ariaLabel={t("Save Form Layout")} @@ -403,14 +609,17 @@ const Edit = React.memo(() => { {(t) => t("Discard Changes")}} + label={ + + {(t) => t("Discard Changes")} + + } onClick={discardChanges} dataTestid="discard-button-testid" ariaLabel={t("cancelBtnariaLabel")} />
-
@@ -427,10 +636,13 @@ const Edit = React.memo(() => {
-
+
-
+
Flow
@@ -438,7 +650,9 @@ const Edit = React.memo(() => { variant="secondary" size="md" icon={} - label={{(t) => t("History")}} + label={ + {(t) => t("History")} + } onClick={handleHistory} dataTestid="flow-history-button-testid" ariaLabel={t("Flow History Button")} @@ -447,7 +661,11 @@ const Edit = React.memo(() => { variant="secondary" size="md" className="mx-2" - label={{(t) => t("Preview & Variables")}} + label={ + + {(t) => t("Preview & Variables")} + + } onClick={handlePreviewAndVariables} dataTestid="preview-and-variables-testid" ariaLabel={t("{Preview and Variables Button}")} @@ -468,7 +686,9 @@ const Edit = React.memo(() => { variant="primary" size="md" className="mx-2" - label={{(t) => t("Save Flow")}} + label={ + {(t) => t("Save Flow")} + } onClick={saveFlow} dataTestid="save-flow-layout" ariaLabel={t("Save Flow Layout")} @@ -476,7 +696,11 @@ const Edit = React.memo(() => { {(t) => t("Discard Changes")}} + label={ + + {(t) => t("Discard Changes")} + + } onClick={discardChanges} dataTestid="discard-flow-changes-testid" ariaLabel={t("Discard Flow Changes")} @@ -492,39 +716,94 @@ const Edit = React.memo(() => { processKey={workflow?.value} tenant={workflow?.tenant} /> -
) : ""} +
+ ) : ( + "" + )}
- {showFlow &&
Layout
} - {showLayout &&
Flow
} + {showFlow && ( +
+ Layout +
+ )} + {showLayout && ( +
+ Flow +
+ )} {/* {showLayout &&
Flow
} */}
-
- + + {/* */} {(t) => t("Save Your Changes")}} - message={{(t) => t("Saving as an incrimental version will affect previous submissions. Saving as a new full version will not affect previous submissions.")}} + message={ + + {(t) => + t( + "Saving as an incrimental version will affect previous submissions. Saving as a new full version will not affect previous submissions." + ) + } + + } primaryBtnAction={saveFormData} onClose={closeSaveModal} secondayBtnAction={"add secondary button action"} - primaryBtnText={{(t) => t("Save as Version 3.5")}} - secondaryBtnText={{(t) => t("Save as Version 4.0")}} + primaryBtnText={ + {(t) => t("Save as Version 3.5")} + } + secondaryBtnText={ + {(t) => t("Save as Version 4.0")} + } size="md" /> -
+
); }); -export default Edit; \ No newline at end of file +export default Edit; diff --git a/forms-flow-web/src/components/Form/List.js b/forms-flow-web/src/components/Form/List.js index 54f95c9292..f93c06c0f6 100644 --- a/forms-flow-web/src/components/Form/List.js +++ b/forms-flow-web/src/components/Form/List.js @@ -1,7 +1,7 @@ import React, { useEffect, useState, useReducer } from "react"; import { connect, useSelector, useDispatch } from "react-redux"; import CreateFormModal from "../Modals/CreateFormModal.js"; -import BuildFormModal from '../Modals/BuildFormModal'; +//import BuildFormModal from '../Modals/BuildFormModal'; import ImportFormModal from "../Modals/ImportFormModal.js"; import { push } from "connected-react-router"; import { toast } from "react-toastify"; @@ -36,7 +36,7 @@ import { CustomButton } from "@formsflow/components"; import _set from "lodash/set"; import _cloneDeep from "lodash/cloneDeep"; import _camelCase from "lodash/camelCase"; -import { formCreate, formImport } from "../../apiManager/services/FormServices"; +import { formCreate, formImport,validateFormName } from "../../apiManager/services/FormServices"; import { addHiddenApplicationComponent } from "../../constants/applicationComponent"; import { setFormSuccessData } from "../../actions/formActions"; import { handleAuthorization } from "../../apiManager/services/authorizationService"; @@ -44,7 +44,7 @@ import { saveFormProcessMapperPost } from "../../apiManager/services/processServ import { CustomSearch } from "@formsflow/components"; import userRoles from "../../constants/permissions.js"; import FileService from "../../services/FileService"; - +import {FormBuilderModal} from "@formsflow/components"; const reducer = (form, { type, value }) => { @@ -251,6 +251,28 @@ const List = React.memo((props) => { return errors; }; + const validateFormNameOnBlur = () => { + if (!form.title || form.title.trim() === "") { + setNameError("This field is required"); + return; + } + + validateFormName(form.title) + .then((response) => { + const data = response?.data; + if (data && data.code === "FORM_EXISTS") { + setNameError(data.message); // Set exact error message + } else { + setNameError(""); + } + }) + .catch((error) => { + const errorMessage = error.response?.data?.message || "An error occurred while validating the form name."; + setNameError(errorMessage); // Set the error message from the server + console.error("Error validating form name:", errorMessage); + }); + }; + const handleChange = (path, event) => { setFormSubmitted(false); const { target } = event; @@ -381,15 +403,19 @@ const List = React.memo((props) => { onClose={onClose} onAction={handleAction} /> - { +const ActionModal = React.memo(({ newActionModal, onClose, CategoryType, onAction }) => { + const handleAction = (actionType) => { + onAction(actionType); + onClose(); // Close the modal after action is triggered + }; return ( <> @@ -27,6 +31,7 @@ const ActionModal = React.memo(({ newActionModal, onClose, CategoryType }) => { className="" dataTestid="duplicate-form-button" ariaLabel="Duplicate Button" + onClick={() => handleAction("DUPLICATE")} /> { className="" dataTestid="save-template-button" ariaLabel="Save as Template" + onClick={() => handleAction("SAVE_AS_TEMPLATE")} /> { className="" dataTestid="import-form-button" ariaLabel="Import Form" + onClick={() => handleAction("IMPORT")} /> { className="" dataTestid="export-form-button" ariaLabel="Export Form" + onClick={() => handleAction("EXPORT")} /> { className="" dataTestid="delete-form-button" ariaLabel="Delete Form" + onClick={() => handleAction("DELETE")} />
From 03f0ad1c5e53d4599d6730154b3f1e2afeac41a2 Mon Sep 17 00:00:00 2001 From: shuhaib-aot Date: Tue, 8 Oct 2024 13:23:11 +0530 Subject: [PATCH 09/39] FWF-3679 [feature] new migration file created and subflow creation and updation code modified --- .../versions/8feb43e1e408_subflow_table.py | 34 +++ .../src/formsflow_api/constants/__init__.py | 4 + .../src/formsflow_api/models/process.py | 49 +++-- .../src/formsflow_api/resources/process.py | 26 ++- .../src/formsflow_api/schemas/process.py | 11 - .../services/external/keycloak.py | 2 +- .../services/form_process_mapper.py | 16 +- .../formsflow_api/services/import_support.py | 2 +- .../src/formsflow_api/services/process.py | 200 ++++++++++++++---- 9 files changed, 263 insertions(+), 81 deletions(-) create mode 100644 forms-flow-api/migrations/versions/8feb43e1e408_subflow_table.py diff --git a/forms-flow-api/migrations/versions/8feb43e1e408_subflow_table.py b/forms-flow-api/migrations/versions/8feb43e1e408_subflow_table.py new file mode 100644 index 0000000000..55b533aa35 --- /dev/null +++ b/forms-flow-api/migrations/versions/8feb43e1e408_subflow_table.py @@ -0,0 +1,34 @@ +"""subflow table + +Revision ID: 8feb43e1e408 +Revises: 069086882f6c +Create Date: 2024-10-08 13:22:27.369462 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '8feb43e1e408' +down_revision = '069086882f6c' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('process', sa.Column('status_changed', sa.Boolean(), nullable=True)) + op.drop_index('ix_process_process_type', table_name='process') + op.drop_constraint('process_form_process_mapper_id_fkey', 'process', type_='foreignkey') + op.drop_column('process', 'form_process_mapper_id') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('process', sa.Column('form_process_mapper_id', sa.INTEGER(), autoincrement=False, nullable=True)) + op.create_foreign_key('process_form_process_mapper_id_fkey', 'process', 'form_process_mapper', ['form_process_mapper_id'], ['id']) + op.create_index('ix_process_process_type', 'process', ['process_type'], unique=False) + op.drop_column('process', 'status_changed') + # ### end Alembic commands ### diff --git a/forms-flow-api/src/formsflow_api/constants/__init__.py b/forms-flow-api/src/formsflow_api/constants/__init__.py index d482d0b19d..f8b8796891 100644 --- a/forms-flow-api/src/formsflow_api/constants/__init__.py +++ b/forms-flow-api/src/formsflow_api/constants/__init__.py @@ -59,6 +59,10 @@ class BusinessErrorCode(ErrorCodeMixin, Enum): FILTER_NOT_FOUND = "The specified filter does not exist", HTTPStatus.BAD_REQUEST PROCESS_START_ERROR = "Cannot start process instance", HTTPStatus.BAD_REQUEST USER_NOT_FOUND = "User not found", HTTPStatus.BAD_REQUEST + INVALID_PROCESS_DATA = ( + "Invalid process data passed; both data and process type are required", + HTTPStatus.BAD_REQUEST, + ) PROCESS_ID_NOT_FOUND = ( "The specified process ID does not exist", HTTPStatus.BAD_REQUEST, diff --git a/forms-flow-api/src/formsflow_api/models/process.py b/forms-flow-api/src/formsflow_api/models/process.py index 32663045c7..a4829a77d1 100644 --- a/forms-flow-api/src/formsflow_api/models/process.py +++ b/forms-flow-api/src/formsflow_api/models/process.py @@ -11,7 +11,7 @@ validate_sort_order_and_order_by, ) from formsflow_api_utils.utils.user_context import UserContext, user_context -from sqlalchemy import LargeBinary, desc, or_ +from sqlalchemy import LargeBinary, and_, desc, or_ from sqlalchemy.dialects.postgresql import ENUM from sqlalchemy.sql.expression import text @@ -42,13 +42,12 @@ class Process(AuditDateTimeMixin, AuditUserMixin, BaseModel, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) - process_type = db.Column( - ENUM(ProcessType, name="ProcessType"), nullable=False, index=True - ) + process_type = db.Column(ENUM(ProcessType, name="ProcessType"), nullable=False) process_data = db.Column(LargeBinary, nullable=False) - status = db.Column(ENUM(ProcessStatus, name="ProcessStatus"), nullable=False) - form_process_mapper_id = db.Column( - db.Integer, db.ForeignKey("form_process_mapper.id"), nullable=True + status = db.Column( + ENUM(ProcessStatus, name="ProcessStatus"), + nullable=False, + default=ProcessStatus.DRAFT, ) tenant = db.Column(db.String(100), nullable=True) major_version = db.Column(db.Integer, nullable=False, index=True) @@ -56,6 +55,7 @@ class Process(AuditDateTimeMixin, AuditUserMixin, BaseModel, db.Model): process_key = db.Column(db.String) parent_process_key = db.Column(db.String) is_subflow = db.Column(db.Boolean, default=False) + status_changed = db.Column(db.Boolean, default=False) @classmethod @user_context @@ -77,7 +77,6 @@ def update(self, process_info: dict): "status", "process_data", "modified_by", - "form_process_mapper_id", "modified", "major_version", "minor_version", @@ -134,10 +133,23 @@ def find_all_process( return query.items, total_count @classmethod - def get_latest_version(cls, process_name): + def get_latest_version_by_key(cls, process_key): + """Get latest version of process.""" + query = ( + cls.auth_query(cls.query.filter(cls.process_key == process_key)) + .order_by(cls.major_version.desc(), cls.minor_version.desc()) + .first() + ) + + return query + + @classmethod + def get_latest_version_by_parent_key(cls, parent_process_key): """Get latest version of process.""" query = ( - cls.auth_query(cls.query.filter(cls.name == process_name)) + cls.auth_query( + cls.query.filter(cls.parent_process_key == parent_process_key) + ) .order_by(cls.major_version.desc(), cls.minor_version.desc()) .first() ) @@ -145,15 +157,20 @@ def get_latest_version(cls, process_name): return query @classmethod - def fetch_histories_by_process_name( - cls, process_name: str, page_no=None, limit=None + def fetch_histories_by_parent_process_key( + cls, parent_process_key: str, page_no=None, limit=None ) -> List[Process]: """Fetch all versions (histories) of a process by process_name.""" - assert process_name is not None + assert parent_process_key is not None - query = cls.auth_query(cls.query.filter(cls.name == process_name)).order_by( - desc(cls.major_version), desc(cls.minor_version) - ) + query = cls.auth_query( + cls.query.filter( + and_( + cls.parent_process_key == parent_process_key, + cls.status_changed.is_(False), + ) + ) + ).order_by(desc(cls.major_version), desc(cls.minor_version)) total_count = query.count() limit = total_count if limit is None else limit query = query.paginate(page=page_no, per_page=limit, error_out=False) diff --git a/forms-flow-api/src/formsflow_api/resources/process.py b/forms-flow-api/src/formsflow_api/resources/process.py index a9d56d80a4..76252af859 100644 --- a/forms-flow-api/src/formsflow_api/resources/process.py +++ b/forms-flow-api/src/formsflow_api/resources/process.py @@ -195,13 +195,18 @@ def get(): ) def post(): """Create process data.""" - response = ProcessService.create_process(request.get_json()) + data = request.get_json() + process_data = data.get("processData") + process_type = data.get("processType") + response = ProcessService.create_process( + process_data=process_data, process_type=process_type, is_subflow=True + ) return response, HTTPStatus.CREATED @cors_preflight("GET, PUT, DELETE, OPTIONS") @API.route("/", methods=["GET", "PUT", "DELETE", "OPTIONS"]) -@API.doc(params={"process_id": "Process data corresponding to process_id"}) +@API.doc(params={"process_id": "Process data corresponding to process id"}) class ProcessResourceById(Resource): """Resource for managing process by id.""" @@ -217,7 +222,7 @@ class ProcessResourceById(Resource): model=process_response, ) def get(process_id: str): - """Get process data by id.""" + """Get process data by process key, here the process id is actually process_key.""" response, status = ProcessService.get_process_by_key(process_id), HTTPStatus.OK return response, status @@ -236,8 +241,15 @@ def get(process_id: str): @API.expect(process_request) def put(process_id: str): """Update process data by id.""" + data = request.get_json() + process_data = data.get("processData") + process_type = data.get("processType") response, status = ( - ProcessService.update_process(process_id, request.get_json()), + ProcessService.update_process( + process_id=process_id, + process_type=process_type, + process_data=process_data, + ), HTTPStatus.OK, ) return response, status @@ -260,7 +272,7 @@ def delete(process_id: str): @cors_preflight("GET, OPTIONS") @API.route( - "/process-history//versions", methods=["GET", "OPTIONS"] + "/process-history//versions", methods=["GET", "OPTIONS"] ) class ProcessHistoryResource(Resource): """Resource for retrieving process history.""" @@ -288,12 +300,12 @@ class ProcessHistoryResource(Resource): }, model=process_history_response_model, ) - def get(process_name: str): + def get(parent_process_key: str): """Get history for a process by process_name.""" # Retrieve all history related to the specified process process_history, count = ProcessService.get_all_history( - process_name, request.args + parent_process_key, request.args ) return ( ( diff --git a/forms-flow-api/src/formsflow_api/schemas/process.py b/forms-flow-api/src/formsflow_api/schemas/process.py index 36b191e5c5..1585dd356d 100644 --- a/forms-flow-api/src/formsflow_api/schemas/process.py +++ b/forms-flow-api/src/formsflow_api/schemas/process.py @@ -35,7 +35,6 @@ class Meta: # pylint: disable=too-few-public-methods process_data = fields.Method( "get_process_data", data_key="processData", dump_only=True ) - form_process_mapper_id = fields.Int(data_key="formProcessMapperId", allow_none=True) tenant = fields.Str(dump_only=True) created = fields.Str(dump_only=True) modified = fields.Str(dump_only=True) @@ -120,18 +119,8 @@ class Meta: # pylint: disable=too-few-public-methods unknown = EXCLUDE - name = fields.Str(required=True) process_type = fields.Str(data_key="processType", required=True) process_data = fields.Str(data_key="processData", required=True) - status = fields.Str(required=True) - form_process_mapper_id = fields.Int( - data_key="formProcessMapperId", required=False, allow_none=True - ) - major_version = fields.Int(data_key="majorVersion") - minor_version = fields.Int(data_key="minorVersion") - is_subflow = fields.Bool(data_key="isSubflow") - process_key = fields.Str(data_key="processKey") - parent_process_key = fields.Str(data_key="parentProcessKey") def load(self, data, *args, **kwargs): """Load method for deserializing data.""" diff --git a/forms-flow-api/src/formsflow_api/services/external/keycloak.py b/forms-flow-api/src/formsflow_api/services/external/keycloak.py index 799a272298..39a7fb7b35 100644 --- a/forms-flow-api/src/formsflow_api/services/external/keycloak.py +++ b/forms-flow-api/src/formsflow_api/services/external/keycloak.py @@ -4,13 +4,13 @@ import requests from flask import current_app +from formsflow_api_utils.exceptions import BusinessException from formsflow_api_utils.utils import ( HTTP_TIMEOUT, UserContext, profiletime, user_context, ) -from formsflow_api_utils.exceptions import BusinessException from formsflow_api.constants import BusinessErrorCode diff --git a/forms-flow-api/src/formsflow_api/services/form_process_mapper.py b/forms-flow-api/src/formsflow_api/services/form_process_mapper.py index 615c9564f7..7b75626f21 100644 --- a/forms-flow-api/src/formsflow_api/services/form_process_mapper.py +++ b/forms-flow-api/src/formsflow_api/services/form_process_mapper.py @@ -639,7 +639,7 @@ def publish(self, mapper_id, **kwargs): process_name = mapper.process_key # Fetch process data from process table - process = Process.get_latest_version(process_name) + process = Process.get_latest_version_by_key(process_name) process_data = process.process_data if process else None # Deploy process self.deploy_process(process_name, process_data, tenant_key, token) @@ -662,7 +662,12 @@ def publish(self, mapper_id, **kwargs): # Capture publish(active) status in form history table. self.capture_form_history(mapper, {"status": "active"}, user_name) # Update status in mapper table - mapper.update({"status": str(FormProcessMapperStatus.ACTIVE.value), "prompt_new_version": False}) + mapper.update( + { + "status": str(FormProcessMapperStatus.ACTIVE.value), + "prompt_new_version": False, + } + ) return {} @user_context @@ -675,5 +680,10 @@ def unpublish(self, mapper_id: int, **kwargs): # Capture publish status in form history table. self.capture_form_history(mapper, {"status": "inactive"}, user_name) # Update status(inactive) in mapper table - mapper.update({"status": str(FormProcessMapperStatus.INACTIVE.value), "prompt_new_version": True}) + mapper.update( + { + "status": str(FormProcessMapperStatus.INACTIVE.value), + "prompt_new_version": True, + } + ) return {} diff --git a/forms-flow-api/src/formsflow_api/services/import_support.py b/forms-flow-api/src/formsflow_api/services/import_support.py index da5a15348a..1791fd7828 100644 --- a/forms-flow-api/src/formsflow_api/services/import_support.py +++ b/forms-flow-api/src/formsflow_api/services/import_support.py @@ -59,7 +59,7 @@ def create_authorization(self, data): def get_latest_version_workflow(self, process_name, include_status=False): """Get latest version of workflow by process name.""" - process = Process.get_latest_version(process_name) + process = Process.get_latest_version_by_key(process_name) # If process not found, consider as initial version if not process: return (1, 0, None) if include_status else (1, 0) diff --git a/forms-flow-api/src/formsflow_api/services/process.py b/forms-flow-api/src/formsflow_api/services/process.py index ee3b3dfec5..146580456b 100644 --- a/forms-flow-api/src/formsflow_api/services/process.py +++ b/forms-flow-api/src/formsflow_api/services/process.py @@ -1,10 +1,12 @@ """This exposes process service.""" import json +import xml.etree.ElementTree as ET from flask import current_app from formsflow_api_utils.exceptions import BusinessException from formsflow_api_utils.utils.user_context import UserContext, user_context +from lxml import etree from formsflow_api.constants import BusinessErrorCode from formsflow_api.models import Process @@ -12,7 +14,6 @@ ProcessDataSchema, ProcessHistorySchema, ProcessListRequestSchema, - ProcessRequestSchema, ) processSchema = ProcessDataSchema() @@ -21,6 +22,11 @@ class ProcessService: # pylint: disable=too-few-public-methods """This class manages process service.""" + @classmethod + def _xml_parser(cls, process_data): + """Parse the process data.""" + return ET.fromstring(process_data.encode("utf-8")) + @classmethod def get_all_process(cls, request_args): # pylint:disable=too-many-locals """Get all process list.""" @@ -59,68 +65,178 @@ def get_all_process(cls, request_args): # pylint:disable=too-many-locals ) return processSchema.dump(process, many=True), count + @classmethod + def _upate_process_name_and_id(cls, xml_data, process_name): + """Parse the workflow XML data & update process name.""" + current_app.logger.info("Updating workflow...") + # pylint: disable=I1101 + root = cls._xml_parser(xml_data) + + # Find the bpmn:process element + process = root.find(".//{http://www.omg.org/spec/BPMN/20100524/MODEL}process") + if process is not None: + process.set("id", process_name) + process.set("name", process_name) + + # Convert the XML tree back to a string + updated_xml = etree.tostring( + root, pretty_print=True, encoding="unicode", xml_declaration=False + ) + # Prepend the XML declaration + updated_xml = '\n' + updated_xml + return updated_xml + @classmethod @user_context - def create_process(cls, payload, **kwargs): + def create_process( # pylint: disable=too-many-arguments, too-many-positional-arguments + cls, + process_data=None, + process_type=None, + process_name=None, + process_key=None, + is_subflow=False, + **kwargs, + ): """Save process data.""" user: UserContext = kwargs["user"] tenant_key = user.tenant_key current_app.logger.debug("Save process data..") - data = ProcessRequestSchema().load(payload) - process_data = data.get("process_data").encode("utf-8") + + if process_data is None or process_type is None: + raise BusinessException(BusinessErrorCode.INVALID_PROCESS_DATA) + + # Process the data name and key based on the process type and subflow status + process_data, process_name, process_key = cls._process_data_name_and_key( + process_data=process_data, + process_type=process_type, + is_subflow=is_subflow, + process_name=process_name, + process_key=process_key, + ) + + # Check if the process already exists if it is a subflow + if is_subflow: + if Process.find_process_by_name_key( + name=process_name, process_key=process_key + ): + raise BusinessException(BusinessErrorCode.PROCESS_EXISTS) + + # Initialize version numbers for the new process + major_version, minor_version = 1, 0 + + # Create a new process instance process = Process( - name=data.get("name"), - process_type=data.get("process_type").upper(), - status=data.get("status").upper(), + name=process_name, + process_type=process_type.upper(), tenant=tenant_key, process_data=process_data, - form_process_mapper_id=data.get("form_process_mapper_id"), created_by=user.user_name, - major_version=data.get("major_version"), - minor_version=data.get("minor_version"), - is_subflow=data.get("is_subflow"), - process_key=data.get("process_key"), - parent_process_key=data.get("parent_process_key"), + major_version=major_version, + minor_version=minor_version, + is_subflow=is_subflow, + process_key=process_key, + parent_process_key=process_key, ) - if process: - process.save() - process_data = processSchema.dump(process) - return process_data - raise BusinessException(BusinessErrorCode.PROCESS_ID_NOT_FOUND) + + # Save the new process to the database + process.save() + + # Return the serialized process data + return processSchema.dump(process) @classmethod - def get_process_by_key(cls, process_id): + def _process_data_name_and_key( # pylint: disable=too-many-arguments, too-many-positional-arguments + cls, + process_data=None, + process_type=None, + is_subflow=False, + process_name=None, + process_key=None, + ): + """Process data name key.""" + # if the process is not a subflow, update the process name and ID in the XML data + # if the process is a subflow, parse the XML data to extract the process name and key + # if the process is of type LOWCODE, convert the process data to JSON format + if is_subflow: + # Parse the XML data to extract process name and key for subflows + parsed_data = cls._xml_parser(process_data) + process_data = process_data.encode("utf-8") + process_name, process_key = parsed_data.get("name"), parsed_data.get("id") + else: + if process_type.upper() == "LOWCODE": + # Convert process data to JSON format for LOWCODE type processes + process_data = json.dumps(process_data) + else: + # Update the process name and ID in the XML data for other process types + process_data = cls._upate_process_name_and_id( + process_data, process_name + ) + return process_data, process_name, process_key + + @classmethod + def get_process_by_key(cls, process_key): """Get process by key.""" - current_app.logger.debug(f"Get process data for process key: {process_id}") - process = Process.get_latest_version(process_id) + current_app.logger.debug(f"Get process data for process key: {process_key}") + process = Process.get_latest_version_by_key(process_key) if process: return processSchema.dump(process) raise BusinessException(BusinessErrorCode.PROCESS_ID_NOT_FOUND) @classmethod @user_context - def update_process(cls, process_id, payload, **kwargs): + def update_process(cls, process_id, process_data, process_type, **kwargs): """Update process data.""" current_app.logger.debug(f"Update process data for process id: {process_id}") user: UserContext = kwargs["user"] tenant_key = user.tenant_key process = Process.find_process_by_id(process_id) - if process: - if tenant_key is not None and process.tenant != tenant_key: - raise BusinessException(BusinessErrorCode.PERMISSION_DENIED) - process_data = payload.get("processData") - data = processSchema.load(payload) - data["modified_by"] = user.user_name - if process_data is not None: - process_data = ( - json.dumps(process_data) - if process.process_type.value == "LOWCODE" - else process_data - ) - data["process_data"] = process_data.encode("utf-8") - process.update(data) - return processSchema.dump(process) - raise BusinessException(BusinessErrorCode.PROCESS_ID_NOT_FOUND) + + if process is None: + raise BusinessException(BusinessErrorCode.PROCESS_ID_NOT_FOUND) + + # Process the data name and key based on the process type and subflow status + process_data, process_name, process_key = cls._process_data_name_and_key( + process_data=process_data, + process_type=process_type or process.process_type, + is_subflow=process.is_subflow, + process_name=process.name, + process_key=process.process_key, + ) + + # Check if the process name or key already exists if it is a subflow + if process.is_subflow and Process.find_process_by_name_key( + name=process_name, + process_key=process_key, + parent_process_key=process.parent_process_key, + ): + raise BusinessException(BusinessErrorCode.PROCESS_EXISTS) + + # Determine version numbers based on the process status + is_unpublished = process.status == "Draft" and process.status_changed + major_version = ( + process.major_version + 1 if is_unpublished else process.major_version + ) + minor_version = 0 if is_unpublished else process.minor_version + 1 + + # Create a new process instance with updated data + process = Process( + name=process_name, + process_type=process_type.upper(), + tenant=tenant_key, + process_data=process_data, + created_by=user.user_name, + major_version=major_version, + minor_version=minor_version, + is_subflow=process.is_subflow, + process_key=process_key, + parent_process_key=process.parent_process_key, + ) + + # Save the updated process to the database + process.save() + + # Return the serialized process data + return processSchema.dump(process) @classmethod def delete_process(cls, process_id): @@ -133,14 +249,14 @@ def delete_process(cls, process_id): raise BusinessException(BusinessErrorCode.PROCESS_ID_NOT_FOUND) @staticmethod - def get_all_history(process_name: str, request_args): + def get_all_history(parent_process_key: str, request_args): """Get all history.""" - assert process_name is not None + assert parent_process_key is not None dict_data = ProcessListRequestSchema().load(request_args) or {} page_no = dict_data.get("page_no") limit = dict_data.get("limit") - process_histories, count = Process.fetch_histories_by_process_name( - process_name, page_no, limit + process_histories, count = Process.fetch_histories_by_parent_process_key( + parent_process_key, page_no, limit ) if process_histories: process_history_schema = ProcessHistorySchema(many=True) From a1776fe7e95733cbea63450a3b0551e3a0968ab7 Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:08:34 +0530 Subject: [PATCH 10/39] FWF-3690: [Feature] Added Subflow Publish & unpublish API --- .../src/formsflow_api/resources/process.py | 58 +++++++++++++++++++ .../services/form_process_mapper.py | 4 +- .../formsflow_api/services/import_support.py | 37 ++++++------ .../src/formsflow_api/services/process.py | 50 ++++++++++++++++ 4 files changed, 129 insertions(+), 20 deletions(-) diff --git a/forms-flow-api/src/formsflow_api/resources/process.py b/forms-flow-api/src/formsflow_api/resources/process.py index 76252af859..41c0835471 100644 --- a/forms-flow-api/src/formsflow_api/resources/process.py +++ b/forms-flow-api/src/formsflow_api/resources/process.py @@ -341,3 +341,61 @@ def get(): """ response = ProcessService.validate_process(request) return response, HTTPStatus.OK + + +@cors_preflight("POST,OPTIONS") +@API.route("//publish", methods=["POST", "OPTIONS"]) +class PublishResource(Resource): + """Resource to support publish sub-process/worklfow.""" + + @staticmethod + @auth.has_one_of_roles([CREATE_DESIGNS]) + @profiletime + @API.response(200, "OK:- Successful request.") + @API.response( + 400, + "BAD_REQUEST:- Invalid request.", + ) + @API.response( + 401, + "UNAUTHORIZED:- Authorization header not provided or an invalid token passed.", + ) + @API.response( + 403, + "FORBIDDEN:- Authorization will not help.", + ) + def post(process_id: int): + """Publish by process id.""" + return ( + ProcessService.publish(process_id), + HTTPStatus.OK, + ) + + +@cors_preflight("POST,OPTIONS") +@API.route("//unpublish", methods=["POST", "OPTIONS"]) +class UnpublishResource(Resource): + """Resource to support unpublish sub-process/workflow.""" + + @staticmethod + @auth.has_one_of_roles([CREATE_DESIGNS]) + @profiletime + @API.response(200, "OK:- Successful request.") + @API.response( + 400, + "BAD_REQUEST:- Invalid request.", + ) + @API.response( + 401, + "UNAUTHORIZED:- Authorization header not provided or an invalid token passed.", + ) + @API.response( + 403, + "FORBIDDEN:- Authorization will not help.", + ) + def post(process_id: int): + """Unpublish by process_id.""" + return ( + ProcessService.unpublish(process_id), + HTTPStatus.OK, + ) diff --git a/forms-flow-api/src/formsflow_api/services/form_process_mapper.py b/forms-flow-api/src/formsflow_api/services/form_process_mapper.py index 7b75626f21..1ff1e712ca 100644 --- a/forms-flow-api/src/formsflow_api/services/form_process_mapper.py +++ b/forms-flow-api/src/formsflow_api/services/form_process_mapper.py @@ -647,6 +647,8 @@ def publish(self, mapper_id, **kwargs): # create entry in process with default flow. process = Process( name=process_name, + process_key=process_name, + parent_process_key=process_name, process_type="BPMN", process_data=default_flow_xml_data(process_name).encode("utf-8"), form_process_mapper_id=mapper_id, @@ -677,7 +679,7 @@ def unpublish(self, mapper_id: int, **kwargs): user_name = user.user_name tenant_key = user.tenant_key mapper = self.validate_mapper(mapper_id, tenant_key) - # Capture publish status in form history table. + # Capture unpublish status in form history table. self.capture_form_history(mapper, {"status": "inactive"}, user_name) # Update status(inactive) in mapper table mapper.update( diff --git a/forms-flow-api/src/formsflow_api/services/import_support.py b/forms-flow-api/src/formsflow_api/services/import_support.py index 1791fd7828..7b6ea1170e 100644 --- a/forms-flow-api/src/formsflow_api/services/import_support.py +++ b/forms-flow-api/src/formsflow_api/services/import_support.py @@ -28,7 +28,6 @@ from .authorization import AuthorizationService from .form_history_logs import FormHistoryService from .form_process_mapper import FormProcessMapperService -from .process import ProcessService class ImportService: # pylint: disable=too-many-public-methods @@ -215,16 +214,18 @@ def update_workflow(self, xml_data, process_name): updated_xml = '\n' + updated_xml return updated_xml + @user_context def save_process_data( # pylint: disable=too-many-arguments, too-many-positional-arguments self, workflow_data, name, - mapper_id, selected_workflow_version=None, is_new=False, + **kwargs, ): """Save process data.""" current_app.logger.info("Saving process data...") + user: UserContext = kwargs["user"] updated_xml = self.update_workflow(workflow_data, name) # Save workflow on new import will have major version as 1 and minor version as 0 major_version, minor_version = 1, 0 @@ -253,18 +254,20 @@ def save_process_data( # pylint: disable=too-many-arguments, too-many-positiona else: minor_version += 1 # Save workflow as draft - process_data = { - "status": "Draft", - "processType": "bpmn", - "name": name, - "processData": updated_xml, - "majorVersion": major_version, - "minorVersion": minor_version, - "formProcessMapperId": mapper_id, - "processKey": name, - "parentProcessKey": name, - } - ProcessService.create_process(payload=process_data) + process_data = updated_xml.encode("utf-8") + process = Process( + name=name, + process_type="BPMN", + status="DRAFT", + tenant=user.tenant_key, + process_data=process_data, + created_by=user.user_name, + major_version=major_version, + minor_version=minor_version, + process_key=name, + parent_process_key=name, + ) + process.save() current_app.logger.info("Process data saved successfully...") def version_response(self, form_major, form_minor, workflow_major, workflow_minor): @@ -327,9 +330,7 @@ def import_new_form_workflow(self, file_data, form_json, workflow_data): process_name, mapper ) process_name = updated_process_name if updated_process_name else process_name - self.save_process_data( - workflow_data, process_name, mapper_id=mapper.id, is_new=True - ) + self.save_process_data(workflow_data, process_name, is_new=True) return form_id def import_form( @@ -579,7 +580,6 @@ def import_form_workflow( self.save_process_data( workflow_data, mapper.process_key, - mapper.id, selected_workflow_version, ) @@ -603,7 +603,6 @@ def import_form_workflow( self.save_process_data( file_content, mapper.process_key, - mapper_id, selected_workflow_version, ) return {"formId": form_id} diff --git a/forms-flow-api/src/formsflow_api/services/process.py b/forms-flow-api/src/formsflow_api/services/process.py index 146580456b..65a098f41c 100644 --- a/forms-flow-api/src/formsflow_api/services/process.py +++ b/forms-flow-api/src/formsflow_api/services/process.py @@ -15,6 +15,7 @@ ProcessHistorySchema, ProcessListRequestSchema, ) +from .form_process_mapper import FormProcessMapperService processSchema = ProcessDataSchema() @@ -182,6 +183,14 @@ def get_process_by_key(cls, process_key): return processSchema.dump(process) raise BusinessException(BusinessErrorCode.PROCESS_ID_NOT_FOUND) + @classmethod + def validate_process_by_id(cls, process_id): + """Validate process by id.""" + process = Process.find_process_by_id(process_id) + if not process: + raise BusinessException(BusinessErrorCode.PROCESS_ID_NOT_FOUND) + return process + @classmethod @user_context def update_process(cls, process_id, process_data, process_type, **kwargs): @@ -281,3 +290,44 @@ def validate_process(request): raise BusinessException(BusinessErrorCode.PROCESS_EXISTS) # If no results, the process name/key is valid return {} + + @classmethod + def update_process_status(cls, process, status, user): + """Update process status.""" + process = Process( + name=process.name, + process_type=process.process_type, + status=status, + tenant=user.tenant_key, + process_data=process.process_data, + created_by=user.user_name, + major_version=process.major_version, + minor_version=process.minor_version, + is_subflow=process.is_subflow, + process_key=process.process_key, + parent_process_key=process.parent_process_key, + status_changed=True, + ) + process.save() + return process + + @classmethod + @user_context + def publish(cls, process_id, **kwargs): + """Publish by process_id.""" + user: UserContext = kwargs["user"] + process = cls.validate_process_by_id(process_id) + cls.update_process_status(process, "PUBLISHED", user) + FormProcessMapperService().deploy_process( + process.name, process.process_data, user.tenant_key, user.bearer_token + ) + return {} + + @classmethod + @user_context + def unpublish(cls, process_id, **kwargs): + """Unpublish by process_id.""" + user: UserContext = kwargs["user"] + process = cls.validate_process_by_id(process_id) + cls.update_process_status(process, "DRAFT", user) + return {} From 40730006942b00670be68b56ed59759145a51d0f Mon Sep 17 00:00:00 2001 From: shuhaib-aot Date: Wed, 9 Oct 2024 12:53:24 +0530 Subject: [PATCH 11/39] Changed process history listing endpoint and fixed dmn and bpmn name , id getting and setting --- .../src/formsflow_api/models/process.py | 6 ++- .../src/formsflow_api/resources/process.py | 4 +- .../src/formsflow_api/services/process.py | 46 ++++++++++++++++--- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/forms-flow-api/src/formsflow_api/models/process.py b/forms-flow-api/src/formsflow_api/models/process.py index a4829a77d1..71c6f7fd6a 100644 --- a/forms-flow-api/src/formsflow_api/models/process.py +++ b/forms-flow-api/src/formsflow_api/models/process.py @@ -113,16 +113,20 @@ def filter_conditions(cls, **filters): return query @classmethod - def find_all_process( + def find_all_process( # pylint: disable=too-many-arguments, too-many-positional-arguments cls, page_no=None, limit=None, sort_by=None, sort_order=None, + is_subflow=False, **filters, ): """Find all processes.""" query = cls.filter_conditions(**filters) + query = query.filter(cls.status_changed.is_(False)) + if is_subflow: + query = query.filter(cls.is_subflow.is_(True)) query = cls.auth_query(query=query) sort_by, sort_order = validate_sort_order_and_order_by(sort_by, sort_order) if sort_by and sort_order: diff --git a/forms-flow-api/src/formsflow_api/resources/process.py b/forms-flow-api/src/formsflow_api/resources/process.py index 41c0835471..0630f09007 100644 --- a/forms-flow-api/src/formsflow_api/resources/process.py +++ b/forms-flow-api/src/formsflow_api/resources/process.py @@ -271,9 +271,7 @@ def delete(process_id: str): @cors_preflight("GET, OPTIONS") -@API.route( - "/process-history//versions", methods=["GET", "OPTIONS"] -) +@API.route("//versions", methods=["GET", "OPTIONS"]) class ProcessHistoryResource(Resource): """Resource for retrieving process history.""" diff --git a/forms-flow-api/src/formsflow_api/services/process.py b/forms-flow-api/src/formsflow_api/services/process.py index 65a098f41c..50d8c4c63c 100644 --- a/forms-flow-api/src/formsflow_api/services/process.py +++ b/forms-flow-api/src/formsflow_api/services/process.py @@ -15,6 +15,7 @@ ProcessHistorySchema, ProcessListRequestSchema, ) + from .form_process_mapper import FormProcessMapperService processSchema = ProcessDataSchema() @@ -55,6 +56,7 @@ def get_all_process(cls, request_args): # pylint:disable=too-many-locals modified_from=modified_from_date, modified_to=modified_to_date, sort_by=sort_by, + is_subflow=True, # now only for subflow listing sort_order=sort_order, created_by=created_by, id=process_id, @@ -64,17 +66,20 @@ def get_all_process(cls, request_args): # pylint:disable=too-many-locals page_no=page_no, limit=limit, ) - return processSchema.dump(process, many=True), count + return ( + ProcessDataSchema(exclude=["process_data"]).dump(process, many=True), + count, + ) @classmethod - def _upate_process_name_and_id(cls, xml_data, process_name): + def _upate_process_name_and_id(cls, xml_data, process_name, process_type): """Parse the workflow XML data & update process name.""" current_app.logger.info("Updating workflow...") # pylint: disable=I1101 root = cls._xml_parser(xml_data) # Find the bpmn:process element - process = root.find(".//{http://www.omg.org/spec/BPMN/20100524/MODEL}process") + process = cls.get_process_by_type(root, process_type) if process is not None: process.set("id", process_name) process.set("name", process_name) @@ -145,6 +150,33 @@ def create_process( # pylint: disable=too-many-arguments, too-many-positional-a # Return the serialized process data return processSchema.dump(process) + @staticmethod + def get_process_by_type(root, process_type): + """Get process name and id by type (BPMN or DMN).""" + # Define namespaces for BPMN and DMN + namespaces = { + "bpmn": "http://www.omg.org/spec/BPMN/20100524/MODEL", + "dmn": "https://www.omg.org/spec/DMN/20191111/MODEL/", + } + + # Check if the provided type exists in the namespace dictionary + if process_type not in namespaces: + raise ValueError(f"Unsupported process type: {process_type}") + + # Use the appropriate namespace for the type + target = ( + f"{process_type}:decision" + if process_type == "dmn" + else f"{process_type}:process" + ) + process = root.find(target, namespaces) + + # Check if process is found + if process is None: + raise ValueError(f"No process found for the given type: {process_type}") + + return process + @classmethod def _process_data_name_and_key( # pylint: disable=too-many-arguments, too-many-positional-arguments cls, @@ -160,9 +192,11 @@ def _process_data_name_and_key( # pylint: disable=too-many-arguments, too-many- # if the process is of type LOWCODE, convert the process data to JSON format if is_subflow: # Parse the XML data to extract process name and key for subflows - parsed_data = cls._xml_parser(process_data) + root = cls._xml_parser(process_data) + process = cls.get_process_by_type(root, process_type) + process_key = process.get("id") + process_name = process.get("name") process_data = process_data.encode("utf-8") - process_name, process_key = parsed_data.get("name"), parsed_data.get("id") else: if process_type.upper() == "LOWCODE": # Convert process data to JSON format for LOWCODE type processes @@ -170,7 +204,7 @@ def _process_data_name_and_key( # pylint: disable=too-many-arguments, too-many- else: # Update the process name and ID in the XML data for other process types process_data = cls._upate_process_name_and_id( - process_data, process_name + process_data, process_name, process_type ) return process_data, process_name, process_key From e70d43c2b10f5e4d1614015502bdb841ea3d0d83 Mon Sep 17 00:00:00 2001 From: shuhaib-aot Date: Wed, 9 Oct 2024 13:13:12 +0530 Subject: [PATCH 12/39] update proces added condition checking the id is latest or not --- .../src/formsflow_api/constants/__init__.py | 4 ++++ forms-flow-api/src/formsflow_api/services/process.py | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/forms-flow-api/src/formsflow_api/constants/__init__.py b/forms-flow-api/src/formsflow_api/constants/__init__.py index f8b8796891..6debc6ef63 100644 --- a/forms-flow-api/src/formsflow_api/constants/__init__.py +++ b/forms-flow-api/src/formsflow_api/constants/__init__.py @@ -20,6 +20,10 @@ class BusinessErrorCode(ErrorCodeMixin, Enum): HTTPStatus.BAD_REQUEST, ) PROCESS_DEF_NOT_FOUND = "Process definition does not exist", HTTPStatus.BAD_REQUEST + PROCESS_NOT_LATEST_VERSION = ( + "Passed process id is not latest version", + HTTPStatus.BAD_REQUEST, + ) DECISION_DEF_NOT_FOUND = ( "Decision definition does not exist", HTTPStatus.BAD_REQUEST, diff --git a/forms-flow-api/src/formsflow_api/services/process.py b/forms-flow-api/src/formsflow_api/services/process.py index 50d8c4c63c..be3cfcc09f 100644 --- a/forms-flow-api/src/formsflow_api/services/process.py +++ b/forms-flow-api/src/formsflow_api/services/process.py @@ -232,11 +232,20 @@ def update_process(cls, process_id, process_data, process_type, **kwargs): current_app.logger.debug(f"Update process data for process id: {process_id}") user: UserContext = kwargs["user"] tenant_key = user.tenant_key + # Find the process by its ID process = Process.find_process_by_id(process_id) - if process is None: + # Raise an exception if the process is not found raise BusinessException(BusinessErrorCode.PROCESS_ID_NOT_FOUND) + # Get the latest version of the process by its parent key + latest_process = Process.get_latest_version_by_parent_key( + process.parent_process_key + ) + if process.id != latest_process.id: + # Raise an exception if the process is not the latest version + raise BusinessException(BusinessErrorCode.PROCESS_NOT_LATEST_VERSION) + # Process the data name and key based on the process type and subflow status process_data, process_name, process_key = cls._process_data_name_and_key( process_data=process_data, From 44a0d7bcb5339d2cdc83989052b1ce9d00a5d57b Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:50:52 +0530 Subject: [PATCH 13/39] Update process status added to mapper publish/unpublish --- .../services/form_process_mapper.py | 63 ++++++++++++++----- .../formsflow_api/services/import_support.py | 2 +- .../src/formsflow_api/services/process.py | 27 ++------ 3 files changed, 53 insertions(+), 39 deletions(-) diff --git a/forms-flow-api/src/formsflow_api/services/form_process_mapper.py b/forms-flow-api/src/formsflow_api/services/form_process_mapper.py index b2ed62f2d6..85efdab855 100644 --- a/forms-flow-api/src/formsflow_api/services/form_process_mapper.py +++ b/forms-flow-api/src/formsflow_api/services/form_process_mapper.py @@ -310,6 +310,24 @@ def form_design_update(data, form_id): FormHistoryService.create_form_log_with_clone(data=data) return response + @classmethod + @user_context + def create_process(cls, process_name, **kwargs): + """Create process with default workflow.""" + user: UserContext = kwargs["user"] + process = Process( + name=process_name, + process_key=process_name, + parent_process_key=process_name, + process_type="BPMN", + process_data=default_flow_xml_data(process_name).encode("utf-8"), + tenant=user.tenant_key, + major_version=1, + minor_version=0, + created_by=user.user_name, + ) + process.save() + @staticmethod def create_form(data, is_designer): """Service to handle form create.""" @@ -367,6 +385,8 @@ def create_form(data, is_designer): "componentChanged": True, } ) + # create entry in process with default flow. + FormProcessMapperService.create_process(process_name) return response def _get_form( # pylint: disable=too-many-arguments, too-many-positional-arguments @@ -708,6 +728,26 @@ def capture_form_history(self, mapper, data, user_name): minor_version=minor_version, ).save() + @classmethod + def update_process_status(cls, process, status, user): + """Update process status.""" + process = Process( + name=process.name, + process_type=process.process_type, + status=status, + tenant=user.tenant_key, + process_data=process.process_data, + created_by=user.user_name, + major_version=process.major_version, + minor_version=process.minor_version, + is_subflow=process.is_subflow, + process_key=process.process_key, + parent_process_key=process.parent_process_key, + status_changed=True, + ) + process.save() + return process + @user_context def publish(self, mapper_id, **kwargs): """Publish by mapper_id.""" @@ -725,21 +765,10 @@ def publish(self, mapper_id, **kwargs): self.deploy_process(process_name, process_data, tenant_key, token) if not process: # create entry in process with default flow. - process = Process( - name=process_name, - process_key=process_name, - parent_process_key=process_name, - process_type="BPMN", - process_data=default_flow_xml_data(process_name).encode("utf-8"), - form_process_mapper_id=mapper_id, - tenant=tenant_key, - major_version=1, - minor_version=0, - created_by=user_name, - ) - # Update process status - process.status = "PUBLISHED" - process.save() + FormProcessMapperService.create_process(process_name) + else: + # Update process status + FormProcessMapperService.update_process_status(process, "PUBLISHED", user) # Capture publish(active) status in form history table. self.capture_form_history(mapper, {"status": "active"}, user_name) @@ -768,4 +797,8 @@ def unpublish(self, mapper_id: int, **kwargs): "prompt_new_version": True, } ) + # Update process status to Draft + process = Process.get_latest_version_by_key(mapper.process_key) + if process: + FormProcessMapperService.update_process_status(process, "DRAFT", user) return {} diff --git a/forms-flow-api/src/formsflow_api/services/import_support.py b/forms-flow-api/src/formsflow_api/services/import_support.py index 41ff2368f2..2fccca38f2 100644 --- a/forms-flow-api/src/formsflow_api/services/import_support.py +++ b/forms-flow-api/src/formsflow_api/services/import_support.py @@ -245,7 +245,6 @@ def save_process_data( # pylint: disable=too-many-arguments, too-many-positiona process = Process( name=name, process_type="BPMN", - status="DRAFT", tenant=user.tenant_key, process_data=process_data, created_by=user.user_name, @@ -322,6 +321,7 @@ def import_new_form_workflow(self, file_data, form_json, workflow_data): ) ) process_name = updated_process_name if updated_process_name else process_name + current_app.logger.info(f"Process Name: {process_name}") self.save_process_data(workflow_data, process_name, is_new=True) return form_id diff --git a/forms-flow-api/src/formsflow_api/services/process.py b/forms-flow-api/src/formsflow_api/services/process.py index be3cfcc09f..b90a1ca42c 100644 --- a/forms-flow-api/src/formsflow_api/services/process.py +++ b/forms-flow-api/src/formsflow_api/services/process.py @@ -221,7 +221,8 @@ def get_process_by_key(cls, process_key): def validate_process_by_id(cls, process_id): """Validate process by id.""" process = Process.find_process_by_id(process_id) - if not process: + # If process not available or if publish/unpublish non subflow + if not process or (process and process.is_subflow is False): raise BusinessException(BusinessErrorCode.PROCESS_ID_NOT_FOUND) return process @@ -334,33 +335,13 @@ def validate_process(request): # If no results, the process name/key is valid return {} - @classmethod - def update_process_status(cls, process, status, user): - """Update process status.""" - process = Process( - name=process.name, - process_type=process.process_type, - status=status, - tenant=user.tenant_key, - process_data=process.process_data, - created_by=user.user_name, - major_version=process.major_version, - minor_version=process.minor_version, - is_subflow=process.is_subflow, - process_key=process.process_key, - parent_process_key=process.parent_process_key, - status_changed=True, - ) - process.save() - return process - @classmethod @user_context def publish(cls, process_id, **kwargs): """Publish by process_id.""" user: UserContext = kwargs["user"] process = cls.validate_process_by_id(process_id) - cls.update_process_status(process, "PUBLISHED", user) + FormProcessMapperService.update_process_status(process, "PUBLISHED", user) FormProcessMapperService().deploy_process( process.name, process.process_data, user.tenant_key, user.bearer_token ) @@ -372,5 +353,5 @@ def unpublish(cls, process_id, **kwargs): """Unpublish by process_id.""" user: UserContext = kwargs["user"] process = cls.validate_process_by_id(process_id) - cls.update_process_status(process, "DRAFT", user) + FormProcessMapperService.update_process_status(process, "DRAFT", user) return {} From 6f4ad2b43d987638ceaedf82aeab722c168370b3 Mon Sep 17 00:00:00 2001 From: shuhaib-aot Date: Wed, 9 Oct 2024 16:09:29 +0530 Subject: [PATCH 14/39] FWF-3679 [feature] fixed test cases --- .../src/formsflow_api/models/process.py | 24 +++ .../src/formsflow_api/services/process.py | 72 ++++----- forms-flow-api/tests/unit/api/test_process.py | 142 ++++++++++-------- forms-flow-api/tests/utilities/base_test.py | 8 + 4 files changed, 149 insertions(+), 97 deletions(-) diff --git a/forms-flow-api/src/formsflow_api/models/process.py b/forms-flow-api/src/formsflow_api/models/process.py index 71c6f7fd6a..58a11296ef 100644 --- a/forms-flow-api/src/formsflow_api/models/process.py +++ b/forms-flow-api/src/formsflow_api/models/process.py @@ -57,6 +57,30 @@ class Process(AuditDateTimeMixin, AuditUserMixin, BaseModel, db.Model): is_subflow = db.Column(db.Boolean, default=False) status_changed = db.Column(db.Boolean, default=False) + @classmethod + def create_from_dict(cls, process_data: dict) -> Process: + """Create a new process from a dictionary.""" + if process_data: + process = Process( + name=process_data.get("name"), + process_type=process_data.get("process_type").upper(), + tenant=process_data.process_data("tenant"), + process_data=process_data.get("process_data"), + created_by=process_data.get("created_by"), + major_version=process_data.get("major_version"), + minor_version=process_data.get("minor_version"), + is_subflow=process_data.get("is_subflow", False), + status=process_data.get("status", ProcessStatus.DRAFT), + status_changed=process_data.get("status_changed", False), + process_key=process_data.get("process_key"), + parent_process_key=process_data.get("parent_process_key"), + ) + + # Save the new process to the database + process.save() + return process + return None + @classmethod @user_context def auth_query(cls, query, **kwargs) -> Process: diff --git a/forms-flow-api/src/formsflow_api/services/process.py b/forms-flow-api/src/formsflow_api/services/process.py index b90a1ca42c..d779e1adb0 100644 --- a/forms-flow-api/src/formsflow_api/services/process.py +++ b/forms-flow-api/src/formsflow_api/services/process.py @@ -72,7 +72,9 @@ def get_all_process(cls, request_args): # pylint:disable=too-many-locals ) @classmethod - def _upate_process_name_and_id(cls, xml_data, process_name, process_type): + def _upate_process_name_and_id( + cls, xml_data, process_name, process_key, process_type + ): """Parse the workflow XML data & update process name.""" current_app.logger.info("Updating workflow...") # pylint: disable=I1101 @@ -80,14 +82,16 @@ def _upate_process_name_and_id(cls, xml_data, process_name, process_type): # Find the bpmn:process element process = cls.get_process_by_type(root, process_type) + if process is not None: process.set("id", process_name) - process.set("name", process_name) + process.set("name", process_key or process_name) # Convert the XML tree back to a string updated_xml = etree.tostring( root, pretty_print=True, encoding="unicode", xml_declaration=False ) + # Prepend the XML declaration updated_xml = '\n' + updated_xml return updated_xml @@ -131,21 +135,19 @@ def create_process( # pylint: disable=too-many-arguments, too-many-positional-a major_version, minor_version = 1, 0 # Create a new process instance - process = Process( - name=process_name, - process_type=process_type.upper(), - tenant=tenant_key, - process_data=process_data, - created_by=user.user_name, - major_version=major_version, - minor_version=minor_version, - is_subflow=is_subflow, - process_key=process_key, - parent_process_key=process_key, - ) - - # Save the new process to the database - process.save() + process_dict = { + "name": process_name, + "process_type": process_type.upper(), + "tenant": tenant_key, + "process_data": process_data, + "created_by": user.user_name, + "major_version": major_version, + "minor_version": minor_version, + "is_subflow": is_subflow, + "process_key": process_key, + "parent_process_key": process_key, + } + process = Process.create_from_dict(process_dict) # Return the serialized process data return processSchema.dump(process) @@ -154,6 +156,7 @@ def create_process( # pylint: disable=too-many-arguments, too-many-positional-a def get_process_by_type(root, process_type): """Get process name and id by type (BPMN or DMN).""" # Define namespaces for BPMN and DMN + process_type = process_type.lower() namespaces = { "bpmn": "http://www.omg.org/spec/BPMN/20100524/MODEL", "dmn": "https://www.omg.org/spec/DMN/20191111/MODEL/", @@ -190,7 +193,7 @@ def _process_data_name_and_key( # pylint: disable=too-many-arguments, too-many- # if the process is not a subflow, update the process name and ID in the XML data # if the process is a subflow, parse the XML data to extract the process name and key # if the process is of type LOWCODE, convert the process data to JSON format - if is_subflow: + if is_subflow and process_type.upper() != "LOWCODE": # Parse the XML data to extract process name and key for subflows root = cls._xml_parser(process_data) process = cls.get_process_by_type(root, process_type) @@ -204,7 +207,10 @@ def _process_data_name_and_key( # pylint: disable=too-many-arguments, too-many- else: # Update the process name and ID in the XML data for other process types process_data = cls._upate_process_name_and_id( - process_data, process_name, process_type + xml_data=process_data, + process_name=process_name, + process_key=process_key, + process_type=process_type, ) return process_data, process_name, process_key @@ -272,21 +278,19 @@ def update_process(cls, process_id, process_data, process_type, **kwargs): minor_version = 0 if is_unpublished else process.minor_version + 1 # Create a new process instance with updated data - process = Process( - name=process_name, - process_type=process_type.upper(), - tenant=tenant_key, - process_data=process_data, - created_by=user.user_name, - major_version=major_version, - minor_version=minor_version, - is_subflow=process.is_subflow, - process_key=process_key, - parent_process_key=process.parent_process_key, - ) - - # Save the updated process to the database - process.save() + process_dict = { + "name": process_name, + "process_type": process_type.upper(), + "tenant": tenant_key, + "process_data": process_data, + "created_by": user.user_name, + "major_version": major_version, + "minor_version": minor_version, + "is_subflow": process.is_subflow, + "process_key": process_key, + "parent_process_key": process.parent_process_key, + } + process = Process.create_from_dict(process_dict) # Return the serialized process data return processSchema.dump(process) diff --git a/forms-flow-api/tests/unit/api/test_process.py b/forms-flow-api/tests/unit/api/test_process.py index f5d29d9ac6..8cfcb99101 100644 --- a/forms-flow-api/tests/unit/api/test_process.py +++ b/forms-flow-api/tests/unit/api/test_process.py @@ -10,6 +10,7 @@ from tests.utilities.base_test import ( get_process_request_payload, get_process_request_payload_low_code, + get_process_request_payload_for_dmn, get_token, ) @@ -22,23 +23,50 @@ def ensure_process_data_binary(process_id): process.save() +@pytest.fixture +def create_process(app, client, session, jwt): + """Create a process.""" + process = Process( + name="Test Workflow", + process_type="BPMN", + tenant=None, + process_data=get_process_request_payload()["processData"].encode("utf-8"), + created_by="test", + major_version=1, + minor_version=0, + is_subflow=False, + process_key="testworkflow", + parent_process_key="testworkflow", + ) + process.save() + return process + + +@pytest.fixture +def create_process_with_api_call(app, client, session, jwt): + """Create a process with API call.""" + token = get_token(jwt, role=CREATE_DESIGNS, username="designer") + headers = { + "Authorization": f"Bearer {token}", + "content-type": "application/json", + } + response = client.post( + "/process", headers=headers, json=get_process_request_payload() + ) + assert response.status_code == 201 + return response + + class TestProcessCreate: """Test suite for the process create method.""" - def test_process_create_method(self, app, client, session, jwt): + def test_process_create_method( + self, app, client, session, jwt, create_process_with_api_call + ): """Tests the process create method with valid payload.""" - token = get_token(jwt, role=CREATE_DESIGNS, username="designer") - headers = { - "Authorization": f"Bearer {token}", - "content-type": "application/json", - } - response = client.post( - "/process", headers=headers, json=get_process_request_payload() - ) - - assert response.status_code == 201 + response = create_process_with_api_call assert response.json.get("id") is not None - assert response.json.get("name") == "Testworkflow" + assert response.json.get("name") == "Test workflow" def test_process_create_method_with_invalid_token(self, app, client, session, jwt): """Tests the process create method with invalid token.""" @@ -56,41 +84,38 @@ def test_process_create_method_with_invalid_token(self, app, client, session, jw class TestProcessUpdate: """Test suite for the process update method.""" - def test_process_update(self, app, client, session, jwt): + def test_process_update_subflow( + self, app, client, session, jwt, create_process_with_api_call + ): """Tests the process update method with valid payload.""" token = get_token(jwt, role=CREATE_DESIGNS, username="designer") headers = { "Authorization": f"Bearer {token}", "content-type": "application/json", } - response = client.post( - "/process", headers=headers, json=get_process_request_payload_low_code() - ) + response = create_process_with_api_call assert response.status_code == 201 + assert response.json.get("processType") == "BPMN" assert response.json.get("id") is not None process_id = response.json.get("id") ensure_process_data_binary(process_id) response = client.put( f"/process/{process_id}", headers=headers, - json=get_process_request_payload_low_code(status="Published"), + json=get_process_request_payload_for_dmn(), ) assert response.status_code == 200 - assert response.json.get("status") == "Published" + assert response.json.get("processType") == "DMN" - def test_process_update_invalid_token(self, app, client, session, jwt): + def test_process_update_invalid_token( + self, app, client, session, jwt, create_process_with_api_call + ): """Tests the process update method with invalid token.""" - token = get_token(jwt, role=CREATE_DESIGNS, username="designer") - headers = { - "Authorization": f"Bearer {token}", - "content-type": "application/json", - } - response = client.post( - "/process", headers=headers, json=get_process_request_payload_low_code() - ) + response = create_process_with_api_call assert response.status_code == 201 assert response.json.get("id") is not None process_id = response.json.get("id") + ensure_process_data_binary(process_id) token = get_token(jwt, role=MANAGE_TASKS) headers = { "Authorization": f"Bearer {token}", @@ -107,31 +132,38 @@ def test_process_update_invalid_token(self, app, client, session, jwt): class TestProcessList: """Test suite for the process list.""" - def test_process_list(self, app, client, session, jwt): + def test_process_list( + self, app, client, session, jwt, create_process_with_api_call + ): """Testing process listing API.""" token = get_token(jwt, role=CREATE_DESIGNS, username="designer") headers = { "Authorization": f"Bearer {token}", "content-type": "application/json", } - response = client.post( - "/process", - headers=headers, - json=get_process_request_payload(name="Testworkflow1"), - ) + response = create_process_with_api_call ensure_process_data_binary(response.json.get("id")) response = client.get("/process", headers=headers) assert response.status_code == 200 assert response.json is not None assert response.json["totalCount"] == 1 - assert response.json["process"][0]["name"] == "Testworkflow1" + assert response.json["process"][0]["name"] == "Test workflow" @pytest.mark.parametrize( ("pageNo", "limit", "sortBy", "sortOrder"), ((1, 5, "id", "asc"), (1, 10, "id", "desc"), (1, 20, "id", "desc")), ) def test_process_list_with_pagination_sorted_list( - self, app, client, session, jwt, pageNo, limit, sortBy, sortOrder + self, + app, + client, + session, + jwt, + pageNo, + limit, + sortBy, + sortOrder, + create_process_with_api_call, ): """Testing process listing API with pagination and sorted list.""" token = get_token(jwt, role=CREATE_DESIGNS, username="designer") @@ -139,17 +171,8 @@ def test_process_list_with_pagination_sorted_list( "Authorization": f"Bearer {token}", "content-type": "application/json", } - response = client.post( - "/process", - headers=headers, - json=get_process_request_payload(name="Testworkflow1"), - ) - ensure_process_data_binary(response.json.get("id")) - response = client.post( - "/process", - headers=headers, - json=get_process_request_payload(name="Testworkflow2"), - ) + + response = create_process_with_api_call ensure_process_data_binary(response.json.get("id")) response = client.get( f"/process?pageNo={pageNo}&limit={limit}&sortBy={sortBy}&sortOrder={sortOrder}", @@ -158,28 +181,21 @@ def test_process_list_with_pagination_sorted_list( assert response.status_code == 200 assert response.json is not None - def test_process_list_with_filters(self, app, client, session, jwt): + def test_process_list_with_filters( + self, app, client, session, jwt, create_process_with_api_call + ): """Testing process listing API with filters.""" token = get_token(jwt, role=CREATE_DESIGNS, username="designer") headers = { "Authorization": f"Bearer {token}", "content-type": "application/json", } - response = client.post( - "/process", - headers=headers, - json=get_process_request_payload(name="Testworkflow1"), - ) - ensure_process_data_binary(response.json.get("id")) - response = client.post( - "/process", - headers=headers, - json=get_process_request_payload_low_code(name="Testworkflow2"), - ) + response = create_process_with_api_call ensure_process_data_binary(response.json.get("id")) + # testing with processType filter with status response = client.get( - "/process?status=Draft&processType=LOWCODE&name=Test", headers=headers + "/process?status=Draft&processType=BPMN&name=Test", headers=headers ) assert response.status_code == 200 assert response.json is not None @@ -199,16 +215,16 @@ def test_process_list_with_invalid_token(self, app, client, session, jwt): class TestProcessDelete: """Test suite for the process delete method.""" - def test_process_delete_method(self, app, client, session, jwt): + def test_process_delete_method( + self, app, client, session, jwt, create_process_with_api_call + ): """Tests the process delete method.""" token = get_token(jwt, role=CREATE_DESIGNS, username="designer") headers = { "Authorization": f"Bearer {token}", "content-type": "application/json", } - response = client.post( - "/process", headers=headers, json=get_process_request_payload() - ) + response = create_process_with_api_call assert response.status_code == 201 assert response.json.get("id") is not None process_id = response.json.get("id") diff --git a/forms-flow-api/tests/utilities/base_test.py b/forms-flow-api/tests/utilities/base_test.py index 49fc6f9042..f5eec66c04 100644 --- a/forms-flow-api/tests/utilities/base_test.py +++ b/forms-flow-api/tests/utilities/base_test.py @@ -602,6 +602,14 @@ def get_process_request_payload( } +def get_process_request_payload_for_dmn(): + """Return process request payload.""" "" + return { + "processType": "dmn", + "processData": """\ncategory\"assignment_notification\"\"Task Assignment\"\"Hello @name,\r\n \r\nYou have a new task for the process. Please click the following link to access your new task.\r\n\r\n@formUrl\r\n\r\n \r\nBest Regards\"\"activity_reminder\"\"Task Reminder\"\"Dear @name,\r\n\r\nThis is a reminder that your outstanding task is due in one day.\r\n\r\nApplication Number : @applicationId\r\n\r\n \r\nPlease click the following link to access your new task.\r\n\r\nTo access the task through formsflow.ai please follow this link: http://localhost:3000/task/@pid\r\n\r\n \r\n Regards, \r\n \"\"activity_escalation\"\"Task Escalation\"\"Dear @name,\r\n \r\nYou have exceeded the deadline for the task. \r\n\r\nApplication Number : @applicationId\r\n\r\n \r\nPlease click the following link to access your new task.\r\n\r\nTo access the task through formsflow.ai please follow this link: http://localhost:3000/task/@pid\r\n \r\n Regards, \r\n \"""", + } + + def get_process_request_payload_low_code(name="Lowcode workflow", status="Draft"): """Return process request payload for lowcode.""" "" return { From 677cfe403f01b293c7a30b8637ef5f82790052a0 Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:01:02 +0530 Subject: [PATCH 15/39] FWF-3690: [Feature] Testcase fixes --- .../src/formsflow_api/models/process.py | 4 +- .../services/form_process_mapper.py | 61 ++++----- forms-flow-api/tests/unit/api/test_process.py | 121 ++++++++++++++---- 3 files changed, 127 insertions(+), 59 deletions(-) diff --git a/forms-flow-api/src/formsflow_api/models/process.py b/forms-flow-api/src/formsflow_api/models/process.py index 58a11296ef..030724fe0d 100644 --- a/forms-flow-api/src/formsflow_api/models/process.py +++ b/forms-flow-api/src/formsflow_api/models/process.py @@ -63,8 +63,8 @@ def create_from_dict(cls, process_data: dict) -> Process: if process_data: process = Process( name=process_data.get("name"), - process_type=process_data.get("process_type").upper(), - tenant=process_data.process_data("tenant"), + process_type=process_data.get("process_type"), + tenant=process_data.get("tenant"), process_data=process_data.get("process_data"), created_by=process_data.get("created_by"), major_version=process_data.get("major_version"), diff --git a/forms-flow-api/src/formsflow_api/services/form_process_mapper.py b/forms-flow-api/src/formsflow_api/services/form_process_mapper.py index 85efdab855..5089a512ee 100644 --- a/forms-flow-api/src/formsflow_api/services/form_process_mapper.py +++ b/forms-flow-api/src/formsflow_api/services/form_process_mapper.py @@ -312,21 +312,22 @@ def form_design_update(data, form_id): @classmethod @user_context - def create_process(cls, process_name, **kwargs): + def create_default_process(cls, process_name, **kwargs): """Create process with default workflow.""" user: UserContext = kwargs["user"] - process = Process( - name=process_name, - process_key=process_name, - parent_process_key=process_name, - process_type="BPMN", - process_data=default_flow_xml_data(process_name).encode("utf-8"), - tenant=user.tenant_key, - major_version=1, - minor_version=0, - created_by=user.user_name, - ) - process.save() + process_dict = { + "name": process_name, + "process_key": process_name, + "parent_process_key": process_name, + "process_type": "BPMN", + "process_data": default_flow_xml_data(process_name).encode("utf-8"), + "tenant": user.tenant_key, + "major_version": 1, + "minor_version": 0, + "created_by": user.user_name, + } + process = Process.create_from_dict(process_dict) + return process @staticmethod def create_form(data, is_designer): @@ -386,7 +387,7 @@ def create_form(data, is_designer): } ) # create entry in process with default flow. - FormProcessMapperService.create_process(process_name) + FormProcessMapperService.create_default_process(process_name) return response def _get_form( # pylint: disable=too-many-arguments, too-many-positional-arguments @@ -731,21 +732,21 @@ def capture_form_history(self, mapper, data, user_name): @classmethod def update_process_status(cls, process, status, user): """Update process status.""" - process = Process( - name=process.name, - process_type=process.process_type, - status=status, - tenant=user.tenant_key, - process_data=process.process_data, - created_by=user.user_name, - major_version=process.major_version, - minor_version=process.minor_version, - is_subflow=process.is_subflow, - process_key=process.process_key, - parent_process_key=process.parent_process_key, - status_changed=True, - ) - process.save() + process_dict = { + "name": process.name, + "process_type": process.process_type, + "status": status, + "tenant": user.tenant_key, + "process_data": process.process_data, + "created_by": user.user_name, + "major_version": process.major_version, + "minor_version": process.minor_version, + "is_subflow": process.is_subflow, + "process_key": process.process_key, + "parent_process_key": process.parent_process_key, + "status_changed": True, + } + process = Process.create_from_dict(process_dict) return process @user_context @@ -765,7 +766,7 @@ def publish(self, mapper_id, **kwargs): self.deploy_process(process_name, process_data, tenant_key, token) if not process: # create entry in process with default flow. - FormProcessMapperService.create_process(process_name) + FormProcessMapperService.create_default_process(process_name) else: # Update process status FormProcessMapperService.update_process_status(process, "PUBLISHED", user) diff --git a/forms-flow-api/tests/unit/api/test_process.py b/forms-flow-api/tests/unit/api/test_process.py index 8cfcb99101..7677ac5331 100644 --- a/forms-flow-api/tests/unit/api/test_process.py +++ b/forms-flow-api/tests/unit/api/test_process.py @@ -1,6 +1,6 @@ """Test suite for Process API endpoints.""" -from unittest.mock import patch +from unittest.mock import MagicMock, patch import pytest from formsflow_api_utils.utils import CREATE_DESIGNS, MANAGE_TASKS @@ -300,9 +300,7 @@ def test_process_version_history_success(self, app, client, session, jwt): ) mock_import_service.return_value = mock_response - response = client.get( - "/process/process-history/test/versions", headers=headers - ) + response = client.get("/process/test/versions", headers=headers) # Assertions assert response.status_code == 200 assert response.json is not None @@ -342,9 +340,7 @@ def test_process_version_history_non_existent_process( } # Call the process version history endpoint with a non-existent process name - response = client.get( - "/process/process-history/non_existent_process/versions", headers=headers - ) + response = client.get("/process/non_existent_process/versions", headers=headers) assert response.status_code == 400 assert response.json.get("message") == "The specified process ID does not exist" @@ -356,9 +352,7 @@ def test_process_version_history_invalid_token(self, app, client, session, jwt): "content-type": "application/json", } - response = client.get( - "/process/process-history/Testworkflow/versions", headers=headers - ) + response = client.get("/process/Testworkflow/versions", headers=headers) assert response.status_code == 401 @@ -418,7 +412,7 @@ def test_process_validation_success(self, app, client, session, jwt): } # Validate process validate api with no exists process key & name response = client.get( - "/process/validate?processKey=Testworkflow&processName=Testworkflow", + "/process/validate?processKey=testflow&processName=testflow", headers=headers, ) assert response.status_code == 200 @@ -428,22 +422,7 @@ def test_process_validation_success(self, app, client, session, jwt): response = client.post( "/process", headers=headers, - json=get_process_request_payload( - name="Testworkflow", parent_process_key="Testworkflow" - ), - ) - assert response.status_code == 201 - assert response.json.get("id") is not None - ensure_process_data_binary(response.json.get("id")) - response = client.post( - "/process", - headers=headers, - json=get_process_request_payload( - name="Testworkflow", - parent_process_key="Testworkflow", - major_version=2, - minor_version=0, - ), + json=get_process_request_payload(), ) assert response.status_code == 201 assert response.json.get("id") is not None @@ -455,3 +434,91 @@ def test_process_validation_success(self, app, client, session, jwt): headers=headers, ) assert response.status_code == 200 + + +class TestProcessPublish: + """Test suite for the process publish.""" + + def test_process_publish_success(self, app, client, session, jwt): + """Test process publish success.""" + token = get_token(jwt, role=CREATE_DESIGNS, username="designer") + headers = { + "Authorization": f"Bearer {token}", + "content-type": "application/json", + } + response = client.post( + "/process", headers=headers, json=get_process_request_payload() + ) + + assert response.status_code == 201 + assert response.json.get("id") is not None + assert response.json.get("name") == "Test workflow" + process_id = response.json.get("id") + ensure_process_data_binary(process_id) + + with patch("requests.post") as mock_post: + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = "{}" + mock_post.return_value = mock_response + + response = client.post( + f"/process/{process_id}/publish", headers=headers, json={} + ) + assert response.status_code == 200 + + def test_process_publish_invalid_id(self, app, client, session, jwt): + """Test process publish with invalid id.""" + token = get_token(jwt, role=CREATE_DESIGNS, username="designer") + headers = { + "Authorization": f"Bearer {token}", + "content-type": "application/json", + } + response = client.post("/process/55/publish", headers=headers, json={}) + assert response.status_code == 400 + + def test_process_publish_unauthorized(app, client, session, jwt): + """Testing process publish without proper authorization.""" + response = client.post("/process/55/publish", json={}) + assert response.status_code == 401 + + +class TestProcessUnPublish: + """Test suite for the process unpublish.""" + + def test_process_unpublish_success(self, app, client, session, jwt): + """Test process unpublish success.""" + token = get_token(jwt, role=CREATE_DESIGNS, username="designer") + headers = { + "Authorization": f"Bearer {token}", + "content-type": "application/json", + } + response = client.post( + "/process", headers=headers, json=get_process_request_payload() + ) + + assert response.status_code == 201 + assert response.json.get("id") is not None + assert response.json.get("name") == "Test workflow" + process_id = response.json.get("id") + ensure_process_data_binary(process_id) + + response = client.post( + f"/process/{process_id}/unpublish", headers=headers, json={} + ) + assert response.status_code == 200 + + def test_process_unpublish_invalid_id(self, app, client, session, jwt): + """Test process unpublish with invalid id.""" + token = get_token(jwt, role=CREATE_DESIGNS, username="designer") + headers = { + "Authorization": f"Bearer {token}", + "content-type": "application/json", + } + response = client.post("/process/55/unpublish", headers=headers, json={}) + assert response.status_code == 400 + + def test_process_unpublish_unauthorized(app, client, session, jwt): + """Testing process unpublish without proper authorization.""" + response = client.post("/process/55/publish", json={}) + assert response.status_code == 401 From 3aabb99e54dc2563312c0057aaaeeaffe374a140 Mon Sep 17 00:00:00 2001 From: Bonymol-aot Date: Thu, 10 Oct 2024 00:23:03 -0700 Subject: [PATCH 16/39] FWF-3542: [Feature] Added functionalities on setting save --- .../CustomComponents/settingsModal.js | 62 +++++----- .../src/components/Form/Item/Edit.js | 108 ++++++++++++++---- 2 files changed, 112 insertions(+), 58 deletions(-) diff --git a/forms-flow-web/src/components/CustomComponents/settingsModal.js b/forms-flow-web/src/components/CustomComponents/settingsModal.js index 5eeb314c88..89ea848a56 100644 --- a/forms-flow-web/src/components/CustomComponents/settingsModal.js +++ b/forms-flow-web/src/components/CustomComponents/settingsModal.js @@ -11,45 +11,32 @@ import _set from "lodash/set"; import _cloneDeep from "lodash/cloneDeep"; import _camelCase from "lodash/camelCase"; -const SettingsModal = ({ show, handleClose, handleConfirm }) => { +const SettingsModal = + ({ show, handleClose, handleConfirm, rolesState, + setRolesState, formDetails, updateFormName, updateFormDescription, formType, setFormType }) => { const { t } = useTranslation(); const dispatch = useDispatch(); - const [rolesState, setRolesState] = useState({ - edit: { - roleInput: '', - filteredRoles: [], - selectedRoles: [], - selectedOption: 'onlyYou', - }, - create: { - roleInput: '', - filteredRoles: [], - selectedRoles: [], - selectedOption: 'registeredUsers', - isChecked: false, - }, - view: { - roleInput: '', - filteredRoles: [], - selectedRoles: [], - selectedOption: 'submitter', - }, - }); + const [userRoles, setUserRoles] = useState([]); const [url, setUrl] = useState(''); - const formName = useSelector((state) => state.form.form.name); - const formDescription = useSelector((state) => state.process.formProcessList.description); + // const formName = useSelector((state) => state.form.form.name); + // const formDescription = useSelector((state) => state.process.formProcessList.description); const formPath = useSelector((state) => state.form.form.path); + // const processListData = useSelector((state) => state.process?.formProcessList); + const [copied, setCopied] = useState(false); const [newPath, setNewPath] = useState(formPath); - const [newFormName, setNewFormName] = useState(formName); - const [newFormDescription, setNewFormDescription] = useState(formDescription); - const formData = useSelector((state) => state.form?.form); - const reducer = (form, { type, value }) => { - const formCopy = _cloneDeep(form); + // const [newFormName, setNewFormName] = useState(formName); + // const [newFormDescription, setNewFormDescription] = useState(formDescription); + + + + const formData = useSelector((state) => state.form?.form.path); + const reducer = (path, { type, value }) => { + const formCopy = _cloneDeep(path); switch (type) { case "formChange": for (let prop in value) { @@ -84,7 +71,7 @@ const SettingsModal = ({ show, handleClose, handleConfirm }) => { target.type === "checkbox" ? target.checked ? "wizard" : "form" : target.value; - + setFormType(value); dispatchFormAction({ type: path, value }); }; @@ -203,6 +190,7 @@ const SettingsModal = ({ show, handleClose, handleConfirm }) => { }; }, []); + return ( @@ -212,11 +200,16 @@ const SettingsModal = ({ show, handleClose, handleConfirm }) => { {/* Section 1: Basic */}
{t("Basic")}
- setNewFormName(e.target.value)} dataTestid="form-name" ariaLabel={t("Form Name")} /> + updateFormName(e.target.value)} + dataTestid="form-name" + ariaLabel={t("Form Name")} /> setNewFormDescription(e.target.value)} + value={formDetails.description} + onChange={(e) => updateFormDescription(e.target.value)} aria-label={t("Description of the edited form")} data-testid="form-description" maxRows={3} @@ -236,7 +229,7 @@ const SettingsModal = ({ show, handleClose, handleConfirm }) => { type="checkbox" id="createCheckbox" label={t("Allow adding multiple pages in this form")} - checked={form.display === "wizard"} + checked={formType === "wizard"} onChange={(event) => handleChange("display", event)} @@ -256,6 +249,7 @@ const SettingsModal = ({ show, handleClose, handleConfirm }) => { { label: t("You and specified roles"), onClick: () => setRolesState((prev) => ({ ...prev, edit: { ...prev.edit, selectedOption: 'specifiedRoles' } })) }, ]} dataTestid="edit-submission-role" ariaLabel={t("Edit Submission Role")} /> + {rolesState.edit.selectedOption === 'onlyYou' && ( )} diff --git a/forms-flow-web/src/components/Form/Item/Edit.js b/forms-flow-web/src/components/Form/Item/Edit.js index 99770fd95d..8b7e44599d 100644 --- a/forms-flow-web/src/components/Form/Item/Edit.js +++ b/forms-flow-web/src/components/Form/Item/Edit.js @@ -76,6 +76,7 @@ const Edit = React.memo(() => { const { t } = useTranslation(); const errors = useSelector((state) => state.form?.error); const processListData = useSelector((state) => state.process?.formProcessList); + const formAuthorization = useSelector((state) => state.process.authorizationDetails); const formData = useSelector((state) => state.form?.form); const [form, dispatchFormAction] = useReducer(reducer, _cloneDeep(formData)); const publisText = processListData.status == "active" ? "Unpublish" : "Publish"; @@ -95,10 +96,14 @@ const Edit = React.memo(() => { const applicationCount = useSelector((state) => state.process?.applicationCount); const [showSaveModal, setShowSaveModal] = useState(false); const [hasRendered, setHasRendered] = useState(false); + // const roleIds = useSelector((state) => state.user?.roleIds || {}); + // const formName = useSelector((state) => state.form.form.name); const [isLoadingDiagram, setIsLoadingDiagram] = useState(true); - //it returns the digram (old method); - // const diagramXML = useSelector((state) => state.process.processDiagramXML); - const roleIds = useSelector((state) => state.user?.roleIds || {}); + + const preferred_userName = useSelector( + (state) => state.user?.userDetail?.preferred_username || "" + ); + const [showSettingsModal, setShowSettingsModal] = useState(false); const handleOpenModal = () => setShowSettingsModal(true); const handleCloseModal = () => setShowSettingsModal(false); @@ -113,6 +118,35 @@ const Edit = React.memo(() => { WORKFLOW: "WORKFLOW" }; + const [formDetails, setFormDetails] = useState({ + name: processListData.formName, + description: processListData.description, + }); + + const [formType, setFormType] = useState(processListData.formType); + + const [rolesState, setRolesState] = useState({ + edit: { + roleInput: '', + filteredRoles: [], + selectedRoles: formAuthorization.DESIGNER?.roles, + selectedOption: 'onlyYou', + }, + create: { + roleInput: '', + filteredRoles: [], + selectedRoles: formAuthorization.FORM?.roles, + selectedOption: 'registeredUsers', + isChecked: processListData.anonymous, + }, + view: { + roleInput: '', + filteredRoles: [], + selectedRoles: formAuthorization.APPLICATION?.roles, + selectedOption: 'submitter', + }, + }); + useEffect(() => { if (showFlow) { setHasRendered(true); @@ -150,6 +184,22 @@ useEffect(() => { + const updateFormName = (formName) => { + setFormDetails((prevState) => ({ + ...prevState, + name: formName, + })); + dispatchFormAction({ type: "title", formName }); + }; + const updateFormDescription = (formDescription) => { + setFormDetails((prevState) => ({ + ...prevState, + description: formDescription, + })); + dispatchFormAction({type: "description", formDescription}); + }; + + //for save farm const isMapperSaveNeeded = (newData) => { return ( @@ -232,17 +282,17 @@ useEffect(() => { }; const handleConfirmSettings = () => { const parentFormId = processListData.parentFormId; - const mapper = { + const mapper = { formId: form._id, - formName: form.title, - description: formDescription, + formName: formDetails?.name, + description: formDetails?.description, status: processListData.status || "inactive", taskVariables: processListData.taskVariables ? processListData.taskVariables : [], - anonymous: formAccess[0]?.roles.includes(roleIds.ANONYMOUS), - parentFormId: parentFormId, - formType: form.type, + anonymous: rolesState.create.isChecked, + parentFormId: parentFormId, + formType: formType, processKey: workflow?.value, processName: workflow?.name, id: processListData.id, @@ -250,27 +300,33 @@ useEffect(() => { statusChanged: false, resourceId: form._id, }; - + const authorizations = { application: { resourceId:parentFormId , resourceDetails: {}, - roles: [] - }, + roles: rolesState.view.selectedOption === "spcifiedRoles" ? rolesState.view.selectedRoles : [], + userName: rolesState.view.selectedOption === "submitter" && '' + }, designer: { resourceId: parentFormId, resourceDetails: {}, - roles: [] - }, - form: { - resourceId: parentFormId, - resourceDetails: {}, - roles: [] - } -}; - dispatch(saveFormProcessMapperPut({mapper, authorizations})); + roles: rolesState.edit.selectedOption === "specifiedRoles" ? rolesState.edit.selectedRoles : [], + userName: rolesState.edit.selectedOption === "onlyYou" ? preferred_userName : '' + + }, + form: { + resourceId: parentFormId, + resourceDetails: {}, + roles: rolesState.create.selectedOption === "specifiedRoles" ? rolesState.create.selectedRoles : [], + userName: rolesState.create.selectedOption === "registeredUsers" ? '' : '' + + } }; - + dispatch(saveFormProcessMapperPut({ mapper, authorizations })); + handleCloseModal(); + }; + const closeSaveModal = () => { setShowSaveModal(false); @@ -386,8 +442,12 @@ useEffect(() => { text={t("Loading...")} > - + From a58843536882e89375f87f4611b1b2dd6b87ac39 Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Thu, 10 Oct 2024 14:35:34 +0530 Subject: [PATCH 17/39] FWF-3679: [Feature] process list api changes --- .../src/formsflow_api_utils/utils/__init__.py | 1 + .../src/formsflow_api_utils/utils/enums.py | 1 + .../src/formsflow_api_utils/utils/util.py | 17 ++++++++++ forms-flow-api/requirements.txt | 2 +- forms-flow-api/requirements/prod.txt | 2 +- .../models/form_process_mapper.py | 32 ++++++++----------- .../src/formsflow_api/models/process.py | 12 +++---- .../src/formsflow_api/services/process.py | 6 ++-- 8 files changed, 42 insertions(+), 31 deletions(-) diff --git a/forms-flow-api-utils/src/formsflow_api_utils/utils/__init__.py b/forms-flow-api-utils/src/formsflow_api_utils/utils/__init__.py index 314f14c146..0d2e38dd51 100644 --- a/forms-flow-api-utils/src/formsflow_api_utils/utils/__init__.py +++ b/forms-flow-api-utils/src/formsflow_api_utils/utils/__init__.py @@ -51,6 +51,7 @@ get_role_ids_from_user_groups, translate, validate_sort_order_and_order_by, + add_sort_filter, ) from .caching import Cache from .sentry import init_sentry diff --git a/forms-flow-api-utils/src/formsflow_api_utils/utils/enums.py b/forms-flow-api-utils/src/formsflow_api_utils/utils/enums.py index 469bd72304..c7d8c06a2b 100644 --- a/forms-flow-api-utils/src/formsflow_api_utils/utils/enums.py +++ b/forms-flow-api-utils/src/formsflow_api_utils/utils/enums.py @@ -79,3 +79,4 @@ class ProcessSortingParameters: # pylint: disable=too-few-public-methods Name = "name" Created = "created" Modified= "modified" + ProcessKey = "processKey" diff --git a/forms-flow-api-utils/src/formsflow_api_utils/utils/util.py b/forms-flow-api-utils/src/formsflow_api_utils/utils/util.py index 04e711a36e..3aa05da428 100644 --- a/forms-flow-api-utils/src/formsflow_api_utils/utils/util.py +++ b/forms-flow-api-utils/src/formsflow_api_utils/utils/util.py @@ -28,6 +28,7 @@ CREATE_SUBMISSIONS, VIEW_SUBMISSIONS, ) +from sqlalchemy.sql.expression import text def cors_preflight(methods: str = "GET"): """Render an option method on the class.""" @@ -70,6 +71,7 @@ def validate_sort_order_and_order_by(order_by: str, sort_order: str) -> bool: ProcessSortingParameters.Name, ProcessSortingParameters.Created, ProcessSortingParameters.Modified, + ProcessSortingParameters.ProcessKey, ]: order_by = None else: @@ -135,3 +137,18 @@ def get_form_and_submission_id_from_form_url(form_url: str) -> Tuple: form_id = form_url[form_url.find("/form/") + 6 : form_url.find("/submission/")] submission_id = form_url[form_url.find("/submission/") + 12 : len(form_url)] return (form_id, submission_id) + + +def add_sort_filter(query, sort_by, sort_order, model_name): + """Adding sortBy and sortOrder.""" + order = [] + if sort_by and sort_order: + for sort_by_att, sort_order_attr in zip(sort_by, sort_order): + name, value = validate_sort_order_and_order_by( + sort_order=sort_order_attr, order_by=sort_by_att + ) + if name and value: + order.append(text(f"{model_name}.{name} {value}")) + + query = query.order_by(*order) + return query diff --git a/forms-flow-api/requirements.txt b/forms-flow-api/requirements.txt index 97b59d9df3..8804b599ed 100644 --- a/forms-flow-api/requirements.txt +++ b/forms-flow-api/requirements.txt @@ -27,7 +27,7 @@ ecdsa==0.18.0 flask-jwt-oidc==0.3.0 flask-marshmallow==1.2.1 flask-restx==1.3.0 -formsflow_api_utils @ git+https://github.com/AOT-Technologies/forms-flow-ai.git@develop#subdirectory=forms-flow-api-utils +formsflow_api_utils @ git+https://github.com/shuhaib-aot/forms-flow-ai.git@feature/FWF-3679-subflow-creation-updation#subdirectory=forms-flow-api-utils gunicorn==21.2.0 h11==0.14.0 h2==4.1.0 diff --git a/forms-flow-api/requirements/prod.txt b/forms-flow-api/requirements/prod.txt index 46703f3c3d..b0cd1cfba6 100644 --- a/forms-flow-api/requirements/prod.txt +++ b/forms-flow-api/requirements/prod.txt @@ -17,4 +17,4 @@ markupsafe PyJWT redis lxml -git+https://github.com/AOT-Technologies/forms-flow-ai.git@develop#subdirectory=forms-flow-api-utils \ No newline at end of file +git+https://github.com/shuhaib-aot/forms-flow-ai.git@feature/FWF-3679-subflow-creation-updation#subdirectory=forms-flow-api-utils \ No newline at end of file diff --git a/forms-flow-api/src/formsflow_api/models/form_process_mapper.py b/forms-flow-api/src/formsflow_api/models/form_process_mapper.py index 3fd5e91178..6dbe5984b5 100644 --- a/forms-flow-api/src/formsflow_api/models/form_process_mapper.py +++ b/forms-flow-api/src/formsflow_api/models/form_process_mapper.py @@ -10,13 +10,12 @@ DEFAULT_PROCESS_KEY, DEFAULT_PROCESS_NAME, FILTER_MAPS, - validate_sort_order_and_order_by, + add_sort_filter, ) from formsflow_api_utils.utils.enums import FormProcessMapperStatus from formsflow_api_utils.utils.user_context import UserContext, user_context from sqlalchemy import UniqueConstraint, and_, desc, func, or_ from sqlalchemy.dialects.postgresql import JSON -from sqlalchemy.sql.expression import text from .audit_mixin import AuditDateTimeMixin, AuditUserMixin from .base_model import BaseModel @@ -184,21 +183,6 @@ def get_latest_form_mapper_ids(cls): .all() ) - @classmethod - def add_sort_filter(cls, query, sort_by, sort_order): - """Adding sortBy and sortOrder.""" - order = [] - if sort_by and sort_order: - for sort_by_att, sort_order_attr in zip(sort_by, sort_order): - name, value = validate_sort_order_and_order_by( - sort_order=sort_order_attr, order_by=sort_by_att - ) - if name and value: - order.append(text(f"form_process_mapper.{name} {value}")) - - query = query.order_by(*order) - return query - @classmethod def add_search_filter(cls, query, search): """Adding search filter in query.""" @@ -241,7 +225,12 @@ def find_all_forms( query = cls.add_search_filter(query=query, search=search) - query = cls.add_sort_filter(query=query, sort_by=sort_by, sort_order=sort_order) + query = add_sort_filter( + query=query, + sort_by=sort_by, + sort_order=sort_order, + model_name="form_process_mapper", + ) # form type is list of type to filter the form if form_type: @@ -293,7 +282,12 @@ def find_all_active_by_formid( ) query = cls.add_search_filter(query=query, search=search) query = cls.access_filter(query=query) - query = cls.add_sort_filter(sort_by=sort_by, sort_order=sort_order, query=query) + query = cls.add_sort_filter( + sort_by=sort_by, + sort_order=sort_order, + query=query, + model_name="form_process_mapper", + ) total_count = query.count() query = query.with_entities( diff --git a/forms-flow-api/src/formsflow_api/models/process.py b/forms-flow-api/src/formsflow_api/models/process.py index 030724fe0d..b83ce8009b 100644 --- a/forms-flow-api/src/formsflow_api/models/process.py +++ b/forms-flow-api/src/formsflow_api/models/process.py @@ -6,14 +6,10 @@ from typing import List from flask_sqlalchemy.query import Query -from formsflow_api_utils.utils import ( - FILTER_MAPS, - validate_sort_order_and_order_by, -) +from formsflow_api_utils.utils import FILTER_MAPS, add_sort_filter from formsflow_api_utils.utils.user_context import UserContext, user_context from sqlalchemy import LargeBinary, and_, desc, or_ from sqlalchemy.dialects.postgresql import ENUM -from sqlalchemy.sql.expression import text from .audit_mixin import AuditDateTimeMixin, AuditUserMixin from .base_model import BaseModel @@ -152,9 +148,9 @@ def find_all_process( # pylint: disable=too-many-arguments, too-many-positional if is_subflow: query = query.filter(cls.is_subflow.is_(True)) query = cls.auth_query(query=query) - sort_by, sort_order = validate_sort_order_and_order_by(sort_by, sort_order) - if sort_by and sort_order: - query = query.order_by(text(f"process.{sort_by} {sort_order}")) + query = add_sort_filter( + query=query, sort_by=sort_by, sort_order=sort_order, model_name="process" + ) total_count = query.count() limit = total_count if limit is None else limit query = query.paginate(page=page_no, per_page=limit, error_out=False) diff --git a/forms-flow-api/src/formsflow_api/services/process.py b/forms-flow-api/src/formsflow_api/services/process.py index d779e1adb0..257ac9f4de 100644 --- a/forms-flow-api/src/formsflow_api/services/process.py +++ b/forms-flow-api/src/formsflow_api/services/process.py @@ -35,7 +35,7 @@ def get_all_process(cls, request_args): # pylint:disable=too-many-locals dict_data = ProcessListRequestSchema().load(request_args) or {} page_no = dict_data.get("page_no") limit = dict_data.get("limit") - sort_by = dict_data.get("sort_by", "id") + sort_by = dict_data.get("sort_by", "") process_id = dict_data.get("process_id") process_name = dict_data.get("name") status = dict_data.get("status").upper() if dict_data.get("status") else None @@ -49,7 +49,9 @@ def get_all_process(cls, request_args): # pylint:disable=too-many-locals created_to_date = dict_data.get("created_to_date") modified_from_date = dict_data.get("modified_from_date") modified_to_date = dict_data.get("modified_to_date") - sort_order = dict_data.get("sort_order", "desc") + sort_order = dict_data.get("sort_order", "") + sort_by = sort_by.split(",") + sort_order = sort_order.split(",") process, count = Process.find_all_process( created_from=created_from_date, created_to=created_to_date, From b35425b943e10292512f5940f6f48106429dc16f Mon Sep 17 00:00:00 2001 From: shuhaib-aot Date: Thu, 10 Oct 2024 15:09:58 +0530 Subject: [PATCH 18/39] security fix --- forms-flow-api/src/formsflow_api/services/process.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forms-flow-api/src/formsflow_api/services/process.py b/forms-flow-api/src/formsflow_api/services/process.py index d779e1adb0..12735ae1d0 100644 --- a/forms-flow-api/src/formsflow_api/services/process.py +++ b/forms-flow-api/src/formsflow_api/services/process.py @@ -1,7 +1,7 @@ """This exposes process service.""" import json -import xml.etree.ElementTree as ET + from flask import current_app from formsflow_api_utils.exceptions import BusinessException @@ -27,7 +27,7 @@ class ProcessService: # pylint: disable=too-few-public-methods @classmethod def _xml_parser(cls, process_data): """Parse the process data.""" - return ET.fromstring(process_data.encode("utf-8")) + return etree.fromstring(process_data.encode("utf-8")) @classmethod def get_all_process(cls, request_args): # pylint:disable=too-many-locals From d4ccad0f4f7aa6e368289306f28ccad83052c46f Mon Sep 17 00:00:00 2001 From: shuhaib-aot Date: Thu, 10 Oct 2024 15:29:41 +0530 Subject: [PATCH 19/39] Fixing api ci --- .github/workflows/forms-flow-api-ci.yml | 4 ++-- forms-flow-api/src/formsflow_api/services/process.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/forms-flow-api-ci.yml b/.github/workflows/forms-flow-api-ci.yml index d634576715..3196efaba3 100644 --- a/.github/workflows/forms-flow-api-ci.yml +++ b/.github/workflows/forms-flow-api-ci.yml @@ -35,12 +35,12 @@ jobs: strategy: matrix: - python-version: [3.12.6] + python-version: [3.12.7] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/forms-flow-api/src/formsflow_api/services/process.py b/forms-flow-api/src/formsflow_api/services/process.py index 84157158b9..e71807c3ae 100644 --- a/forms-flow-api/src/formsflow_api/services/process.py +++ b/forms-flow-api/src/formsflow_api/services/process.py @@ -28,7 +28,7 @@ class ProcessService: # pylint: disable=too-few-public-methods def _xml_parser(cls, process_data): """Parse the process data.""" parser = etree.XMLParser(resolve_entities=False) - return etree.fromstring(process_data.encode("utf-8"),parser=parser) + return etree.fromstring(process_data.encode("utf-8"), parser=parser) @classmethod def get_all_process(cls, request_args): # pylint:disable=too-many-locals From 72365f7104b423008049349fad3cfc7a40d0d474 Mon Sep 17 00:00:00 2001 From: Bonymol-aot Date: Thu, 10 Oct 2024 03:44:24 -0700 Subject: [PATCH 20/39] FWF-3542: [Feature] Updated functionalities on settings save --- .../CustomComponents/settingsModal.js | 24 ++++++++++-------- .../src/components/Form/Item/Edit.js | 25 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/forms-flow-web/src/components/CustomComponents/settingsModal.js b/forms-flow-web/src/components/CustomComponents/settingsModal.js index 89ea848a56..02ecc5239d 100644 --- a/forms-flow-web/src/components/CustomComponents/settingsModal.js +++ b/forms-flow-web/src/components/CustomComponents/settingsModal.js @@ -13,7 +13,8 @@ import _camelCase from "lodash/camelCase"; const SettingsModal = ({ show, handleClose, handleConfirm, rolesState, - setRolesState, formDetails, updateFormName, updateFormDescription, formType, setFormType }) => { + setRolesState, formDetails, updateFormName, updateFormDescription, formType, + setFormType, newPath, handleFormPathChange }) => { const { t } = useTranslation(); const dispatch = useDispatch(); @@ -21,14 +22,15 @@ const SettingsModal = const [userRoles, setUserRoles] = useState([]); const [url, setUrl] = useState(''); + // const formName = useSelector((state) => state.form.form.name); // const formDescription = useSelector((state) => state.process.formProcessList.description); - const formPath = useSelector((state) => state.form.form.path); + // const formPath = useSelector((state) => state.form.form.path); // const processListData = useSelector((state) => state.process?.formProcessList); const [copied, setCopied] = useState(false); - const [newPath, setNewPath] = useState(formPath); + // const [newPath, setNewPath] = useState(formPath); // const [newFormName, setNewFormName] = useState(formName); // const [newFormDescription, setNewFormDescription] = useState(formDescription); @@ -158,9 +160,9 @@ const SettingsModal = }); }; - const handleFormPathChange = (e) => { - setNewPath(e.target.value); - }; + // const handleFormPathChange = (e) => { + // setNewPath(e.target.value); + // }; const handleClickOutside = (event) => { if (dropEditRef.current && !dropEditRef.current.contains(event.target)) { @@ -283,9 +285,9 @@ const SettingsModal = type="checkbox" id="createCheckbox" label={t("Anonymous users")} - checked={rolesState.create.isChecked} + checked={rolesState.create.isPublic} onChange={() => setRolesState((prev) => - ({ ...prev, create: { ...prev.create, isChecked: !prev.create.isChecked } }))} + ({ ...prev, create: { ...prev.create, isPublic: !prev.create.isPublic } }))} className='field-label' />
- -
+ {rolesState.create.isPublic && ( +
{t("Link for this form")}
@@ -411,6 +413,8 @@ const SettingsModal =
+ )} + { // const roleIds = useSelector((state) => state.user?.roleIds || {}); // const formName = useSelector((state) => state.form.form.name); const [isLoadingDiagram, setIsLoadingDiagram] = useState(true); + const formPath = useSelector((state) => state.form.form.path); + const [newPath, setNewPath] = useState(formPath); + const preferred_userName = useSelector( (state) => state.user?.userDetail?.preferred_username || "" @@ -137,7 +140,8 @@ const Edit = React.memo(() => { filteredRoles: [], selectedRoles: formAuthorization.FORM?.roles, selectedOption: 'registeredUsers', - isChecked: processListData.anonymous, + isPublic: processListData.anonymous, + }, view: { roleInput: '', @@ -153,6 +157,9 @@ const Edit = React.memo(() => { } }, [showFlow]); + const handleFormPathChange = (e) => { + setNewPath(e.target.value); + }; const handleShowLayout = () => { setShowFlow(false); @@ -290,7 +297,7 @@ useEffect(() => { taskVariables: processListData.taskVariables ? processListData.taskVariables : [], - anonymous: rolesState.create.isChecked, + anonymous: rolesState.create.isPublic, parentFormId: parentFormId, formType: formType, processKey: workflow?.value, @@ -305,22 +312,19 @@ useEffect(() => { application: { resourceId:parentFormId , resourceDetails: {}, - roles: rolesState.view.selectedOption === "spcifiedRoles" ? rolesState.view.selectedRoles : [], - userName: rolesState.view.selectedOption === "submitter" && '' + roles: rolesState.view.selectedOption === "specifiedRoles" ? rolesState.view.selectedRoles : [], + ...(rolesState.view.selectedOption === "submitter" && { userName: preferred_userName }) // TBD }, designer: { resourceId: parentFormId, resourceDetails: {}, roles: rolesState.edit.selectedOption === "specifiedRoles" ? rolesState.edit.selectedRoles : [], - userName: rolesState.edit.selectedOption === "onlyYou" ? preferred_userName : '' - - }, + ...(rolesState.edit.selectedOption === "onlyYou" && { userName: preferred_userName }) + }, form: { resourceId: parentFormId, resourceDetails: {}, roles: rolesState.create.selectedOption === "specifiedRoles" ? rolesState.create.selectedRoles : [], - userName: rolesState.create.selectedOption === "registeredUsers" ? '' : '' - } }; dispatch(saveFormProcessMapperPut({ mapper, authorizations })); @@ -447,7 +451,8 @@ useEffect(() => { rolesState={rolesState} setRolesState={setRolesState} setFormDetails={setFormDetails} formDetails={formDetails} updateFormName={updateFormName} formType={formType} setFormType={setFormType} - updateFormDescription={updateFormDescription}/> + updateFormDescription={updateFormDescription} newPath={newPath} + handleFormPathChange={handleFormPathChange}/> From 2b3d4073b8b85e7afd5e5fc5162197daf01a30ba Mon Sep 17 00:00:00 2001 From: Bonymol-aot Date: Thu, 10 Oct 2024 04:12:02 -0700 Subject: [PATCH 21/39] FWF-3542: [Feature] code formatted --- .../CustomComponents/settingsModal.js | 27 +------------------ .../src/components/Form/Item/Edit.js | 7 ++--- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/forms-flow-web/src/components/CustomComponents/settingsModal.js b/forms-flow-web/src/components/CustomComponents/settingsModal.js index 02ecc5239d..010721a3af 100644 --- a/forms-flow-web/src/components/CustomComponents/settingsModal.js +++ b/forms-flow-web/src/components/CustomComponents/settingsModal.js @@ -17,25 +17,9 @@ const SettingsModal = setFormType, newPath, handleFormPathChange }) => { const { t } = useTranslation(); const dispatch = useDispatch(); - - - const [userRoles, setUserRoles] = useState([]); const [url, setUrl] = useState(''); - - // const formName = useSelector((state) => state.form.form.name); - // const formDescription = useSelector((state) => state.process.formProcessList.description); - // const formPath = useSelector((state) => state.form.form.path); - // const processListData = useSelector((state) => state.process?.formProcessList); - - - const [copied, setCopied] = useState(false); - // const [newPath, setNewPath] = useState(formPath); - // const [newFormName, setNewFormName] = useState(formName); - // const [newFormDescription, setNewFormDescription] = useState(formDescription); - - - + const [copied, setCopied] = useState(false); const formData = useSelector((state) => state.form?.form.path); const reducer = (path, { type, value }) => { const formCopy = _cloneDeep(path); @@ -160,10 +144,6 @@ const SettingsModal = }); }; - // const handleFormPathChange = (e) => { - // setNewPath(e.target.value); - // }; - const handleClickOutside = (event) => { if (dropEditRef.current && !dropEditRef.current.contains(event.target)) { setRolesState((prevState) => ({ @@ -407,11 +387,6 @@ const SettingsModal = - - - - -
)} diff --git a/forms-flow-web/src/components/Form/Item/Edit.js b/forms-flow-web/src/components/Form/Item/Edit.js index 94302a8caf..24abd4a013 100644 --- a/forms-flow-web/src/components/Form/Item/Edit.js +++ b/forms-flow-web/src/components/Form/Item/Edit.js @@ -96,8 +96,7 @@ const Edit = React.memo(() => { const applicationCount = useSelector((state) => state.process?.applicationCount); const [showSaveModal, setShowSaveModal] = useState(false); const [hasRendered, setHasRendered] = useState(false); - // const roleIds = useSelector((state) => state.user?.roleIds || {}); - // const formName = useSelector((state) => state.form.form.name); + const [isLoadingDiagram, setIsLoadingDiagram] = useState(true); const formPath = useSelector((state) => state.form.form.path); const [newPath, setNewPath] = useState(formPath); @@ -422,9 +421,7 @@ useEffect(() => { console.log("discardChanges"); }; - // const editorSettings = () => { - // console.log("ecitorActions"); - // }; + const editorActions = () => { setNewActionModal(true); }; From 66320b09360d3c4e45c4e505ab066a405241f4ea Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Thu, 10 Oct 2024 16:50:17 +0530 Subject: [PATCH 22/39] FWF-3679: [Modified] Reuse xml parser --- .../src/formsflow_api/services/import_support.py | 4 ++-- forms-flow-api/src/formsflow_api/services/process.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/forms-flow-api/src/formsflow_api/services/import_support.py b/forms-flow-api/src/formsflow_api/services/import_support.py index 2fccca38f2..3182b0d0cd 100644 --- a/forms-flow-api/src/formsflow_api/services/import_support.py +++ b/forms-flow-api/src/formsflow_api/services/import_support.py @@ -28,6 +28,7 @@ from .authorization import AuthorizationService from .form_history_logs import FormHistoryService from .form_process_mapper import FormProcessMapperService +from .process import ProcessService class ImportService: # pylint: disable=too-many-public-methods @@ -184,8 +185,7 @@ def validate_edit_form_exists(self, form_json, mapper, tenant_key): def update_workflow(self, xml_data, process_name): """Parse the workflow XML data & update process name.""" current_app.logger.info("Updating workflow...") - # pylint: disable=I1101 - root = etree.fromstring(xml_data.encode("utf-8")) + root = ProcessService.xml_parser(xml_data) # Find the bpmn:process element process = root.find(".//{http://www.omg.org/spec/BPMN/20100524/MODEL}process") diff --git a/forms-flow-api/src/formsflow_api/services/process.py b/forms-flow-api/src/formsflow_api/services/process.py index e71807c3ae..02cd2452e4 100644 --- a/forms-flow-api/src/formsflow_api/services/process.py +++ b/forms-flow-api/src/formsflow_api/services/process.py @@ -25,8 +25,9 @@ class ProcessService: # pylint: disable=too-few-public-methods """This class manages process service.""" @classmethod - def _xml_parser(cls, process_data): + def xml_parser(cls, process_data): """Parse the process data.""" + # pylint: disable=I1101 parser = etree.XMLParser(resolve_entities=False) return etree.fromstring(process_data.encode("utf-8"), parser=parser) @@ -81,7 +82,7 @@ def _upate_process_name_and_id( """Parse the workflow XML data & update process name.""" current_app.logger.info("Updating workflow...") # pylint: disable=I1101 - root = cls._xml_parser(xml_data) + root = cls.xml_parser(xml_data) # Find the bpmn:process element process = cls.get_process_by_type(root, process_type) @@ -198,7 +199,7 @@ def _process_data_name_and_key( # pylint: disable=too-many-arguments, too-many- # if the process is of type LOWCODE, convert the process data to JSON format if is_subflow and process_type.upper() != "LOWCODE": # Parse the XML data to extract process name and key for subflows - root = cls._xml_parser(process_data) + root = cls.xml_parser(process_data) process = cls.get_process_by_type(root, process_type) process_key = process.get("id") process_name = process.get("name") From 3878783a9cd275d1945063b6202bc4c18cb9511e Mon Sep 17 00:00:00 2001 From: abilpraju-aot Date: Thu, 10 Oct 2024 16:59:33 +0530 Subject: [PATCH 23/39] build fix --- forms-flow-web/src/components/Form/Item/Edit.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/forms-flow-web/src/components/Form/Item/Edit.js b/forms-flow-web/src/components/Form/Item/Edit.js index 4577607336..9f0425c70c 100644 --- a/forms-flow-web/src/components/Form/Item/Edit.js +++ b/forms-flow-web/src/components/Form/Item/Edit.js @@ -48,13 +48,13 @@ import { getFormProcesses } from "../../../apiManager/services/processServices"; import { getProcessXml } from "../../../apiManager/services/processServices"; import SettingsModal from "../../CustomComponents/settingsModal"; -import DeleteFormModal from "../../Modals/DeleteFormModal.js"; + // constant values const DUPLICATE = "DUPLICATE"; // const SAVE_AS_TEMPLATE= "SAVE_AS_TEMPLATE"; // const IMPORT= "IMPORT"; // const EXPORT= "EXPORT"; -const DELETE = "DELETE"; +//const DELETE = "DELETE"; const reducer = (form, { type, value }) => { const formCopy = _cloneDeep(form); @@ -142,10 +142,6 @@ const Edit = React.memo(() => { setNameError(""); setFormSubmitted(false); } - if (selectedAction === DELETE) { - //setNameError(""); - //setFormSubmitted(false); - } }; useEffect(() => { @@ -751,11 +747,6 @@ const Edit = React.memo(() => { nameValidationOnBlur={validateFormNameOnBlur} nameError={nameError} /> - {(t) => t("Save Your Changes")}} From 3f8c9d5adf84ce95e0865ff3ced15fb33b839183 Mon Sep 17 00:00:00 2001 From: abilpraju-aot Date: Fri, 11 Oct 2024 14:53:52 +0530 Subject: [PATCH 24/39] updated multi-tenancy relm with brut force prevention --- .../realm-exports/Multi tenant realm.json | 105 +++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/forms-flow-idm/realm-exports/Multi tenant realm.json b/forms-flow-idm/realm-exports/Multi tenant realm.json index a01dc26c0e..53632669f1 100644 --- a/forms-flow-idm/realm-exports/Multi tenant realm.json +++ b/forms-flow-idm/realm-exports/Multi tenant realm.json @@ -2,6 +2,15 @@ "id": "multitenant", "realm": "multitenant", "loginTheme": "formsflow", + "bruteForceProtected": true, + "permanentLockout": false, + "maxTemporaryLockouts": 0, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 1800, + "failureFactor": 5, "roles": { "client": { "forms-flow-bpm": [], @@ -211,5 +220,99 @@ } } ] - } + }, + "eventsEnabled": true, + "eventsExpiration": 1800, + "eventsListeners": ["jboss-logging"], + "enabledEventTypes": [ + "UPDATE_CONSENT_ERROR", + "SEND_RESET_PASSWORD", + "GRANT_CONSENT", + "VERIFY_PROFILE_ERROR", + "UPDATE_TOTP", + "REMOVE_TOTP", + "REVOKE_GRANT", + "LOGIN_ERROR", + "CLIENT_LOGIN", + "RESET_PASSWORD_ERROR", + "IMPERSONATE_ERROR", + "CODE_TO_TOKEN_ERROR", + "CUSTOM_REQUIRED_ACTION", + "OAUTH2_DEVICE_CODE_TO_TOKEN_ERROR", + "RESTART_AUTHENTICATION", + "UPDATE_PROFILE_ERROR", + "IMPERSONATE", + "LOGIN", + "UPDATE_PASSWORD_ERROR", + "OAUTH2_DEVICE_VERIFY_USER_CODE", + "CLIENT_INITIATED_ACCOUNT_LINKING", + "USER_DISABLED_BY_PERMANENT_LOCKOUT", + "OAUTH2_EXTENSION_GRANT", + "TOKEN_EXCHANGE", + "REGISTER", + "LOGOUT", + "AUTHREQID_TO_TOKEN", + "DELETE_ACCOUNT_ERROR", + "CLIENT_REGISTER", + "IDENTITY_PROVIDER_LINK_ACCOUNT", + "USER_DISABLED_BY_TEMPORARY_LOCKOUT", + "UPDATE_PASSWORD", + "DELETE_ACCOUNT", + "FEDERATED_IDENTITY_LINK_ERROR", + "CLIENT_DELETE", + "IDENTITY_PROVIDER_FIRST_LOGIN", + "VERIFY_EMAIL", + "CLIENT_DELETE_ERROR", + "CLIENT_LOGIN_ERROR", + "RESTART_AUTHENTICATION_ERROR", + "REMOVE_FEDERATED_IDENTITY_ERROR", + "EXECUTE_ACTIONS", + "TOKEN_EXCHANGE_ERROR", + "PERMISSION_TOKEN", + "FEDERATED_IDENTITY_OVERRIDE_LINK", + "SEND_IDENTITY_PROVIDER_LINK_ERROR", + "EXECUTE_ACTION_TOKEN_ERROR", + "SEND_VERIFY_EMAIL", + "OAUTH2_EXTENSION_GRANT_ERROR", + "OAUTH2_DEVICE_AUTH", + "EXECUTE_ACTIONS_ERROR", + "REMOVE_FEDERATED_IDENTITY", + "OAUTH2_DEVICE_CODE_TO_TOKEN", + "IDENTITY_PROVIDER_POST_LOGIN", + "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", + "FEDERATED_IDENTITY_OVERRIDE_LINK_ERROR", + "UPDATE_EMAIL", + "OAUTH2_DEVICE_VERIFY_USER_CODE_ERROR", + "REGISTER_ERROR", + "REVOKE_GRANT_ERROR", + "LOGOUT_ERROR", + "UPDATE_EMAIL_ERROR", + "EXECUTE_ACTION_TOKEN", + "CLIENT_UPDATE_ERROR", + "UPDATE_PROFILE", + "AUTHREQID_TO_TOKEN_ERROR", + "INVITE_ORG_ERROR", + "FEDERATED_IDENTITY_LINK", + "CLIENT_REGISTER_ERROR", + "INVITE_ORG", + "SEND_VERIFY_EMAIL_ERROR", + "SEND_IDENTITY_PROVIDER_LINK", + "RESET_PASSWORD", + "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", + "OAUTH2_DEVICE_AUTH_ERROR", + "UPDATE_CONSENT", + "REMOVE_TOTP_ERROR", + "VERIFY_EMAIL_ERROR", + "SEND_RESET_PASSWORD_ERROR", + "CLIENT_UPDATE", + "IDENTITY_PROVIDER_POST_LOGIN_ERROR", + "CUSTOM_REQUIRED_ACTION_ERROR", + "UPDATE_TOTP_ERROR", + "CODE_TO_TOKEN", + "VERIFY_PROFILE", + "GRANT_CONSENT_ERROR", + "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR" + ], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false } From 993899fb2cf4978399ba729293f7f313469b1a0e Mon Sep 17 00:00:00 2001 From: Bonymol-aot Date: Fri, 11 Oct 2024 03:26:06 -0700 Subject: [PATCH 25/39] FWF-3542: [Feature] index update added --- .../CustomComponents/settingsModal.js | 98 ++++++++++++++++--- 1 file changed, 84 insertions(+), 14 deletions(-) diff --git a/forms-flow-web/src/components/CustomComponents/settingsModal.js b/forms-flow-web/src/components/CustomComponents/settingsModal.js index 010721a3af..e484943015 100644 --- a/forms-flow-web/src/components/CustomComponents/settingsModal.js +++ b/forms-flow-web/src/components/CustomComponents/settingsModal.js @@ -21,6 +21,9 @@ const SettingsModal = const [url, setUrl] = useState(''); const [copied, setCopied] = useState(false); const formData = useSelector((state) => state.form?.form.path); + const [editIndexValue, setEditIndexValue] = useState(0); + const [createIndexValue, setCreateIndexValue] = useState(0); + const [viewIndexValue, setViewIndexValue] = useState(0); const reducer = (path, { type, value }) => { const formCopy = _cloneDeep(path); switch (type) { @@ -227,9 +230,31 @@ const SettingsModal = {t("Who Can Edit This Form")} setRolesState((prev) => ({ ...prev, edit: { ...prev.edit, selectedOption: 'onlyYou' } })) }, - { label: t("You and specified roles"), onClick: () => setRolesState((prev) => ({ ...prev, edit: { ...prev.edit, selectedOption: 'specifiedRoles' } })) }, - ]} dataTestid="edit-submission-role" ariaLabel={t("Edit Submission Role")} /> + { + label: t("Only You"), + onClick: () => { + setRolesState((prev) => ({ + ...prev, + edit: { ...prev.edit, selectedOption: 'onlyYou' } + })); + setEditIndexValue(0); + } + }, + { + label: t("You and specified roles"), + onClick: () => { + setRolesState((prev) => ({ + ...prev, + edit: { ...prev.edit, selectedOption: 'specifiedRoles' } + })); + setEditIndexValue(1); + } + }, + ]} + dataTestid="edit-submission-role" + ariaLabel={t("Edit Submission Role")} + indexValue={editIndexValue} + /> {rolesState.edit.selectedOption === 'onlyYou' && ( @@ -270,10 +295,35 @@ const SettingsModal = ({ ...prev, create: { ...prev.create, isPublic: !prev.create.isPublic } }))} className='field-label' /> - setRolesState((prev) => ({ ...prev, create: { ...prev.create, selectedOption: 'registeredUsers' } })) }, - { label: t("Specific roles"), onClick: () => setRolesState((prev) => ({ ...prev, create: { ...prev.create, selectedOption: 'specifiedRoles' } })) }, - ]} dataTestid="create-submission-role" ariaLabel={t("Create Submission Role")} /> + { + setRolesState((prev) => ({ + ...prev, + create: { ...prev.create, selectedOption: 'registeredUsers' } + })); + setCreateIndexValue(0); + // Set index value for Registered users + } + }, + { + label: t("Specific roles"), + onClick: () => { + setRolesState((prev) => ({ + ...prev, + create: { ...prev.create, selectedOption: 'specifiedRoles' } + })); + setCreateIndexValue(1); // Set index value for Specific roles + } + }, + ]} + dataTestid="create-submission-role" + ariaLabel={t("Create Submission Role")} + indexValue={createIndexValue} +/> + {rolesState.create.selectedOption === 'registeredUsers' && ( @@ -305,13 +355,33 @@ const SettingsModal = {t("Who Can View Submissions")} setRolesState((prev) => ({ ...prev, view: { ...prev.view, selectedOption: 'submitter' } })) }, - { label: t("Submitter and specified roles"), onClick: () => setRolesState((prev) => ({ ...prev, view: { ...prev.view, selectedOption: 'specifiedRoles' } })) }, - ]} - dataTestid="view-submission-role" - ariaLabel={t("View Submission Role")} - /> + items={[ + { + label: t("Submitter"), + onClick: () => { + setRolesState((prev) => ({ + ...prev, + view: { ...prev.view, selectedOption: 'submitter' } + })); + setViewIndexValue(0); // Set index value for Submitter + } + }, + { + label: t("Submitter and specified roles"), + onClick: () => { + setRolesState((prev) => ({ + ...prev, + view: { ...prev.view, selectedOption: 'specifiedRoles' } + })); + setViewIndexValue(0); + } + }, + ]} + dataTestid="view-submission-role" + ariaLabel={t("View Submission Role")} + indexValue={viewIndexValue} +/> + {rolesState.view.selectedOption === 'submitter' && ( From ef38f6c115ad25a93f2af7401e83f4ddc27def3a Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:56:17 +0530 Subject: [PATCH 26/39] python version upgrade in github workflow for api test --- .github/workflows/forms-flow-api-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/forms-flow-api-ci.yml b/.github/workflows/forms-flow-api-ci.yml index 59cb2fe489..0b191f073a 100644 --- a/.github/workflows/forms-flow-api-ci.yml +++ b/.github/workflows/forms-flow-api-ci.yml @@ -87,7 +87,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: [3.12.6] + python-version: [3.12.7] services: postgres: @@ -104,7 +104,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install Docker Compose From 8806b76b857f42a9616432140160ad9065432008 Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Fri, 11 Oct 2024 16:29:16 +0530 Subject: [PATCH 27/39] Update forms-flow-documents-ci.yml --- .github/workflows/forms-flow-documents-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/forms-flow-documents-ci.yml b/.github/workflows/forms-flow-documents-ci.yml index c0aad1d035..ddec9d177e 100644 --- a/.github/workflows/forms-flow-documents-ci.yml +++ b/.github/workflows/forms-flow-documents-ci.yml @@ -34,12 +34,12 @@ jobs: strategy: matrix: - python-version: [3.12.6] + python-version: [3.12.7] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -79,7 +79,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: [3.12.6] + python-version: [3.12.7] services: postgres: @@ -96,7 +96,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install Docker Compose From e94be27407ad665ecc1476a7a07a5d49df1f64af Mon Sep 17 00:00:00 2001 From: Bonymol-aot Date: Sun, 13 Oct 2024 22:44:35 -0700 Subject: [PATCH 28/39] FWF-3542: [Feature] Added form display to mapper --- .../src/components/CustomComponents/settingsModal.js | 8 ++++---- forms-flow-web/src/components/Form/Item/Edit.js | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/forms-flow-web/src/components/CustomComponents/settingsModal.js b/forms-flow-web/src/components/CustomComponents/settingsModal.js index e484943015..d5c555b86a 100644 --- a/forms-flow-web/src/components/CustomComponents/settingsModal.js +++ b/forms-flow-web/src/components/CustomComponents/settingsModal.js @@ -13,8 +13,8 @@ import _camelCase from "lodash/camelCase"; const SettingsModal = ({ show, handleClose, handleConfirm, rolesState, - setRolesState, formDetails, updateFormName, updateFormDescription, formType, - setFormType, newPath, handleFormPathChange }) => { + setRolesState, formDetails, updateFormName, updateFormDescription, formDisplay, + setFormDisplay, newPath, handleFormPathChange }) => { const { t } = useTranslation(); const dispatch = useDispatch(); const [userRoles, setUserRoles] = useState([]); @@ -60,7 +60,7 @@ const SettingsModal = target.type === "checkbox" ? target.checked ? "wizard" : "form" : target.value; - setFormType(value); + setFormDisplay(value); dispatchFormAction({ type: path, value }); }; @@ -214,7 +214,7 @@ const SettingsModal = type="checkbox" id="createCheckbox" label={t("Allow adding multiple pages in this form")} - checked={formType === "wizard"} + checked={formDisplay === "wizard"} onChange={(event) => handleChange("display", event)} diff --git a/forms-flow-web/src/components/Form/Item/Edit.js b/forms-flow-web/src/components/Form/Item/Edit.js index 24abd4a013..ea641882da 100644 --- a/forms-flow-web/src/components/Form/Item/Edit.js +++ b/forms-flow-web/src/components/Form/Item/Edit.js @@ -125,7 +125,7 @@ const Edit = React.memo(() => { description: processListData.description, }); - const [formType, setFormType] = useState(processListData.formType); + const [formDisplay, setFormDisplay] = useState(processListData.formType); const [rolesState, setRolesState] = useState({ edit: { @@ -298,7 +298,8 @@ useEffect(() => { : [], anonymous: rolesState.create.isPublic, parentFormId: parentFormId, - formType: formType, + formType: form.type, + display: formDisplay, processKey: workflow?.value, processName: workflow?.name, id: processListData.id, @@ -447,7 +448,8 @@ useEffect(() => { handleConfirm={handleConfirmSettings} rolesState={rolesState} setRolesState={setRolesState} setFormDetails={setFormDetails} formDetails={formDetails} - updateFormName={updateFormName} formType={formType} setFormType={setFormType} + updateFormName={updateFormName} formDisplay={formDisplay} + setFormDisplay={setFormDisplay} updateFormDescription={updateFormDescription} newPath={newPath} handleFormPathChange={handleFormPathChange}/> From 35b500a48a3b88b98be8903b6029fa5167c0246b Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Mon, 14 Oct 2024 12:06:31 +0530 Subject: [PATCH 29/39] [Feature]: Included form major, minor versions to form by formid api response --- .../src/formsflow_api/services/form_process_mapper.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/forms-flow-api/src/formsflow_api/services/form_process_mapper.py b/forms-flow-api/src/formsflow_api/services/form_process_mapper.py index 5089a512ee..288e9ba2f2 100644 --- a/forms-flow-api/src/formsflow_api/services/form_process_mapper.py +++ b/forms-flow-api/src/formsflow_api/services/form_process_mapper.py @@ -123,6 +123,14 @@ def get_mapper_by_formid(form_id: str, **kwargs): raise PermissionError("Tenant authentication failed.") mapper_schema = FormProcessMapperSchema() response = mapper_schema.dump(mapper) + # Include form versions + version_data = FormHistory.get_latest_version(mapper.parent_form_id) + major_version, minor_version = 1, 0 + if version_data: + major_version = version_data.major_version + minor_version = version_data.minor_version + response["majorVersion"] = major_version + response["minorVersion"] = minor_version if response.get("deleted") is False: return response From 4bd9ab5140afd2195423746b1448fa326b608a01 Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:14:47 +0530 Subject: [PATCH 30/39] FWF-3751: [Bugfix] Fix form loading issue for user with create submission --- forms-flow-api/requirements.txt | 2 +- forms-flow-api/requirements/prod.txt | 2 +- forms-flow-api/src/formsflow_api/models/form_process_mapper.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/forms-flow-api/requirements.txt b/forms-flow-api/requirements.txt index 6e947ccad5..5b0a9897ec 100644 --- a/forms-flow-api/requirements.txt +++ b/forms-flow-api/requirements.txt @@ -28,7 +28,7 @@ exceptiongroup==1.2.2 flask-jwt-oidc==0.7.0 flask-marshmallow==1.2.1 flask-restx==1.3.0 -formsflow_api_utils @ git+https://github.com/shuhaib-aot/forms-flow-ai.git@feature/FWF-3679-subflow-creation-updation#subdirectory=forms-flow-api-utils +formsflow_api_utils @ git+https://github.com/AOT-Technologies/forms-flow-ai.git@develop#subdirectory=forms-flow-api-utils gunicorn==23.0.0 h11==0.14.0 h2==4.1.0 diff --git a/forms-flow-api/requirements/prod.txt b/forms-flow-api/requirements/prod.txt index 5e94b8f72d..dfa553abd2 100644 --- a/forms-flow-api/requirements/prod.txt +++ b/forms-flow-api/requirements/prod.txt @@ -17,4 +17,4 @@ markupsafe PyJWT redis lxml -git+https://github.com/shuhaib-aot/forms-flow-ai.git@feature/FWF-3679-subflow-creation-updation#subdirectory=forms-flow-api-utils \ No newline at end of file +git+https://github.com/AOT-Technologies/forms-flow-ai.git@develop#subdirectory=forms-flow-api-utils \ No newline at end of file diff --git a/forms-flow-api/src/formsflow_api/models/form_process_mapper.py b/forms-flow-api/src/formsflow_api/models/form_process_mapper.py index 6dbe5984b5..5ac71429d4 100644 --- a/forms-flow-api/src/formsflow_api/models/form_process_mapper.py +++ b/forms-flow-api/src/formsflow_api/models/form_process_mapper.py @@ -282,7 +282,7 @@ def find_all_active_by_formid( ) query = cls.add_search_filter(query=query, search=search) query = cls.access_filter(query=query) - query = cls.add_sort_filter( + query = add_sort_filter( sort_by=sort_by, sort_order=sort_order, query=query, From f832a57629f115ab0b5c213051769a17f6c9cd95 Mon Sep 17 00:00:00 2001 From: josephalexantony-aot Date: Mon, 14 Oct 2024 07:18:28 -0700 Subject: [PATCH 31/39] FWF-3739: [Feature] - Save and Publish Layout --- .../src/apiManager/endpoints/index.js | 2 + .../src/apiManager/services/FormServices.js | 10 + .../src/components/Form/Item/Edit.js | 263 +++++++++--------- 3 files changed, 146 insertions(+), 129 deletions(-) diff --git a/forms-flow-web/src/apiManager/endpoints/index.js b/forms-flow-web/src/apiManager/endpoints/index.js index 1ea1788358..d32ce22858 100644 --- a/forms-flow-web/src/apiManager/endpoints/index.js +++ b/forms-flow-web/src/apiManager/endpoints/index.js @@ -31,6 +31,8 @@ const API = { FORM: `${WEB_BASE_URL}/form`, FORM_DESIGN: `${WEB_BASE_URL}/form/form-design`, FORM_IMPORT: `${WEB_BASE_URL}/import`, + PUBLISH: `${WEB_BASE_URL}/form//publish`, + UN_PUBLISH: `${WEB_BASE_URL}/form//unpublish`, FORM_HISTORY: `${WEB_BASE_URL}/form/form-history`, LANG_UPDATE: `${WEB_BASE_URL}/user/locale`, FORM_PROCESSES: `${WEB_BASE_URL}/form/formid`, diff --git a/forms-flow-web/src/apiManager/services/FormServices.js b/forms-flow-web/src/apiManager/services/FormServices.js index 6b48962f71..7b82b08ebd 100644 --- a/forms-flow-web/src/apiManager/services/FormServices.js +++ b/forms-flow-web/src/apiManager/services/FormServices.js @@ -7,6 +7,16 @@ export const formCreate = (formData) => { return RequestService.httpPOSTRequest(API.FORM_DESIGN, formData); }; +export const publish = (mapperId) => { + const publishUrl = replaceUrl(API.PUBLISH, "", mapperId); + return RequestService.httpPOSTRequest(publishUrl); +}; + +export const unPublish = (mapperId) => { + const unPublishUrl = replaceUrl(API.UN_PUBLISH, "", mapperId); + return RequestService.httpPOSTRequest(unPublishUrl); +}; + export const formImport = (importData, data) => { return RequestService.httpMultipartPOSTRequest(API.FORM_IMPORT, importData, data); }; diff --git a/forms-flow-web/src/components/Form/Item/Edit.js b/forms-flow-web/src/components/Form/Item/Edit.js index 310ece3e20..64ec6fc4f7 100644 --- a/forms-flow-web/src/components/Form/Item/Edit.js +++ b/forms-flow-web/src/components/Form/Item/Edit.js @@ -1,9 +1,8 @@ import React, { useReducer, useState, useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import { Card } from "react-bootstrap"; -import { Errors, FormBuilder, Formio } from "@aot-technologies/formio-react"; -import { BackToPrevIcon } from "@formsflow/components"; -import { CustomButton, ConfirmModal } from "@formsflow/components"; +import { Errors, FormBuilder } from "@aot-technologies/formio-react"; +import { CustomButton, ConfirmModal, BackToPrevIcon } from "@formsflow/components"; import { RESOURCE_BUNDLES_DATA } from "../../../resourceBundles/i18n"; import LoadingOverlay from "react-loading-overlay-ts"; import _set from "lodash/set"; @@ -21,22 +20,17 @@ import { formUpdate, validateFormName, } from "../../../apiManager/services/FormServices"; -import { INACTIVE } from "../constants/formListConstants"; -import { formCreate } from "../../../apiManager/services/FormServices"; +import { formCreate, publish, unPublish } from "../../../apiManager/services/FormServices"; import utils from "@aot-technologies/formiojs/lib/utils"; import { setFormFailureErrorData, setFormSuccessData, - setRestoreFormData, - setRestoreFormId, } from "../../../actions/formActions"; import { - saveFormProcessMapperPost, saveFormProcessMapperPut, } from "../../../apiManager/services/processServices"; import _isEquial from "lodash/isEqual"; -import { toast } from "react-toastify"; import { FormBuilderModal } from "@formsflow/components"; import _ from "lodash"; @@ -92,8 +86,6 @@ const Edit = React.memo(() => { const formAuthorization = useSelector((state) => state.process.authorizationDetails); const formData = useSelector((state) => state.form?.form); const [form, dispatchFormAction] = useReducer(reducer, _cloneDeep(formData)); - const publisText = - processListData.status == "active" ? "Unpublish" : "Publish"; const [showFlow, setShowFlow] = useState(false); const [showLayout, setShowLayout] = useState(true); const tenantKey = useSelector((state) => state.tenants?.tenantId); @@ -101,25 +93,28 @@ const Edit = React.memo(() => { const processXmlDiagram = useSelector((state) => state.process?.processXml); const { formId } = useParams(); const [nameError, setNameError] = useState(""); - //const formProcessList = useSelector(state => state.process?.formProcessList); - - //for save form + const [promptNewVersion, setPromptNewVersion] = useState(processListData.promptNewVersion); + const [version, setVersion] = useState({ major: 1, minor: 0 }); + const [isPublished, setIsPublished] = useState(processListData?.status == "active" ? true : false); + const [isPublishLoading, setIsPublishLoading] = useState(false); + const publishText = isPublished ? "Unpublish" : "Publish"; + const [versionSaved, setVersionSaved] = useState(false); + const prviousData = useSelector((state) => state.process?.formPreviousData); const [formSubmitted, setFormSubmitted] = useState(false); const formAccess = useSelector((state) => state.user?.formAccess || []); const submissionAccess = useSelector( (state) => state.user?.submissionAccess || [] ); const previousData = useSelector((state) => state.process?.formPreviousData); - const formDescription = form?.description; const restoredFormData = useSelector( (state) => state.formRestore?.restoredFormData ); const restoredFormId = useSelector( (state) => state.formRestore?.restoredFormId ); - const applicationCount = useSelector( - (state) => state.process?.applicationCount - ); + // const applicationCount = useSelector( + // (state) => state.process?.applicationCount + // ); const [showSaveModal, setShowSaveModal] = useState(false); const [hasRendered, setHasRendered] = useState(false); @@ -225,15 +220,15 @@ const Edit = React.memo(() => { setFormDetails((prevState) => ({ ...prevState, name: formName, - })); + })); dispatchFormAction({ type: "title", formName }); }; const updateFormDescription = (formDescription) => { setFormDetails((prevState) => ({ ...prevState, description: formDescription, - })); - dispatchFormAction({type: "description", formDescription}); + })); + dispatchFormAction({ type: "description", formDescription }); }; @@ -263,37 +258,6 @@ const Edit = React.memo(() => { console.error("Error validating form name:", errorMessage); }); }; - //for save farm - const isMapperSaveNeeded = (newData) => { - return ( - previousData.formName !== newData.title || - previousData.anonymous !== processListData.anonymous || - processListData.anonymous === null || - processListData.formType !== newData.type || - processListData.description !== formDescription - ); - }; - - const setFormProcessDataToVariable = (submittedData) => { - const data = { - anonymous: - processListData.anonymous === null ? false : processListData.anonymous, - formName: submittedData.title, - parentFormId: processListData.parentFormId, - formType: submittedData.type, - status: processListData.status ? processListData.status : INACTIVE, - taskVariables: processListData.taskVariables - ? processListData.taskVariables - : [], - id: processListData.id, - formId: submittedData._id, - formTypeChanged: previousData.formType !== submittedData.type, - titleChanged: previousData.formName !== submittedData.title, - anonymousChanged: previousData.anonymous !== processListData.anonymous, - description: formDescription, - }; - return data; - }; const isFormComponentsChanged = () => { if (restoredFormData && restoredFormId) { return true; @@ -340,9 +304,6 @@ const Edit = React.memo(() => { ); }; - const isNewMapperNeeded = () => { - return previousData.formName !== form.title && applicationCount > 0; - }; const handleConfirmSettings = () => { const parentFormId = processListData.parentFormId; const mapper = { @@ -377,7 +338,7 @@ const Edit = React.memo(() => { resourceDetails: {}, roles: rolesState.edit.selectedOption === "specifiedRoles" ? rolesState.edit.selectedRoles : [], ...(rolesState.edit.selectedOption === "onlyYou" && { userName: preferred_userName }) - }, + }, form: { resourceId: parentFormId, resourceDetails: {}, @@ -407,39 +368,9 @@ const Edit = React.memo(() => { newFormData.parentFormId = previousData.parentFormId; formUpdate(newFormData._id, newFormData) - .then((res) => { - const { data: submittedData } = res; - if (isMapperSaveNeeded(submittedData)) { - const data = setFormProcessDataToVariable(submittedData); - - // PUT request : when application count is zero. - // POST request with updated version : when application count is positive. - - if (isNewMapperNeeded()) { - data["version"] = String(+previousData.version + 1); - data["processKey"] = previousData.processKey; - data["processName"] = previousData.processName; - data.parentFormId = processListData.parentFormId; - dispatch(saveFormProcessMapperPost(data)); - } else { - // For hadling uploaded forms case. - - if (processListData && processListData.id) { - // For created forms we would be having a mapper - - dispatch(saveFormProcessMapperPut(data)); - } else { - // For uploaded forms we have to create new mapper. - - dispatch(saveFormProcessMapperPost(data)); - } - } - } - dispatch(setRestoreFormData({})); - dispatch(setRestoreFormId(null)); - toast.success(t("Form saved")); - dispatch(setFormSuccessData("form", submittedData)); - Formio.cache = {}; + .then(() => { + setPromptNewVersion(false); + setVersionSaved(true); }) .catch((err) => { const error = err.response?.data || err.message; @@ -452,7 +383,6 @@ const Edit = React.memo(() => { const backToForm = () => { dispatch(push(`${redirectUrl}form/`)); - console.log("back", redirectUrl); }; const handleHistory = () => { @@ -471,10 +401,6 @@ const Edit = React.memo(() => { console.log("saveFlow"); }; - const saveLayout = () => { - setShowSaveModal(true); - }; - const discardChanges = () => { console.log("discardChanges"); }; @@ -484,10 +410,6 @@ const Edit = React.memo(() => { setNewActionModal(true); }; - const handlePublish = () => { - console.log("publish"); - }; - const handleChange = (path, event) => { setFormSubmitted(false); const { target } = event; @@ -543,6 +465,105 @@ const Edit = React.memo(() => { const formChange = (newForm) => dispatchFormAction({ type: "formChange", value: newForm }); + const handlePublish = () => { + if (!promptNewVersion) { + setIsPublishLoading(true); + if (processListData.status === "active") { + unPublish(processListData.id) + .then(() => { + setPromptNewVersion(true); + setIsPublishLoading(false); + setIsPublished(!isPublished); + setVersionSaved(true); + }) + .catch((err) => { + setIsPublishLoading(false); + const error = err.response?.data || err.message; + dispatch(setFormFailureErrorData("form", error)); + }); + } + else { + publish(processListData.id) + .then(() => { + setIsPublishLoading(false); + setIsPublished(!isPublished); + setVersionSaved(true); + }) + .catch((err) => { + setIsPublishLoading(false); + const error = err.response?.data || err.message; + dispatch(setFormFailureErrorData("form", error)); + }) + .finally(() => { + setIsPublishLoading(false); + dispatch(push(`${redirectUrl}form/`)); + }); + } + + } + else { + setVersion((prevVersion) => ({ + ...prevVersion, + major: ((processListData.majorVersion + 1) + ".0"), // Increment the major version + minor: processListData.majorVersion + "." + (processListData.minorVersion + 1), // Reset the minor version to 0 + })); + setShowSaveModal(true); + } + + + }; + + const handleVersioning = () => { + setVersion((prevVersion) => ({ + ...prevVersion, + major: ((processListData.majorVersion + 1) + ".0"), // Increment the major version + minor: processListData.majorVersion + "." + (processListData.minorVersion + 1), // Reset the minor version to 0 + })); + + //get mapper data + + //call for new version save + setShowSaveModal(true); + }; + + const saveAsNewVersion = async () => { + setFormSubmitted(true); + const newFormData = manipulatingFormData( + form, + MULTITENANCY_ENABLED, + tenantKey, + formAccess, + submissionAccess + ); + const oldFormData = manipulatingFormData( + formData, + MULTITENANCY_ENABLED, + tenantKey, + formAccess, + submissionAccess + ); + + const newPathAndName = "-v" + Math.random().toString(16).slice(9); + oldFormData.path += newPathAndName; + oldFormData.name += newPathAndName; + await formUpdate(oldFormData._id, oldFormData); + newFormData.componentChanged = true; + newFormData.newVersion = true; + newFormData.parentFormId = prviousData.parentFormId; + delete newFormData.machineName; + delete newFormData._id; + formCreate(newFormData) + .then(() => { + setPromptNewVersion(false); + }) + .catch((err) => { + const error = err.response.data || err.message; + dispatch(setFormFailureErrorData("form", error)); + }) + .finally(() => { + setFormSubmitted(false); + }); + }; return (
@@ -559,7 +580,7 @@ const Edit = React.memo(() => { updateFormName={updateFormName} formDisplay={formDisplay} setFormDisplay={setFormDisplay} updateFormDescription={updateFormDescription} newPath={newPath} - handleFormPathChange={handleFormPathChange}/> + handleFormPathChange={handleFormPathChange} /> @@ -569,20 +590,15 @@ const Edit = React.memo(() => {
{form.title}
- - {processListData.status == "active" ? ( + + {isPublished ? ( <>
) : (
)} - {processListData.status == "active" - ? t("Live") - : t("Draft")} + {isPublished ? t("Live") : t("Draft")}
@@ -606,10 +622,11 @@ const Edit = React.memo(() => {
@@ -654,10 +671,11 @@ const Edit = React.memo(() => { variant="primary" size="md" className="mx-2" + disabled={isPublished} label={ {(t) => t("Save Layout")} } - onClick={saveLayout} + onClick={versionSaved ? () => saveFormData() : () => handleVersioning()} dataTestid="save-form-layout" ariaLabel={t("Save Form Layout")} /> @@ -789,9 +807,8 @@ const Edit = React.memo(() => { )} {showLayout && (
Flow @@ -825,24 +842,12 @@ const Edit = React.memo(() => { {(t) => t("Save Your Changes")}} - message={ - - {(t) => - t( - "Saving as an incrimental version will affect previous submissions. Saving as a new full version will not affect previous submissions." - ) - } - - } + message={{(t) => t("Saving as an incremental version will affect previous submissions. Saving as a new full version will not affect previous submissions.")}} primaryBtnAction={saveFormData} onClose={closeSaveModal} - secondayBtnAction={"add secondary button action"} - primaryBtnText={ - {(t) => t("Save as Version 3.5")} - } - secondaryBtnText={ - {(t) => t("Save as Version 4.0")} - } + secondayBtnAction={saveAsNewVersion} + primaryBtnText={{(t) => t(`Save as Version ${version.minor}`)}} + secondaryBtnText={{(t) => t(`Save as Version ${version.major}`)}} size="md" />
From 30cefb6529fba1d17af9c1a99dec158bad742593 Mon Sep 17 00:00:00 2001 From: abilpraju-aot Date: Mon, 14 Oct 2024 08:46:51 -0700 Subject: [PATCH 32/39] updated payload of name --- forms-flow-web/src/components/Form/Item/Edit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forms-flow-web/src/components/Form/Item/Edit.js b/forms-flow-web/src/components/Form/Item/Edit.js index 310ece3e20..a7247279be 100644 --- a/forms-flow-web/src/components/Form/Item/Edit.js +++ b/forms-flow-web/src/components/Form/Item/Edit.js @@ -508,10 +508,10 @@ const Edit = React.memo(() => { ); const newPathAndName = - "duplicate-version-" + Math.random().toString(16).slice(9); + "duplicate-version-" + Math.random().toString(16).slice(9); newFormData.path = newPathAndName; newFormData.title = form.title; - newFormData.name = form.title; + newFormData.name = newPathAndName; newFormData.componentChanged = true; delete newFormData.machineName; delete newFormData.parentFormId; From 6117299cc0039b0e706c19219449109c6a0ae9d3 Mon Sep 17 00:00:00 2001 From: shuhaib-aot Date: Tue, 15 Oct 2024 11:29:16 +0530 Subject: [PATCH 33/39] FWF-3742 [feature] implemented save flow and code optimization --- .../src/formsflow_api/services/process.py | 2 +- forms-flow-web/src/actions/actionConstants.js | 1 + forms-flow-web/src/actions/processActions.js | 7 + .../src/apiManager/endpoints/index.js | 2 +- .../apiManager/services/processServices.js | 29 +- forms-flow-web/src/components/BaseRouting.jsx | 13 +- .../src/components/Form/Item/Edit.js | 144 ++---- .../src/components/Form/Item/FlowEdit.js | 146 ++++++ .../Modeler/Editors/BpmnEditor/index.js | 465 +++++------------- forms-flow-web/src/helper/processHelper.js | 86 ++++ forms-flow-web/src/modules/processReducer.js | 3 + 11 files changed, 408 insertions(+), 490 deletions(-) create mode 100644 forms-flow-web/src/components/Form/Item/FlowEdit.js create mode 100644 forms-flow-web/src/helper/processHelper.js diff --git a/forms-flow-api/src/formsflow_api/services/process.py b/forms-flow-api/src/formsflow_api/services/process.py index 02cd2452e4..adabfef1ec 100644 --- a/forms-flow-api/src/formsflow_api/services/process.py +++ b/forms-flow-api/src/formsflow_api/services/process.py @@ -98,7 +98,7 @@ def _upate_process_name_and_id( # Prepend the XML declaration updated_xml = '\n' + updated_xml - return updated_xml + return updated_xml.encode("utf-8") @classmethod @user_context diff --git a/forms-flow-web/src/actions/actionConstants.js b/forms-flow-web/src/actions/actionConstants.js index 851451c95f..3d04a21cc1 100644 --- a/forms-flow-web/src/actions/actionConstants.js +++ b/forms-flow-web/src/actions/actionConstants.js @@ -173,6 +173,7 @@ const ACTION_CONSTANTS = { // Process RESET_PROCESS: "RESET_PROCESS", + SET_PROCESS_DATA: "SET_PROCESS_DATA", FORM_STATUS_LOADING: "FORM_STATUS_LOADING", // Tenant RESET_TENANT: "RESET_TENANT", diff --git a/forms-flow-web/src/actions/processActions.js b/forms-flow-web/src/actions/processActions.js index 30b0ee83e9..80bd56e3fa 100644 --- a/forms-flow-web/src/actions/processActions.js +++ b/forms-flow-web/src/actions/processActions.js @@ -29,6 +29,13 @@ export const setProcessLoadError = (data) => (dispatch) => { }); }; +export const setProcessData = (data) => (dispatch) => { + dispatch({ + type: ACTION_CONSTANTS.SET_PROCESS_DATA, + payload: data, + }); +}; + export const setProcessActivityLoadError = (data) => (dispatch) => { dispatch({ type: ACTION_CONSTANTS.IS_PROCESS_ACTIVITY_LOAD_ERROR, diff --git a/forms-flow-web/src/apiManager/endpoints/index.js b/forms-flow-web/src/apiManager/endpoints/index.js index 1ea1788358..0737b0ef9d 100644 --- a/forms-flow-web/src/apiManager/endpoints/index.js +++ b/forms-flow-web/src/apiManager/endpoints/index.js @@ -25,7 +25,7 @@ const API = { GET_ALL_APPLICATIONS_STATUS: `${WEB_BASE_URL}/application/status/list`, GET_PROCESS_DEFINITION: `${BPM_BASE_URL_EXT}/v1/process-definition/key/`, PROCESSES_XML: `${BPM_BASE_URL_EXT}/v1/process-definition/key//xml`, - GET_PROCESSES_XML: `${WEB_BASE_URL}/process`, + GET_PROCESSES_DETAILS: `${WEB_BASE_URL}/process`, DMN_XML: `${BPM_BASE_URL_EXT}/v1/decision-definition/key//xml`, PROCESS_ACTIVITIES: `${BPM_BASE_URL_EXT}/v1/process-instance//activity-instances`, FORM: `${WEB_BASE_URL}/form`, diff --git a/forms-flow-web/src/apiManager/services/processServices.js b/forms-flow-web/src/apiManager/services/processServices.js index 7b77a66029..37f6e56cec 100644 --- a/forms-flow-web/src/apiManager/services/processServices.js +++ b/forms-flow-web/src/apiManager/services/processServices.js @@ -13,7 +13,6 @@ import { setProcessDiagramXML, setProcessDiagramLoading, setFormPreviosData, - setProcessXml, setApplicationCountResponse, setUnPublishApiError, setResetProcess, @@ -221,6 +220,7 @@ export const getFormProcesses = (formId, ...rest) => { ) .then((res) => { if (res.data) { + console.log("res.data", res.data); dispatch(setFormPreviosData(res.data)); dispatch(setFormProcessesData(res.data)); // need to check api and put exact respose @@ -241,26 +241,13 @@ export const getFormProcesses = (formId, ...rest) => { }; }; -export const getProcessXml = (processKey) => { -return (dispatch) => { - RequestService.httpGETRequest( - `${API.GET_PROCESSES_XML}/${processKey}`, - {}, - StorageService.get(StorageService.User.AUTH_TOKEN), - true - ) - .then((res, err) => { - if (res.data) { - dispatch(setProcessXml(res.data.processData)); - } else { - console.log(err); - } - }) - .catch((error) => { - console.log(error); - }); - }; -}; + export const getProcessDetails = (processKey) => + RequestService.httpGETRequest(`${API.GET_PROCESSES_DETAILS}/${processKey}`); + + export const updateProcess = ({id,data,type}) => { + return RequestService.httpPUTRequest(`${API.GET_PROCESSES_DETAILS}/${id}`, + {processData:data,processType:type}); + }; // fetching task variables export const fetchTaskVariables = (formId) =>{ diff --git a/forms-flow-web/src/components/BaseRouting.jsx b/forms-flow-web/src/components/BaseRouting.jsx index 9aee717778..842227ec78 100644 --- a/forms-flow-web/src/components/BaseRouting.jsx +++ b/forms-flow-web/src/components/BaseRouting.jsx @@ -61,7 +61,18 @@ const BaseRouting = React.memo( return (
- + { const [showLayout, setShowLayout] = useState(true); const tenantKey = useSelector((state) => state.tenants?.tenantId); const redirectUrl = MULTITENANCY_ENABLED ? `/tenant/${tenantKey}/` : "/"; - const processXmlDiagram = useSelector((state) => state.process?.processXml); const { formId } = useParams(); const [nameError, setNameError] = useState(""); - //const formProcessList = useSelector(state => state.process?.formProcessList); + + + // flow edit + const [isProcessDetailsLoading, setIsProcessDetailsLoading] = useState(false); + //for save form const [formSubmitted, setFormSubmitted] = useState(false); @@ -123,7 +126,6 @@ const Edit = React.memo(() => { const [showSaveModal, setShowSaveModal] = useState(false); const [hasRendered, setHasRendered] = useState(false); - const [isLoadingDiagram, setIsLoadingDiagram] = useState(true); const formPath = useSelector((state) => state.form.form.path); const [newPath, setNewPath] = useState(formPath); @@ -199,25 +201,24 @@ const Edit = React.memo(() => { setShowFlow(true); setShowLayout(false); }; + useEffect(() => { if (formId) { // Fetch form processes with formId - dispatch(getFormProcesses(formId)); + dispatch(getFormProcesses(formId,(err,data)=>{ + if(err) return; + setIsProcessDetailsLoading(true); + getProcessDetails(data.processKey).then((response)=>{ + const { data } = response; + dispatch(setProcessData(data)); + setIsProcessDetailsLoading(false); + } + ); + })); } }, [formId]); - useEffect(() => { - if (workflow?.value) { - // Fetch workflow diagram XML when the workflow value is present - dispatch(getProcessXml(workflow.value)); - } - - if (workflow?.value && processXmlDiagram) { - // Set the process diagram XML and stop the loading spinner when both workflow and XML are available - dispatch(setProcessDiagramXML(processXmlDiagram)); - setIsLoadingDiagram(false); - } - }, [workflow?.value, processXmlDiagram]); + @@ -459,17 +460,13 @@ const Edit = React.memo(() => { console.log("handleHistory"); }; - const handlePreviewAndVariables = () => { - console.log("handlePreviewAndVariables"); - }; + const handlePreview = () => { console.log("handlePreview"); }; - const saveFlow = () => { - console.log("saveFlow"); - }; + const saveLayout = () => { setShowSaveModal(true); @@ -692,92 +689,8 @@ const Edit = React.memo(() => {
- - -
-
-
Flow
-
- } - label={ - {(t) => t("History")} - } - onClick={handleHistory} - dataTestid="flow-history-button-testid" - ariaLabel={t("Flow History Button")} - /> - - {(t) => t("Preview & Variables")} - - } - onClick={handlePreviewAndVariables} - dataTestid="preview-and-variables-testid" - ariaLabel={t("{Preview and Variables Button}")} - /> - {/* {(t) => t("Switch to BPMN")}} - onClick={setWorkflowType} - dataTestid={"cancelBtndataTestid"} - ariaLabel={"cancelBtnariaLabel"} - /> */} -
-
-
- {(t) => t("Save Flow")} - } - onClick={saveFlow} - dataTestid="save-flow-layout" - ariaLabel={t("Save Flow Layout")} - /> - - {(t) => t("Discard Changes")} - - } - onClick={discardChanges} - dataTestid="discard-flow-changes-testid" - ariaLabel={t("Discard Flow Changes")} - /> -
-
-
- -
- {isLoadingDiagram ? ( - "loading..." // TBD: add a loader here - ) : ( - - )} -
-
-
+ {isProcessDetailsLoading ? <>loading... : + }
{showFlow && (
{ } size="md" /> -
); }); diff --git a/forms-flow-web/src/components/Form/Item/FlowEdit.js b/forms-flow-web/src/components/Form/Item/FlowEdit.js new file mode 100644 index 0000000000..571d847bfa --- /dev/null +++ b/forms-flow-web/src/components/Form/Item/FlowEdit.js @@ -0,0 +1,146 @@ +import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react'; +import { CustomButton, HistoryIcon } from "@formsflow/components"; +import { Card } from 'react-bootstrap'; +import { useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; +import { setProcessData } from '../../../actions/processActions.js'; +import BpmnEditor from '../../Modeler/Editors/BpmnEditor/index.js'; +import { updateProcess } from "../../../apiManager/services/processServices.js"; +import { toast } from 'react-toastify'; +import { createXMLFromModeler, validateProcessNames, compareXML } from '../../../helper/processHelper.js'; +import { ERROR_LINTING_CLASSNAME } from '../../Modeler/constants/bpmnModelerConstants.js'; + +const FlowEdit = forwardRef((_, ref) => { + const { t } = useTranslation(); + const dispatch = useDispatch(); + const bpmnRef = useRef(); + const processData = useSelector((state) => state.process?.processData); + const [lintErrors, setLintErrors] = useState([]); + const [historyModalShow, setHistoryModalShow] = useState(false); + + // handle history modal + const handleHistoryModal = () => setHistoryModalShow(!historyModalShow); + + //validate any erros in bpmn lint + const validateBpmnLintErrors = () => { + // only return false if there are errors, warnings are ok + let hasErrors = false; + + for (const key in lintErrors) { + const err = lintErrors[key]; + err.forEach((x) => { + // Only toast errors, not warnings + if (x.category === "error") { + hasErrors = true; + toast.error(t(x.message)); + } + }); + } + return hasErrors ? false : true; + }; + + // validate the xml data and any erros in bpmn lint + const validateProcess = (xml) => { + if (document.getElementsByClassName(ERROR_LINTING_CLASSNAME).length > 0) { + return validateBpmnLintErrors(); + } + if (!validateProcessNames(xml)) { + toast.error(t("Process name(s) must not be empty")); + return false; + } + return true; + }; + + + + const saveFlow = async () => { + const bpmnModeler = bpmnRef.current?.getBpmnModeler(); + const xml = await createXMLFromModeler(bpmnModeler); + + //if xml is same as existing process data, no need to update + const isEqual = await compareXML(processData?.processData, xml); + if (isEqual) { + toast.success(t("Process updated successfully")); + return; + } + if (!validateProcess(xml)) { + return; + } + + updateProcess({ type:"BPMN", id: processData.id, data:xml }).then((response) => { + dispatch(setProcessData(response.data)); + toast.success(t("Process updated successfully")); + }); + }; + + //handle discard changes + const handleDiscard = ()=>{ + if(bpmnRef.current){ + //import the existing process data to bpmn + bpmnRef.current?.handleImport(processData?.processData); + } +}; + + useImperativeHandle(ref, () => ({ + saveFlow, + })); + + return ( + + + +
+
+
Flow
+
+ } + label={t("History")} + onClick={handleHistoryModal} + dataTestid="flow-history-button-testid" + ariaLabel={t("Flow History Button")} + /> + console.log("handlePreviewAndVariables")} + dataTestid="preview-and-variables-testid" + ariaLabel={t("{Preview and Variables Button}")} + /> + +
+
+
+ + +
+
+
+ + + +
+ ); +}); + +export default FlowEdit; diff --git a/forms-flow-web/src/components/Modeler/Editors/BpmnEditor/index.js b/forms-flow-web/src/components/Modeler/Editors/BpmnEditor/index.js index e96db1f72e..5e982c2e4d 100644 --- a/forms-flow-web/src/components/Modeler/Editors/BpmnEditor/index.js +++ b/forms-flow-web/src/components/Modeler/Editors/BpmnEditor/index.js @@ -1,33 +1,4 @@ -// TBD: Remove unnecessory buttons those are kept for near future ref. -// const display = false; is added for hideing those elaments. - -import React, { useCallback, useEffect, useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import "../Editor.scss"; -import Button from "react-bootstrap/Button"; -import { toast } from "react-toastify"; -import { useTranslation } from "react-i18next"; -import { extractDataFromDiagram } from "../../helpers/helper"; -import { createXML } from "../../helpers/deploy"; -import { MULTITENANCY_ENABLED, PUBLIC_WORKFLOW_ENABLED } from "../../../../constants/constants"; -import { deployBpmnDiagram } from "../../../../apiManager/services/bpmServices"; -import Loading from "../../../../containers/Loading"; -import { push } from "connected-react-router"; -import { - SUCCESS_MSG, - ERROR_MSG, - ERROR_LINTING_CLASSNAME, -} from "../../constants/bpmnModelerConstants"; - -import { - fetchDiagram, -} from "../../../../apiManager/services/processServices"; - -import { - setProcessDiagramLoading, - setProcessDiagramXML, - setWorkflowAssociation, -} from "../../../../actions/processActions"; +import React, { useCallback, useEffect, useState, forwardRef, useImperativeHandle } from "react"; import BpmnModeler from "bpmn-js/lib/Modeler"; import "bpmn-js/dist/assets/diagram-js.css"; @@ -45,335 +16,129 @@ import camundaModdleDescriptors from "camunda-bpmn-moddle/resources/camunda"; import lintModule from "bpmn-js-bpmnlint"; import "bpmn-js-bpmnlint/dist/assets/css/bpmn-js-bpmnlint.css"; import linterConfig from "../../lint-rules/packed-config"; + +const BpmnEditor = forwardRef(({ bpmnXml, setLintErrors }, ref) => { + const [bpmnModeler, setBpmnModeler] = useState(null); + + + + const containerRef = useCallback((node) => { + if (node !== null) { + initializeModeler(); + } + }, []); + + const initializeModeler = () => { + setBpmnModeler( + new BpmnModeler({ + container: "#canvas", + propertiesPanel: { + parent: "#js-properties-panel", + }, + linting: { + bpmnlint: linterConfig, + active: true, + }, + additionalModules: [ + BpmnPropertiesPanelModule, + BpmnPropertiesProviderModule, + CamundaPlatformPropertiesProviderModule, + CamundaExtensionModule, + lintModule, + ], + moddleExtensions: { + camunda: camundaModdleDescriptors, + }, + }) + ); + }; -export default React.memo( - ({ processKey, tenant, isNewDiagram, bpmnXml}) => { - const { t } = useTranslation(); - const dispatch = useDispatch(); - const diagramXML = useSelector((state) => state.process.processDiagramXML); - const [bpmnModeler, setBpmnModeler] = useState(null); - const tenantKey = useSelector((state) => state.tenants?.tenantId); - const [applyAllTenants, setApplyAllTenants] = useState(false); - const [lintErrors, setLintErrors] = useState([]); - const [deploymentLoading, setDeploymentLoading] = useState(false); - const redirectUrl = MULTITENANCY_ENABLED ? `/tenant/${tenantKey}/` : "/"; - const display = false; - - // const [processName,setProcessName] = useState(true); - // const bpmPropertyInput = document.getElementById("bio-properties-panel-name")?.value; - - const containerRef = useCallback((node) => { - if (node !== null) { - initializeModeler(); - } - }, []); - - const cancel = () => { - dispatch(push(`${redirectUrl}processes`)); - }; - - const initializeModeler = () => { - setBpmnModeler( - new BpmnModeler({ - container: "#canvas", - propertiesPanel: { - parent: "#js-properties-panel", - }, - linting: { - bpmnlint: linterConfig, - active: true, - }, - additionalModules: [ - BpmnPropertiesPanelModule, - BpmnPropertiesProviderModule, - CamundaPlatformPropertiesProviderModule, - CamundaExtensionModule, - lintModule, - ], - moddleExtensions: { - camunda: camundaModdleDescriptors, - }, - }) - ); - }; - useEffect(() => { - if (PUBLIC_WORKFLOW_ENABLED) { - tenant === null || tenant === undefined ? setApplyAllTenants(true) - : setApplyAllTenants(false); - } - if (diagramXML) { - dispatch(setProcessDiagramLoading(true)); - dispatch(setProcessDiagramXML(diagramXML)); - } else if (processKey && !isNewDiagram && !bpmnXml) { - dispatch(setProcessDiagramLoading(true)); - dispatch(fetchDiagram(processKey, tenant)); - } else { - dispatch(setProcessDiagramLoading(false)); - } - }, [processKey, tenant, dispatch]); - - useEffect(() => { - if (bpmnXml && bpmnModeler) { - bpmnModeler - .importXML(bpmnXml) - .then(({ warnings }) => { - if (warnings.length) { - console.log("Warnings", warnings); - } - // Add event listeners for bpmn linting - bpmnModeler.on("linting.completed", function (event) { - setLintErrors(event.issues); - }); - }) - .catch((err) => { - handleError(err, "BPMN Import Error: "); - }); - } - }, [diagramXML, bpmnModeler, bpmnXml]); - - const handleApplyAllTenants = () => { - setApplyAllTenants(!applyAllTenants); - }; - - const deployProcess = async () => { - let xml = await createXML(bpmnModeler); - - const isValidated = await validateProcess(xml); - if (!isValidated) { - toast.error(t(ERROR_MSG)); - } else { - // Deploy to Camunda - deployXML(xml); - } - }; - const validateProcess = async (xml) => { - // If the BPMN Linting is active then check for linting errors, else check for Camunda API errors - // Check for linting errors in the modeler view - if (document.getElementsByClassName(ERROR_LINTING_CLASSNAME).length > 0) { - validateBpmnLintErrors(); - return false; - } - - // Check for undefined process names - if (!validateProcessNames(xml)) return false; - - return true; - }; - - const createBpmnForm = (xml) => { - const form = new FormData(); - - const deploymentName = extractDataFromDiagram(xml).name; - - // Deployment Name - form.append("deployment-name", deploymentName); - // Deployment Source - form.append("deployment-source", "Camunda Modeler"); - // Tenant ID - if (tenantKey && !applyAllTenants && PUBLIC_WORKFLOW_ENABLED) { - form.append("tenant-id", tenantKey); - } - //If the env value is false,and Multitenancy is enabled, then by default it will create a tenant based workflow. - if (MULTITENANCY_ENABLED && !PUBLIC_WORKFLOW_ENABLED) { - form.append("tenant-id", tenantKey); - } - // Make sure that we do not re-deploy already existing deployment - form.append("enable-duplicate-filtering", "true"); - // Create 'bpmn file' using blob which includes the xml of the process - const blob = new Blob([xml], { type: "text/bpmn" }); - // TODO: How to name the file - let filename = deploymentName.replaceAll(" / ", "-"); - //filename = filename.replaceAll(' / ', ''); - - form.append("upload", blob, filename + ".bpmn"); + useEffect(() => { + handleImport(bpmnXml); + }, [bpmnXml, bpmnModeler]); - return form; - }; - const deployXML = (xml) => { - const form = createBpmnForm(xml); - setDeploymentLoading(true); - deployBpmnDiagram(form) - .then((res) => { - if (res?.data) { - toast.success(t(SUCCESS_MSG)); - setDeploymentLoading(false); - dispatch(push(`${redirectUrl}processes`)); - } else { - setDeploymentLoading(false); - toast.error(t(ERROR_MSG)); + const handleImport = (bpmnXml) => { + if (bpmnXml && bpmnModeler) { + bpmnModeler + .importXML(bpmnXml) + .then(({ warnings }) => { + if (warnings.length) { + console.log("Warnings", warnings); } + // Add event listeners for bpmn linting + bpmnModeler.on("linting.completed", function (event) { + setLintErrors(event.issues || []); + }); }) - .catch((error) => { - setDeploymentLoading(false); - showCamundaHTTTPErrors(error); - }); - }; - const showCamundaHTTTPErrors = (error) => { - const errors = error.response.data.details; - for (var key in errors) { - var value = errors[key]; - value.errors.forEach((x) => { - toast.error(t(x.message)); - }); - value.warnings.forEach((x) => { - toast.warn(t(x.message)); + .catch((err) => { + handleError(err, "BPMN Import Error: "); }); - } - }; - - const validateBpmnLintErrors = () => { - // only return false if there are errors, warnings are ok - let hasErrors = false; - - for (var key in lintErrors) { - var err = lintErrors[key]; - err.forEach((x) => { - // Only toast errors, not warnings - if (x.category === "error") { - hasErrors = true; - toast.error(t(x.message)); - } - }); - } - return hasErrors ? false : true; - }; - - const validateProcessNames = (xml) => { - let isValidated = true; - // Check for undefined process names - if ( - !extractDataFromDiagram(xml).name || - extractDataFromDiagram(xml).name.includes("undefined") - ) { - toast.error(t("Process name(s) must not be empty")); - isValidated = false; - } - - return isValidated; - }; - - - - const handleExport = async () => { - let xml = await createXML(bpmnModeler); - - const isValidated = await validateProcess(xml); - if (isValidated) { - const element = document.createElement("a"); - const file = new Blob([xml], { type: "text/bpmn" }); - element.href = URL.createObjectURL(file); - let deploymentName = extractDataFromDiagram(xml).name; - deploymentName = deploymentName.replaceAll(" / ", "-") + ".bpmn"; - element.download = deploymentName.replaceAll(" ", ""); - document.body.appendChild(element); - element.click(); - } - }; - - const handleError = () => { - document.getElementById("inputWorkflow").value = null; - dispatch(setWorkflowAssociation(null)); - //setShowModeler(false); - }; - - const zoom = () => { - bpmnModeler.get("zoomScroll").stepZoom(1); - }; - - const zoomOut = () => { - bpmnModeler.get("zoomScroll").stepZoom(-1); - }; - const zoomReset = () => { - bpmnModeler.get("zoomScroll").reset(); - }; - - return ( - <> - {display &&
-
- {MULTITENANCY_ENABLED && PUBLIC_WORKFLOW_ENABLED ? ( - - ) : null} - + } + }; + + useImperativeHandle(ref, () => ({ + getBpmnModeler: () => bpmnModeler, + handleImport: (bpmnXml) => handleImport(bpmnXml), + })); + + const handleError = () => { + document.getElementById("inputWorkflow").value = null; + }; + + const zoom = () => { + bpmnModeler.get("zoomScroll").stepZoom(1); + }; + + const zoomOut = () => { + bpmnModeler.get("zoomScroll").stepZoom(-1); + }; + + const zoomReset = () => { + bpmnModeler.get("zoomScroll").reset(); + }; + + return ( +
+
+
+ +
+
- - + -
-
} -
-
- - -
-
-