diff --git a/.github/workflows/chart-test.yml b/.github/workflows/chart-test.yml index d90e1a36..a39ece31 100644 --- a/.github/workflows/chart-test.yml +++ b/.github/workflows/chart-test.yml @@ -96,6 +96,15 @@ jobs: push: true tags: kind-registry:5000/credential-issuer-processes-worker:testing + - name: Build reissuance app + id: build-reissuance-app-image + uses: docker/build-push-action@v6 + with: + context: . + file: ./docker/Dockerfile-credential-reissuance-app + push: true + tags: kind-registry:5000/credential-reissuance-app:testing + - name: Set up Helm uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4 with: diff --git a/.github/workflows/credential-reissuance-app-docker.yml b/.github/workflows/credential-reissuance-app-docker.yml new file mode 100644 index 00000000..888407ea --- /dev/null +++ b/.github/workflows/credential-reissuance-app-docker.yml @@ -0,0 +1,88 @@ +############################################################### +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################### + +name: Build Credential Reissuance App Image + +on: + push: + paths: + # service and transitive paths + - 'src/**' + # workflow file + - '.github/workflows/credential-reissuance-app-docker.yml' + # dockerfile + - 'docker/Dockerfile-credential-reissuance-app' + + branches: + - 'main' + workflow_dispatch: + +env: + IMAGE_NAMESPACE: "tractusx" + IMAGE_NAME: "ssi-credential-reissuance-app" + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Login to DockerHub + if: github.event_name != 'pull_request' + uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + with: + username: ${{ secrets.DOCKER_HUB_USER }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3.4.0 + + - name: Docker meta + id: meta + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 + with: + images: ${{ env.IMAGE_NAMESPACE }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=main + type=raw,value=${{ github.sha }} + + - name: Build and push Docker image + uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c # v6.3.0 + with: + context: . + file: ./docker/Dockerfile-credential-reissuance-app + platforms: linux/amd64, linux/arm64 + pull: true + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + # https://github.com/peter-evans/dockerhub-description + - name: Update Docker Hub description + if: github.event_name != 'pull_request' + uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae # v4.0.0 + with: + username: ${{ secrets.DOCKER_HUB_USER }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + repository: ${{ env.IMAGE_NAMESPACE }}/${{ env.IMAGE_NAME }} + readme-filepath: ./docker/notice-credential-reissuance-app.md diff --git a/.github/workflows/owasp-zap.yml b/.github/workflows/owasp-zap.yml index 497b2538..cb5a6b0c 100644 --- a/.github/workflows/owasp-zap.yml +++ b/.github/workflows/owasp-zap.yml @@ -95,7 +95,16 @@ jobs: file: ./docker/Dockerfile-credential-expiry-app push: true tags: kind-registry:5000/credential-expiry-app:testing - + + - name: Build Reissuance image + id: build-reissuance-image + uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c # v6.3.0 + with: + context: . + file: ./docker/Dockerfile-credential-reissuance-app + push: true + tags: kind-registry:5000/credential-reissuance-app:testing + - name: Add bitnami repo run: | helm repo add bitnami https://charts.bitnami.com/bitnami diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f895bd52..1a609bb1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,6 +103,9 @@ jobs: - image: tractusx/ssi-credential-expiry-app dockerfile: ./docker/Dockerfile-credential-expiry-app dockernotice: ./docker/notice-credential-expiry-app.md + - image: tractusx/ssi-credential-reissuance-app + dockerfile: ./docker/Dockerfile-credential-reissuance-app + dockernotice: ./docker/notice-credential-reissuance-app.md outputs: app-version: ${{ steps.app-version.outputs.current }} version-check: ${{ steps.version-check.outputs.exists }} diff --git a/.github/workflows/release_candidate.yml b/.github/workflows/release_candidate.yml index 04e61a57..901c924a 100644 --- a/.github/workflows/release_candidate.yml +++ b/.github/workflows/release_candidate.yml @@ -45,6 +45,9 @@ jobs: - image: tractusx/ssi-credential-expiry-app dockerfile: ./docker/Dockerfile-credential-expiry-app dockernotice: ./docker/notice-credential-expiry-app.md + - image: tractusx/ssi-credential-reissuance-app + dockerfile: ./docker/Dockerfile-credential-reissuance-app + dockernotice: ./docker/notice-credential-reissuance-app.md steps: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 diff --git a/.github/workflows/trivy-main.yml b/.github/workflows/trivy-main.yml index addc9c5f..4ba3c2b8 100644 --- a/.github/workflows/trivy-main.yml +++ b/.github/workflows/trivy-main.yml @@ -198,3 +198,36 @@ jobs: uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 with: sarif_file: "trivy-results5.sarif" + + analyze-ssi-credential-issuer-reissuance-app: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # It's also possible to scan your private registry with Trivy's built-in image scan. + # All you have to do is set ENV vars. + # Docker Hub needs TRIVY_USERNAME and TRIVY_PASSWORD. + # You don't need to set ENV vars when downloading from a public repository. + # For public images, no ENV vars must be set. + - name: Run Trivy vulnerability scanner + if: always() + uses: aquasecurity/trivy-action@7c2007bcb556501da015201bcba5aa14069b74e2 # v0.23.0 + with: + # Path to Docker image + image-ref: "${{ env.IMAGE_NAMESPACE }}/ssi-credential-issuer-reissuance-app:main" + format: "sarif" + output: "trivy-results6.sarif" + vuln-type: "os,library" + skip-dirs: "docs/" + + - name: Upload Trivy scan results to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + with: + sarif_file: "trivy-results6.sarif" diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 250e3ee6..3dedec55 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -196,3 +196,35 @@ jobs: uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 with: sarif_file: "trivy-results5.sarif" + + analyze-ssi-credential-reissuance-app: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # It's also possible to scan your private registry with Trivy's built-in image scan. + # All you have to do is set ENV vars. + # Docker Hub needs TRIVY_USERNAME and TRIVY_PASSWORD. + # You don't need to set ENV vars when downloading from a public repository. + # For public images, no ENV vars must be set. + - name: Run Trivy vulnerability scanner + if: always() + uses: aquasecurity/trivy-action@7c2007bcb556501da015201bcba5aa14069b74e2 # v0.23.0 + with: + # Path to Docker image + image-ref: "${{ env.IMAGE_NAMESPACE }}/ssi-credential-reissuance-app:latest" + format: "sarif" + output: "trivy-results6.sarif" + vuln-type: "os,library" + + - name: Upload Trivy scan results to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + with: + sarif_file: "trivy-results6.sarif" diff --git a/README.md b/README.md index 10c0e210..2fe509a8 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ See Docker notice files for more information: - [credential-issuer-service](./docker//notice-credential-issuer-service.md) - [credential-issuer-processes-worker](./docker/notice-credential-issuer-processes-worker.md) - [credential-expiry-app](./docker/notice-credential-expiry-app.md) +- [credential-reissuance-app](./docker/notice-credential-reissuance-app.md) - [credential-issuer-migrations](./docker/notice-credential-issuer-migrations.md) ## Contributing diff --git a/charts/ssi-credential-issuer/README.md b/charts/ssi-credential-issuer/README.md index fd984e47..ae6b77ab 100644 --- a/charts/ssi-credential-issuer/README.md +++ b/charts/ssi-credential-issuer/README.md @@ -107,6 +107,14 @@ dependencies: | credentialExpiry.logging.default | string | `"Information"` | | | credentialExpiry.expiry.expiredVcsToDeleteInMonth | int | `12` | | | credentialExpiry.expiry.inactiveVcsToDeleteInWeeks | int | `12` | | +| credentialReissuance.name | string | `"reissuance"` | | +| credentialReissuance.image.name | string | `"docker.io/tractusx/ssi-credential-reissuance-app"` | | +| credentialReissuance.image.tag | string | `""` | | +| credentialReissuance.imagePullPolicy | string | `"IfNotPresent"` | | +| credentialReissuance.resources | object | `{"limits":{"cpu":"45m","memory":"105M"},"requests":{"cpu":"15m","memory":"105M"}}` | We recommend to review the default resource limits as this should a conscious choice. | +| credentialReissuance.processIdentity.identityId | string | `"23db9ff3-20c7-476c-ba70-6bdfe5c97104"` | | +| credentialReissuance.logging.default | string | `"Information"` | | +| credentialReissuance.expiry.expiredVcsToReissueInDays | int | `1` | | | existingSecret | string | `""` | Secret containing the client-secrets for the connection to portal and wallet as well as encryptionKeys for issuer.credential and processesworker.wallet | | dotnetEnvironment | string | `"Production"` | | | dbConnection.schema | string | `"issuer"` | | diff --git a/charts/ssi-credential-issuer/templates/cronjob-reissuance-app.yaml b/charts/ssi-credential-issuer/templates/cronjob-reissuance-app.yaml new file mode 100644 index 00000000..cf032599 --- /dev/null +++ b/charts/ssi-credential-issuer/templates/cronjob-reissuance-app.yaml @@ -0,0 +1,78 @@ +############################################################### +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################### + +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ include "issuer.fullname" . }}-{{ .Values.credentialReissuance.name }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "issuer.labels" . | nindent 4 }} +spec: + schedule: "0 0 * * *" + concurrencyPolicy: Forbid + jobTemplate: + metadata: + name: {{ include "issuer.fullname" . }}-{{ .Values.credentialReissuance.name }} + spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: {{ include "issuer.fullname" . }}-{{ .Values.credentialReissuance.name }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + image: "{{ .Values.credentialReissuance.image.name }}:{{ .Values.credentialReissuance.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: "{{ .Values.credentialReissuance.imagePullPolicy }}" + env: + - name: DOTNET_ENVIRONMENT + value: "{{ .Values.dotnetEnvironment }}" + {{- if .Values.postgresql.enabled }} + - name: "ISSUER_PASSWORD" + valueFrom: + secretKeyRef: + name: "{{ template "issuer.postgresSecretName" . }}" + key: "password" + - name: "CONNECTIONSTRINGS__ISSUERDB" + value: "Server={{ template "issuer.postgresql.primary.fullname" . }};Database={{ .Values.postgresql.auth.database }};Port={{ .Values.postgresql.auth.port }};User Id={{ .Values.postgresql.auth.username }};Password=$(ISSUER_PASSWORD);Ssl Mode={{ .Values.dbConnection.sslMode }};" + {{- end }} + {{- if not .Values.postgresql.enabled }} + - name: "ISSUER_PASSWORD" + valueFrom: + secretKeyRef: + name: "{{ .Values.externalDatabase.secret }}" + key: "password" + - name: "CONNECTIONSTRINGS__ISSUERDB" + value: "Server={{ .Values.externalDatabase.host }};Database={{ .Values.externalDatabase.database }};Port={{ .Values.externalDatabase.port }};User Id={{ .Values.externalDatabase.username }};Password=$(ISSUER_PASSWORD);Ssl Mode={{ .Values.dbConnection.sslMode }};" + {{- end }} + - name: "REISSUANCE__EXPIREDVCSTOREISSUEINDAYS" + value: "{{ .Values.credentialReissuance.expiry.expiredVcsToReissueInDays }}" + - name: "REISSUANCE__ISSUERBPN" + value: "{{ .Values.service.credential.issuerBpn }}" + ports: + - name: http + containerPort: {{ .Values.portContainer }} + protocol: TCP + resources: + {{- toYaml .Values.credentialReissuance.resources | nindent 14 }} diff --git a/charts/ssi-credential-issuer/values.yaml b/charts/ssi-credential-issuer/values.yaml index ed139024..85d8ea61 100644 --- a/charts/ssi-credential-issuer/values.yaml +++ b/charts/ssi-credential-issuer/values.yaml @@ -157,6 +157,27 @@ credentialExpiry: expiredVcsToDeleteInMonth: 12 inactiveVcsToDeleteInWeeks: 12 +credentialReissuance: + name: "reissuance" + image: + name: "docker.io/tractusx/ssi-credential-reissuance-app" + tag: "" + imagePullPolicy: "IfNotPresent" + # -- We recommend to review the default resource limits as this should a conscious choice. + resources: + requests: + cpu: 15m + memory: 105M + limits: + cpu: 45m + memory: 105M + processIdentity: + identityId: 23db9ff3-20c7-476c-ba70-6bdfe5c97104 + logging: + default: "Information" + expiry: + expiredVcsToReissueInDays: 1 + # -- Secret containing the client-secrets for the connection to portal and wallet # as well as encryptionKeys for issuer.credential and processesworker.wallet existingSecret: "" diff --git a/docker/Dockerfile-credential-reissuance-app b/docker/Dockerfile-credential-reissuance-app new file mode 100644 index 00000000..d9bd295c --- /dev/null +++ b/docker/Dockerfile-credential-reissuance-app @@ -0,0 +1,35 @@ +############################################################### +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################### + +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS publish +ARG TARGETARCH +WORKDIR / +COPY LICENSE NOTICE.md DEPENDENCIES / +COPY src/ src/ +RUN dotnet restore "src/credentials/SsiCredentialIssuer.Reissuance.App/SsiCredentialIssuer.Reissuance.App.csproj" +WORKDIR /src/credentials/SsiCredentialIssuer.Reissuance.App +RUN dotnet publish "SsiCredentialIssuer.Reissuance.App.csproj" -c Release -o /app/publish + +FROM mcr.microsoft.com/dotnet/runtime:8.0-alpine +ENV COMPlus_EnableDiagnostics=0 +WORKDIR /app +COPY --from=publish /app/publish . +RUN chown -R 1000:3000 /app +USER 1000:3000 +ENTRYPOINT ["dotnet", "Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.dll"] diff --git a/docker/notice-credential-reissuance-app.md b/docker/notice-credential-reissuance-app.md new file mode 100644 index 00000000..59c12893 --- /dev/null +++ b/docker/notice-credential-reissuance-app.md @@ -0,0 +1,22 @@ +## Notice for Docker image + +DockerHub: [https://hub.docker.com/r/tractusx/ssi-credential-reissuance-app](https://hub.docker.com/r/tractusx/ssi-credential-reissuance-app) + +Eclipse Tractus-X product(s) installed within the image: + +__Credential Reissuance App__ + +- GitHub: https://github.com/eclipse-tractusx/ssi-credential-issuer +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- Dockerfile: https://github.com/eclipse-tractusx/ssi-credential-issuer/blob/main/docker/Dockerfile-credential-reissuance-app +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/ssi-credential-issuer/blob/main/LICENSE) + +__Used base images__ + +- Dockerfile: [mcr.microsoft.com/dotnet/runtime:8.0-alpine](https://github.com/dotnet/dotnet-docker/blob/main/src/runtime/8.0/alpine3.19/amd64/Dockerfile) +- GitHub project: [https://github.com/dotnet/dotnet-docker](https://github.com/dotnet/dotnet-docker) +- DockerHub: [https://hub.docker.com/_/microsoft-dotnet-runtime](https://hub.docker.com/_/microsoft-dotnet-runtime) + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. diff --git a/docs/database/db-view.md b/docs/database/db-view.md index 57c647dd..24f639dd 100644 --- a/docs/database/db-view.md +++ b/docs/database/db-view.md @@ -135,6 +135,10 @@ erDiagram timestamp lock_expiry_date uuid version } + REISSUANCES { + uuid id FK + uuid reissued_credential_id + } USE_CASES { uuid id PK text name @@ -187,6 +191,7 @@ erDiagram PROCESS_STEPS ||--|| PROCESS_STEP_TYPES : process_step_type_id PROCESS_STEPS ||--|| PROCESSES : process_id PROCESSES ||--|| PROCESS_TYPES : process_type_id + REISSUANCES ||--|| COMPANY_SSI_DETAILS : company_ssi_detail_id VERIFIED_CREDENTIAL_EXTERNAL_TYPE_DETAIL_VERSIONS ||--|| VERIFIED_CREDENTIAL_EXTERNAL_TYPES : verified_credential_external_type_id VERIFIED_CREDENTIAL_TYPE_ASSIGNED_EXTERNAL_TYPES ||--|| VERIFIED_CREDENTIAL_EXTERNAL_TYPES : has VERIFIED_CREDENTIAL_TYPE_ASSIGNED_EXTERNAL_TYPES ||--|| VERIFIED_CREDENTIAL_TYPES : has @@ -321,6 +326,7 @@ The database is organized into several key tables, each serving a specific purpo - `CREATE_CREDENTIAL`: Creates a credential in the issuer wallet. - `SIGN_CREDENTIAL`: Signs the credential in the issuer wallet. +- `REVOKE_REISSUED_CREDENTIAL`: Revoke reissued credentials. - `SAVE_CREDENTIAL_DOCUMENT`: Saves the credential in the database. - `CREATE_CREDENTIAL_FOR_HOLDER`: Creates the credential in the holder wallet. - `TRIGGER_CALLBACK`: Triggers the callback to the portal. @@ -355,6 +361,11 @@ The database is organized into several key tables, each serving a specific purpo - **lock_expiry_date (TIMESTAMP)**: The lock expiry date of the process. - **version (UUID)**: The version of the process. +### REISSUANCES + +id (UUID): A foreign key referencing id in the COMPANY_SSI_DETAILS. +reissued_credential_id(UUID): Id that identifies the reissued credential added to the COMPANY_SSI_DETAILS. + ### USE_CASES - **id (UUID)**: A unique identifier for the use case. This is the primary key of the table. diff --git a/environments/consortia/helm-values/values-dev.yaml b/environments/consortia/helm-values/values-dev.yaml index 6b014515..5bf99328 100644 --- a/environments/consortia/helm-values/values-dev.yaml +++ b/environments/consortia/helm-values/values-dev.yaml @@ -86,6 +86,13 @@ credentialExpiry: logging: default: "Debug" +credentialReissuance: + image: + tag: "main" + imagePullPolicy: "Always" + logging: + default: "Debug" + centralidp: address: "https://centralidp.dev.demo.catena-x.net" jwtBearerOptions: diff --git a/environments/consortia/helm-values/values-int.yaml b/environments/consortia/helm-values/values-int.yaml index ee584e55..b51ec389 100644 --- a/environments/consortia/helm-values/values-int.yaml +++ b/environments/consortia/helm-values/values-int.yaml @@ -74,6 +74,10 @@ credentialExpiry: logging: default: "Debug" +credentialReissuance: + logging: + default: "Debug" + centralidp: address: "https://centralidp.int.demo.catena-x.net" jwtBearerOptions: diff --git a/environments/consortia/helm-values/values-rc.yaml b/environments/consortia/helm-values/values-rc.yaml index 51a81e6a..5f6fd5d2 100644 --- a/environments/consortia/helm-values/values-rc.yaml +++ b/environments/consortia/helm-values/values-rc.yaml @@ -86,6 +86,13 @@ credentialExpiry: logging: default: "Debug" +credentialReissuance: + image: + tag: "rc" + imagePullPolicy: "Always" + logging: + default: "Debug" + centralidp: address: "https://centralidp-rc.dev.demo.catena-x.net" jwtBearerOptions: diff --git a/environments/helm-values/values-int.yaml b/environments/helm-values/values-int.yaml index ef8b970c..a8aef232 100644 --- a/environments/helm-values/values-int.yaml +++ b/environments/helm-values/values-int.yaml @@ -73,6 +73,10 @@ credentialExpiry: logging: default: "Debug" +credentialReissuance: + logging: + default: "Debug" + centralidp: address: "https://centralidp.int.catena-x.net" jwtBearerOptions: diff --git a/src/SsiCredentialIssuer.sln b/src/SsiCredentialIssuer.sln index 7a965431..fda5e2dd 100644 --- a/src/SsiCredentialIssuer.sln +++ b/src/SsiCredentialIssuer.sln @@ -1,4 +1,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 +# +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "database", "database", "{E9E08CE9-985A-4507-BBD3-9470623986CF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issuer", "issuer", "{32D0AE23-BFAA-4D65-AF9D-2DF951BA5A3B}" @@ -59,6 +62,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Callback.Service", "externa EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Callback.Service.Tests", "..\tests\externalservices\Callback.Service.Tests\Callback.Service.Tests.csproj", "{61DB2ADF-DBC1-4647-AAD2-A8E992E75B37}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SsiCredentialIssuer.Reissuance.App", "credentials\SsiCredentialIssuer.Reissuance.App\SsiCredentialIssuer.Reissuance.App.csproj", "{2757DB44-F658-420F-B00A-48EC6DBA6035}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "credentials\SsiCredentialIssuer.Credential.Library", "credentials\SsiCredentialIssuer.Credential.Library\SsiCredentialIssuer.Credential.Library.csproj", "{832CA2B5-E08D-4EB6-9C99-020E01A86A2C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SsiCredentialIssuer.Reissuance.App.Tests", "..\tests\credentials\SsiCredentialIssuer.Reissuance.App.Tests\SsiCredentialIssuer.Reissuance.App.Tests.csproj", "{677EC522-C102-4446-80E4-B98E6C527D33}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -161,6 +170,21 @@ Global {61DB2ADF-DBC1-4647-AAD2-A8E992E75B37}.Debug|Any CPU.Build.0 = Debug|Any CPU {61DB2ADF-DBC1-4647-AAD2-A8E992E75B37}.Release|Any CPU.ActiveCfg = Release|Any CPU {61DB2ADF-DBC1-4647-AAD2-A8E992E75B37}.Release|Any CPU.Build.0 = Release|Any CPU + {2757DB44-F658-420F-B00A-48EC6DBA6035}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2757DB44-F658-420F-B00A-48EC6DBA6035}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2757DB44-F658-420F-B00A-48EC6DBA6035}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2757DB44-F658-420F-B00A-48EC6DBA6035}.Release|Any CPU.Build.0 = Release|Any CPU + {832CA2B5-E08D-4EB6-9C99-020E01A86A2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {832CA2B5-E08D-4EB6-9C99-020E01A86A2C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {832CA2B5-E08D-4EB6-9C99-020E01A86A2C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {832CA2B5-E08D-4EB6-9C99-020E01A86A2C}.Release|Any CPU.Build.0 = Release|Any CPU + {677EC522-C102-4446-80E4-B98E6C527D33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {677EC522-C102-4446-80E4-B98E6C527D33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {677EC522-C102-4446-80E4-B98E6C527D33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {677EC522-C102-4446-80E4-B98E6C527D33}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {6905B6DF-722B-4882-A2CB-5E6BFD0244F2} = {32D0AE23-BFAA-4D65-AF9D-2DF951BA5A3B} @@ -187,5 +211,8 @@ Global {0A9C65F7-62B6-421F-ADA5-709A1EE10901} = {A2FC3E0F-5AFE-44FA-909B-B8016DD1EB44} {E1821527-A5F9-4D56-BAB0-6F45FE6F3299} = {A37A220C-4242-4FB2-98ED-EF4B602EF6C1} {61DB2ADF-DBC1-4647-AAD2-A8E992E75B37} = {A2FC3E0F-5AFE-44FA-909B-B8016DD1EB44} + {2757DB44-F658-420F-B00A-48EC6DBA6035} = {A79FF417-08E7-4175-8089-5F21054F5BDE} + {832CA2B5-E08D-4EB6-9C99-020E01A86A2C} = {A79FF417-08E7-4175-8089-5F21054F5BDE} + {677EC522-C102-4446-80E4-B98E6C527D33} = {A2FC3E0F-5AFE-44FA-909B-B8016DD1EB44} EndGlobalSection EndGlobal diff --git a/src/credentials/SsiCredentialIssuer.Credential.Library/Context/CredentialContext.cs b/src/credentials/SsiCredentialIssuer.Credential.Library/Context/CredentialContext.cs new file mode 100644 index 00000000..40bec1c3 --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Credential.Library/Context/CredentialContext.cs @@ -0,0 +1,27 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using System.Collections.Immutable; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Credential.Library.Context; + +public static class CredentialContext +{ + public static ImmutableArray Context => ["https://www.w3.org/2018/credentials/v1", "https://w3id.org/catenax/credentials/v1.0.0"]; +} diff --git a/src/credentials/SsiCredentialIssuer.Credential.Library/SsiCredentialIssuer.Credential.Library.csproj b/src/credentials/SsiCredentialIssuer.Credential.Library/SsiCredentialIssuer.Credential.Library.csproj new file mode 100644 index 00000000..e1b3b608 --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Credential.Library/SsiCredentialIssuer.Credential.Library.csproj @@ -0,0 +1,23 @@ + + + + Org.Eclipse.TractusX.SsiCredentialIssuer.Credential.Library + Org.Eclipse.TractusX.SsiCredentialIssuer.Credential.Library + net8.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/DependencyInjection/ReissuanceServiceExtensions.cs b/src/credentials/SsiCredentialIssuer.Reissuance.App/DependencyInjection/ReissuanceServiceExtensions.cs new file mode 100644 index 00000000..f3e04f75 --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/DependencyInjection/ReissuanceServiceExtensions.cs @@ -0,0 +1,53 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Handler; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Services; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.DependencyInjection; + +/// +/// Extension method to register the renewal service and dependent services +/// +public static class ReissuanceServiceExtensions +{ + /// + /// Adds the renewal service + /// + /// the services + /// Expiry section + /// the enriched service collection + public static IServiceCollection AddReissuanceService(this IServiceCollection services, IConfigurationSection section) + { + services + .AddOptions() + .ValidateOnStart() + .ValidateDataAnnotations() + .Bind(section); + services + .AddTransient() + .AddTransient() + .AddTransient(); + + return services; + } +} diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/DependencyInjection/ReissuanceSettings.cs b/src/credentials/SsiCredentialIssuer.Reissuance.App/DependencyInjection/ReissuanceSettings.cs new file mode 100644 index 00000000..712c2c11 --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/DependencyInjection/ReissuanceSettings.cs @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using System.ComponentModel.DataAnnotations; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.DependencyInjection; + +/// +/// Settings for the ReissuanceExpirySettings +/// +public class ReissuanceSettings +{ + [Required(AllowEmptyStrings = false)] + public string IssuerBpn { get; set; } = null!; + + /// + /// Vcs which are about to expire in the next day(s). + /// + [Required] + public int ExpiredVcsToReissueInDays { get; init; } +} diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/Handler/CredentialIssuerHandler.cs b/src/credentials/SsiCredentialIssuer.Reissuance.App/Handler/CredentialIssuerHandler.cs new file mode 100644 index 00000000..19f27b45 --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/Handler/CredentialIssuerHandler.cs @@ -0,0 +1,85 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.DependencyInjection; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Handler; + +/// +public class CredentialIssuerHandler(IIssuerRepositories repositories, IOptions options) : ICredentialIssuerHandler +{ + private readonly ReissuanceSettings _settings = options.Value; + + /// + public async Task HandleCredentialProcessCreation(IssuerCredentialRequest issuerCredentialRequest) + { + var documentContent = Encoding.UTF8.GetBytes(issuerCredentialRequest.Schema); + var hash = SHA512.HashData(documentContent); + var documentRepository = repositories.GetInstance(); + var companyCredentialDetailsRepository = repositories.GetInstance(); + var docId = documentRepository.CreateDocument($"{issuerCredentialRequest.TypeId}.json", documentContent, + hash, MediaTypeId.JSON, DocumentTypeId.PRESENTATION, x => + { + x.IdentityId = issuerCredentialRequest.IdentiyId; + x.DocumentStatusId = DocumentStatusId.ACTIVE; + }).Id; + + Guid? processId = CreateProcess(repositories); + + var ssiDetailId = companyCredentialDetailsRepository.CreateSsiDetails( + issuerCredentialRequest.Bpnl, + issuerCredentialRequest.TypeId, + CompanySsiDetailStatusId.ACTIVE, + _settings.IssuerBpn, + issuerCredentialRequest.IdentiyId, + c => + { + c.VerifiedCredentialExternalTypeDetailVersionId = issuerCredentialRequest.DetailVersionId; + c.ProcessId = processId; + c.ExpiryDate = issuerCredentialRequest.ExpiryDate; + c.ReissuedCredentialId = issuerCredentialRequest.Id; + }).Id; + + documentRepository.AssignDocumentToCompanySsiDetails(docId, ssiDetailId); + + companyCredentialDetailsRepository.CreateProcessData(ssiDetailId, JsonDocument.Parse(issuerCredentialRequest.Schema), issuerCredentialRequest.KindId, + c => + { + c.HolderWalletUrl = issuerCredentialRequest.HolderWalletUrl; + c.CallbackUrl = issuerCredentialRequest.CallbackUrl; + }); + + await repositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + } + + private static Guid CreateProcess(IIssuerRepositories repositories) + { + var processStepRepository = repositories.GetInstance(); + var processId = processStepRepository.CreateProcess(ProcessTypeId.CREATE_CREDENTIAL).Id; + processStepRepository.CreateProcessStep(ProcessStepTypeId.CREATE_CREDENTIAL, ProcessStepStatusId.TODO, processId); + return processId; + } +} diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/Handler/ICredentialIssuerHandler.cs b/src/credentials/SsiCredentialIssuer.Reissuance.App/Handler/ICredentialIssuerHandler.cs new file mode 100644 index 00000000..8b72603d --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/Handler/ICredentialIssuerHandler.cs @@ -0,0 +1,33 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Handler; + +/// +/// Handles the re-issuance of a new credential then creates a new create credential process +/// +public interface ICredentialIssuerHandler +{ + /// + /// Hadkes the request to create a new credential process + /// + /// Credential Request Object + /// + public Task HandleCredentialProcessCreation(IssuerCredentialRequest issuerCredentialRequest); +} diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/Handler/IssuerCredentialRequest.cs b/src/credentials/SsiCredentialIssuer.Reissuance.App/Handler/IssuerCredentialRequest.cs new file mode 100644 index 00000000..07d05013 --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/Handler/IssuerCredentialRequest.cs @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Handler; + +public record IssuerCredentialRequest( + Guid Id, + string Bpnl, + VerifiedCredentialTypeKindId KindId, + VerifiedCredentialTypeId TypeId, + DateTimeOffset ExpiryDate, + string IdentiyId, + string Schema, + string? HolderWalletUrl, + Guid? DetailVersionId, + string? CallbackUrl); diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/Models/CredentialData.cs b/src/credentials/SsiCredentialIssuer.Reissuance.App/Models/CredentialData.cs new file mode 100644 index 00000000..18d5408e --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/Models/CredentialData.cs @@ -0,0 +1,82 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using System.Text.Json.Serialization; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Models; + +public record FrameworkCredential( + [property: JsonPropertyName("id")] Guid Id, + [property: JsonPropertyName("@context")] IEnumerable Context, + [property: JsonPropertyName("type")] IEnumerable Type, + [property: JsonPropertyName("issuanceDate")] DateTimeOffset IssuanceDate, + [property: JsonPropertyName("expirationDate")] DateTimeOffset ExpirationDate, + [property: JsonPropertyName("issuer")] string Issuer, + [property: JsonPropertyName("credentialSubject")] FrameworkCredentialSubject CredentialSubject, + [property: JsonPropertyName("credentialStatus")] CredentialStatus CredentialStatus); + +public record FrameworkCredentialSubject( + [property: JsonPropertyName("id")] string Did, + [property: JsonPropertyName("holderIdentifier")] string HolderIdentifier, + [property: JsonPropertyName("group")] string Group, + [property: JsonPropertyName("useCase")] string UseCase, + [property: JsonPropertyName("contractTemplate")] string ContractTemplate, + [property: JsonPropertyName("contractVersion")] string ContractVersion +); + +public record MembershipCredential( + [property: JsonPropertyName("id")] Guid Id, + [property: JsonPropertyName("@context")] IEnumerable Context, + [property: JsonPropertyName("type")] IEnumerable Type, + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("description")] string Description, + [property: JsonPropertyName("issuanceDate")] DateTimeOffset IssuanceDate, + [property: JsonPropertyName("expirationDate")] DateTimeOffset ExpirationDate, + [property: JsonPropertyName("issuer")] string Issuer, + [property: JsonPropertyName("credentialSubject")] MembershipCredentialSubject CredentialSubject, + [property: JsonPropertyName("credentialStatus")] CredentialStatus CredentialStatus); + +public record MembershipCredentialSubject( + [property: JsonPropertyName("id")] string Did, + [property: JsonPropertyName("holderIdentifier")] string HolderIdentifier, + [property: JsonPropertyName("memberOf")] string MemberOf +); + +public record BpnCredential( + [property: JsonPropertyName("id")] Guid Id, + [property: JsonPropertyName("@context")] IEnumerable Context, + [property: JsonPropertyName("type")] IEnumerable Type, + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("description")] string Description, + [property: JsonPropertyName("issuanceDate")] DateTimeOffset IssuanceDate, + [property: JsonPropertyName("expirationDate")] DateTimeOffset ExpirationDate, + [property: JsonPropertyName("issuer")] string Issuer, + [property: JsonPropertyName("credentialSubject")] BpnCredentialSubject CredentialSubject, + [property: JsonPropertyName("credentialStatus")] CredentialStatus CredentialStatus); + +public record CredentialStatus( + [property: JsonPropertyName("id")] string Id, + [property: JsonPropertyName("type")] string Type +); + +public record BpnCredentialSubject( + [property: JsonPropertyName("id")] string Did, + [property: JsonPropertyName("holderIdentifier")] string HolderIdentifier, + [property: JsonPropertyName("bpn")] string Bpn +); diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/Program.cs b/src/credentials/SsiCredentialIssuer.Reissuance.App/Program.cs new file mode 100644 index 00000000..1e4c82ce --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/Program.cs @@ -0,0 +1,69 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Logging; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Token; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Portal.Service.DependencyInjection; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Processes.Worker.Library.DependencyInjection; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.DependencyInjection; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Services; +using Serilog; + +LoggingExtensions.EnsureInitialized(); +Log.Information("Building Reissuance App"); +try +{ + var host = Host + .CreateDefaultBuilder(args) + .ConfigureServices((hostContext, services) => + { + services + .AddProcessIdentity(hostContext.Configuration.GetSection("ProcessIdentity")) + .AddIssuerRepositories(hostContext.Configuration) + .AddReissuanceService(hostContext.Configuration.GetSection("Reissuance")); + }) + .AddLogging() + .Build(); + Log.Information("Building Reissuance App completed"); + + using var tokenSource = new CancellationTokenSource(); + Console.CancelKeyPress += (s, e) => + { + Log.Information("Canceling..."); + tokenSource.Cancel(); + e.Cancel = true; + }; + + Log.Information("Start processing"); + var workerInstance = host.Services.GetRequiredService(); + await workerInstance.ExecuteAsync(tokenSource.Token).ConfigureAwait(ConfigureAwaitOptions.None); + Log.Information("Execution finished shutting down"); +} +catch (Exception ex) when (!ex.GetType().Name.Equals("StopTheHostException", StringComparison.Ordinal)) +{ + Log.Fatal(ex, "Unhandled exception"); +} +finally +{ + Log.Information("Server Shutting down"); + await Log.CloseAndFlushAsync().ConfigureAwait(false); +} diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/Properties/launchSettings.json b/src/credentials/SsiCredentialIssuer.Reissuance.App/Properties/launchSettings.json new file mode 100644 index 00000000..57157e42 --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "SsiCredentialIssuer.Renewal.App": { + "commandName": "Project", + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/Services/IReissuanceService.cs b/src/credentials/SsiCredentialIssuer.Reissuance.App/Services/IReissuanceService.cs new file mode 100644 index 00000000..fcf81a79 --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/Services/IReissuanceService.cs @@ -0,0 +1,6 @@ +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Services; + +public interface IReissuanceService +{ + Task ExecuteAsync(CancellationToken stoppingToken); +} diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/Services/ReissuanceService.cs b/src/credentials/SsiCredentialIssuer.Reissuance.App/Services/ReissuanceService.cs new file mode 100644 index 00000000..a936d258 --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/Services/ReissuanceService.cs @@ -0,0 +1,139 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Credential.Library.Context; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Models; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.DependencyInjection; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Handler; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Models; +using System.Text.Json; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Services; + +/// +/// Service to re-issue credentials that will expire in the day after. +/// +public class ReissuanceService(IServiceScopeFactory serviceScopeFactory, + ICredentialIssuerHandler credentialIssuerHandler, + IOptions options, + ILogger logger) : IReissuanceService +{ + private static readonly JsonSerializerOptions Options = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + private readonly ReissuanceSettings _settings = options.Value; + + /// + /// Handles the process of re-issuing new verifiable credentias + /// + /// Cancellation Token + public async Task ExecuteAsync(CancellationToken stoppingToken) + { + if (!stoppingToken.IsCancellationRequested) + { + try + { + using var processServiceScope = serviceScopeFactory.CreateScope(); + var dateTimeProvider = processServiceScope.ServiceProvider.GetRequiredService(); + var repositories = processServiceScope.ServiceProvider.GetRequiredService(); + var expirationDate = dateTimeProvider.OffsetNow.AddDays(_settings.ExpiredVcsToReissueInDays); + var companySsiDetailsRepository = repositories.GetInstance(); + var credentialsAboutToExpire = companySsiDetailsRepository.GetCredentialsAboutToExpire(expirationDate); + await ProcessCredentials(credentialsAboutToExpire, dateTimeProvider); + } + catch (Exception ex) + { + Environment.ExitCode = 1; + logger.LogError(ex, "Verified Credential re-issuance check failed with error: {Errors}", ex.Message); + } + } + } + + private async Task ProcessCredentials(IAsyncEnumerable credentialsAboutToExpire, IDateTimeProvider dateTimeProvider) + { + await foreach (var credential in credentialsAboutToExpire) + { + var expirationDate = dateTimeProvider.OffsetNow.AddMonths(12); + + var schemaData = CreateNewCredential(credential, expirationDate); + + await credentialIssuerHandler.HandleCredentialProcessCreation(new IssuerCredentialRequest( + credential.Id, + credential.HolderBpn, + credential.VerifiedCredentialTypeKindId, + credential.VerifiedCredentialTypeId, + expirationDate, + credential.IdentityId, + schemaData, + credential.WalletUrl, + credential.DetailVersionId, + credential.CallbackUrl + )); + } + } + + private static string CreateNewCredential(CredentialAboutToExpireData credential, DateTimeOffset expirationDate) => + credential.VerifiedCredentialTypeKindId switch + { + VerifiedCredentialTypeKindId.BPN => CreateBpnCredential(credential, expirationDate), + VerifiedCredentialTypeKindId.MEMBERSHIP => CreateMembershipCredential(credential, expirationDate), + var _ => throw new NotSupportedException("Only BPN or MEMBERSHIP credentials can be reissued") + }; + + private static string CreateBpnCredential(CredentialAboutToExpireData credential, DateTimeOffset expirationDate) + { + var bpnAboutToExpire = credential.Schema.Deserialize(); + var bpnCredential = new BpnCredential( + Guid.NewGuid(), + CredentialContext.Context, + bpnAboutToExpire!.Type, + bpnAboutToExpire.Name, + bpnAboutToExpire.Description, + DateTimeOffset.UtcNow, + expirationDate, + bpnAboutToExpire.Issuer, + bpnAboutToExpire.CredentialSubject, + bpnAboutToExpire.CredentialStatus); + + return JsonSerializer.Serialize(bpnCredential, Options); + } + + private static string CreateMembershipCredential(CredentialAboutToExpireData credential, DateTimeOffset expirationDate) + { + var membershipAboutToExpire = credential.Schema.Deserialize(); + var membershipCredential = new MembershipCredential( + Guid.NewGuid(), + CredentialContext.Context, + membershipAboutToExpire!.Type, + membershipAboutToExpire.Name, + membershipAboutToExpire.Description, + DateTimeOffset.UtcNow, + expirationDate, + membershipAboutToExpire.Issuer, + membershipAboutToExpire.CredentialSubject, + membershipAboutToExpire.CredentialStatus); + + return JsonSerializer.Serialize(membershipCredential, Options); + } +} diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/SsiCredentialIssuer.Reissuance.App.csproj b/src/credentials/SsiCredentialIssuer.Reissuance.App/SsiCredentialIssuer.Reissuance.App.csproj new file mode 100644 index 00000000..b68b90a6 --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/SsiCredentialIssuer.Reissuance.App.csproj @@ -0,0 +1,58 @@ + + + + + + Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App + Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App + Exe + net8.0 + enable + enable + 47895ab5-89cb-4818-9b08-570588b6aaf3 + Linux + ..\..\.. + True + CS1591 + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + + + + + diff --git a/src/credentials/SsiCredentialIssuer.Reissuance.App/appsettings.json b/src/credentials/SsiCredentialIssuer.Reissuance.App/appsettings.json new file mode 100644 index 00000000..fb6e7583 --- /dev/null +++ b/src/credentials/SsiCredentialIssuer.Reissuance.App/appsettings.json @@ -0,0 +1,33 @@ +{ + "Serilog": { + "Using": [ "Serilog.Sinks.Console" ], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Information", + "Microsoft.Hosting.Lifetime": "Information", + "Org.Eclipse.TractusX.SsiCredentialIssuer": "Information" + } + }, + "WriteTo": [ + { "Name": "Console" } + ], + "Enrich": [ + "FromLogContext" + ], + "Properties": { + "Application": "Org.Eclipse.TractusX.SsiCredentialIssuer.Renewal.App" + } + }, + "ConnectionStrings": { + "IssuerDb": "Server=localhost;Database=issuer;Port=5432;User Id=issuer;Password=postgres;Ssl Mode=Disable;" + }, + "ProcessIdentity": { + "IdentityId": "" + }, + "Reissuance": { + "ExpiredVcsToReissueInDays": 1, + "IssuerBpn": "" + } +} diff --git a/src/database/SsiCredentialIssuer.DbAccess/Models/CredentialExpiryData.cs b/src/database/SsiCredentialIssuer.DbAccess/Models/CredentialExpiryData.cs index 3f515bcb..d871b8b2 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Models/CredentialExpiryData.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Models/CredentialExpiryData.cs @@ -18,6 +18,7 @@ ********************************************************************************/ using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; +using System.Text.Json; namespace Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Models; @@ -39,3 +40,16 @@ public record CredentialScheduleData( bool IsOneMonthNotification, bool IsVcToDecline ); + +public record CredentialAboutToExpireData( + Guid Id, + string HolderBpn, + VerifiedCredentialTypeId VerifiedCredentialTypeId, + VerifiedCredentialTypeKindId VerifiedCredentialTypeKindId, + JsonDocument Schema, + string IdentityId, + string? WalletUrl, + Guid? DetailVersionId, + string? CallbackUrl +); + diff --git a/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs b/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs index 220585f8..57bd842d 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs @@ -22,6 +22,8 @@ using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities; using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities; using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography.X509Certificates; using System.Text.Json; namespace Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; @@ -292,4 +294,39 @@ public void AttachAndModifyProcessData(Guid companySsiDetailId, Action GetCredentialsAboutToExpire(DateTimeOffset expirationDate) + { + return context.CompanySsiDetails + .Select(x => new + { + Details = x, + IsDateAboutToExpire = x.ExpiryDate != null && x.ExpiryDate.Value.Date.CompareTo(expirationDate.Date) == 0, + IsSsiStatusIdActive = x.CompanySsiDetailStatusId == CompanySsiDetailStatusId.ACTIVE, + IsValidCredendialType = x.CompanySsiProcessData != null && (x.CompanySsiProcessData.CredentialTypeKindId == VerifiedCredentialTypeKindId.BPN || x.CompanySsiProcessData.CredentialTypeKindId == VerifiedCredentialTypeKindId.MEMBERSHIP), + IsCredentialNotReissued = x.ReissuedCredentialId == null + }) + .Where(ssi => ssi.IsSsiStatusIdActive && ssi.IsDateAboutToExpire && ssi.IsValidCredendialType && ssi.IsCredentialNotReissued) + .Select(x => new CredentialAboutToExpireData( + x.Details.Id, + x.Details.Bpnl, + x.Details.VerifiedCredentialTypeId, + x.Details.CompanySsiProcessData!.CredentialTypeKindId, + x.Details.CompanySsiProcessData.Schema, + x.Details.Documents.Select(document => document.IdentityId).SingleOrDefault()!, + x.Details.CompanySsiProcessData.HolderWalletUrl!, + x.Details.VerifiedCredentialExternalTypeDetailVersion!.Id, + x.Details.CompanySsiProcessData.CallbackUrl + )) + .AsAsyncEnumerable(); + } + + public Task IsCredentialRevokedByReissuance(Guid credentialId) => + context.CompanySsiDetails.AnyAsync(x => x.Id == credentialId && x.ReissuedCredentialId != null); + + public Task GetCredentialToRevoke(Guid credentialId) => + context.CompanySsiDetails + .Where(x => x.Id == credentialId) + .Select(x => x.ReissuedCredentialId) + .SingleOrDefaultAsync(); } diff --git a/src/database/SsiCredentialIssuer.DbAccess/Repositories/CredentialRepository.cs b/src/database/SsiCredentialIssuer.DbAccess/Repositories/CredentialRepository.cs index 370d0f47..abd444ce 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Repositories/CredentialRepository.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Repositories/CredentialRepository.cs @@ -28,9 +28,10 @@ namespace Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; public class CredentialRepository(IssuerDbContext dbContext) : ICredentialRepository { - public Task GetWalletCredentialId(Guid credentialId) => - dbContext.CompanySsiDetails.Where(x => x.Id == credentialId) - .Select(x => x.ExternalCredentialId) + public Task<(Guid? ExternalCredentialId, bool IsReissuance)> GetSigningData(Guid credentialId) => + dbContext.CompanySsiDetails + .Where(x => x.Id == credentialId) + .Select(x => new ValueTuple(x.ExternalCredentialId, x.ReissuedCredentialId != null)) .SingleOrDefaultAsync(); public Task<(HolderWalletData HolderWalletData, string? Credential, EncryptionTransformationData EncryptionInformation, string? CallbackUrl)> GetCredentialData(Guid credentialId) => @@ -91,10 +92,13 @@ public void AttachAndModifyCredential(Guid credentialId, Action GetCredentialNotificationData(Guid credentialId) => + public Task<(VerifiedCredentialExternalTypeId ExternalTypeId, string RequesterId, bool isReissuance)> GetCredentialNotificationData(Guid credentialId) => dbContext.CompanySsiDetails .Where(x => x.Id == credentialId) - .Select(x => new ValueTuple(x.VerifiedCredentialType!.VerifiedCredentialTypeAssignedExternalType!.VerifiedCredentialExternalTypeId, x.CreatorUserId)) + .Select(x => new ValueTuple( + x.VerifiedCredentialType!.VerifiedCredentialTypeAssignedExternalType!.VerifiedCredentialExternalTypeId, + x.CreatorUserId, + x.ReissuedCredentialId != null)) .SingleOrDefaultAsync(); public Task<(bool Exists, bool IsSameCompany, IEnumerable<(DocumentStatusId StatusId, byte[] Content)> Documents)> GetSignedCredentialForCredentialId(Guid credentialId, string bpnl) => diff --git a/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs b/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs index 6291c544..8836ba67 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs @@ -103,4 +103,7 @@ public interface ICompanySsiDetailsRepository void RemoveSsiDetail(Guid companySsiDetailId, string bpnl, string userId); void CreateProcessData(Guid companySsiDetailId, JsonDocument schema, VerifiedCredentialTypeKindId credentialTypeKindId, Action? setOptionalFields); void AttachAndModifyProcessData(Guid companySsiDetailId, Action? initialize, Action setOptionalFields); + IAsyncEnumerable GetCredentialsAboutToExpire(DateTimeOffset expirationDate); + Task IsCredentialRevokedByReissuance(Guid credentialId); + Task GetCredentialToRevoke(Guid credentialId); } diff --git a/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICredentialRepository.cs b/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICredentialRepository.cs index 0f66e489..857487fd 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICredentialRepository.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICredentialRepository.cs @@ -26,7 +26,7 @@ namespace Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; public interface ICredentialRepository { - Task GetWalletCredentialId(Guid credentialId); + Task<(Guid? ExternalCredentialId, bool IsReissuance)> GetSigningData(Guid credentialId); Task<(HolderWalletData HolderWalletData, string? Credential, EncryptionTransformationData EncryptionInformation, string? CallbackUrl)> GetCredentialData(Guid credentialId); Task<(bool Exists, Guid CredentialId)> GetDataForProcessId(Guid processId); Task<(VerifiedCredentialTypeKindId CredentialTypeKindId, JsonDocument Schema)> GetCredentialStorageInformationById(Guid credentialId); @@ -34,7 +34,7 @@ public interface ICredentialRepository Task<(string Bpn, string? CallbackUrl)> GetCallbackUrl(Guid credentialId); Task<(bool Exists, bool IsSameBpnl, Guid? ExternalCredentialId, CompanySsiDetailStatusId StatusId, IEnumerable<(Guid DocumentId, DocumentStatusId DocumentStatusId)> Documents)> GetRevocationDataById(Guid credentialId, string bpnl); void AttachAndModifyCredential(Guid credentialId, Action? initialize, Action modify); - Task<(VerifiedCredentialExternalTypeId ExternalTypeId, string RequesterId)> GetCredentialNotificationData(Guid credentialId); + Task<(VerifiedCredentialExternalTypeId ExternalTypeId, string RequesterId, bool isReissuance)> GetCredentialNotificationData(Guid credentialId); Task<(bool Exists, bool IsSameCompany, IEnumerable<(DocumentStatusId StatusId, byte[] Content)> Documents)> GetSignedCredentialForCredentialId(Guid credentialId, string bpnl); Task<(bool Exists, bool IsSameCompany, string FileName, DocumentStatusId StatusId, byte[] Content, MediaTypeId MediaTypeId)> GetDocumentById(Guid documentId, string bpnl); } diff --git a/src/database/SsiCredentialIssuer.Entities/AuditEntities/AuditCompanySsiDetail20240902.cs b/src/database/SsiCredentialIssuer.Entities/AuditEntities/AuditCompanySsiDetail20240902.cs new file mode 100644 index 00000000..7b1941fb --- /dev/null +++ b/src/database/SsiCredentialIssuer.Entities/AuditEntities/AuditCompanySsiDetail20240902.cs @@ -0,0 +1,59 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Auditing; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Auditing.Enums; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; +using System.ComponentModel.DataAnnotations; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.AuditEntities; + +public class AuditCompanySsiDetail20240902 : IAuditEntityV2 +{ + /// + [Key] + public Guid AuditV2Id { get; set; } + + public Guid Id { get; set; } + public string Bpnl { get; set; } = null!; + public string IssuerBpn { get; set; } = null!; + public VerifiedCredentialTypeId VerifiedCredentialTypeId { get; set; } + public CompanySsiDetailStatusId CompanySsiDetailStatusId { get; set; } + public DateTimeOffset DateCreated { get; private set; } + public string CreatorUserId { get; set; } = null!; + public DateTimeOffset? ExpiryDate { get; set; } + public Guid? VerifiedCredentialExternalTypeDetailVersionId { get; set; } + + public ExpiryCheckTypeId? ExpiryCheckTypeId { get; set; } + public Guid? ProcessId { get; set; } + public Guid? ExternalCredentialId { get; set; } + public string? Credential { get; set; } + public Guid? ReissuedCredentialId { get; set; } + public DateTimeOffset? DateLastChanged { get; set; } + public string? LastEditorId { get; set; } + + /// + public string? AuditV2LastEditorId { get; set; } + + /// + public AuditOperationId AuditV2OperationId { get; set; } + + /// + public DateTimeOffset AuditV2DateLastChanged { get; set; } +} diff --git a/src/database/SsiCredentialIssuer.Entities/Entities/CompanySsiDetail.cs b/src/database/SsiCredentialIssuer.Entities/Entities/CompanySsiDetail.cs index 343018d6..457a3075 100644 --- a/src/database/SsiCredentialIssuer.Entities/Entities/CompanySsiDetail.cs +++ b/src/database/SsiCredentialIssuer.Entities/Entities/CompanySsiDetail.cs @@ -24,7 +24,7 @@ namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities; -[AuditEntityV2(typeof(AuditCompanySsiDetail20240419))] +[AuditEntityV2(typeof(AuditCompanySsiDetail20240902))] public class CompanySsiDetail : IAuditableV2, IBaseEntity { private CompanySsiDetail() @@ -61,6 +61,7 @@ public CompanySsiDetail(Guid id, string bpnl, VerifiedCredentialTypeId verifiedC public Guid? ProcessId { get; set; } public Guid? ExternalCredentialId { get; set; } public string? Credential { get; set; } + public Guid? ReissuedCredentialId { get; set; } [LastChangedV2] public DateTimeOffset? DateLastChanged { get; set; } @@ -73,6 +74,7 @@ public CompanySsiDetail(Guid id, string bpnl, VerifiedCredentialTypeId verifiedC public virtual ExpiryCheckType? ExpiryCheckType { get; set; } public virtual CompanySsiDetailStatus? CompanySsiDetailStatus { get; set; } public virtual Process? Process { get; set; } + public virtual CompanySsiDetail? ReissuedCredential { get; set; } public virtual VerifiedCredentialExternalTypeDetailVersion? VerifiedCredentialExternalTypeDetailVersion { get; set; } public virtual CompanySsiProcessData? CompanySsiProcessData { get; set; } public virtual ICollection Documents { get; private set; } diff --git a/src/database/SsiCredentialIssuer.Entities/Enums/ProcessStepTypeId.cs b/src/database/SsiCredentialIssuer.Entities/Enums/ProcessStepTypeId.cs index e870cf07..199208f0 100644 --- a/src/database/SsiCredentialIssuer.Entities/Enums/ProcessStepTypeId.cs +++ b/src/database/SsiCredentialIssuer.Entities/Enums/ProcessStepTypeId.cs @@ -27,6 +27,7 @@ public enum ProcessStepTypeId SAVE_CREDENTIAL_DOCUMENT = 3, CREATE_CREDENTIAL_FOR_HOLDER = 4, TRIGGER_CALLBACK = 5, + REVOKE_REISSUED_CREDENTIAL = 6, // DECLINE PROCESS REVOKE_CREDENTIAL = 100, diff --git a/src/database/SsiCredentialIssuer.Entities/IssuerDbContext.cs b/src/database/SsiCredentialIssuer.Entities/IssuerDbContext.cs index 529a769d..337a078c 100644 --- a/src/database/SsiCredentialIssuer.Entities/IssuerDbContext.cs +++ b/src/database/SsiCredentialIssuer.Entities/IssuerDbContext.cs @@ -44,8 +44,9 @@ public IssuerDbContext(DbContextOptions options, IAuditHandler } public virtual DbSet AuditCompanySsiDetail20240228 { get; set; } = default!; - public virtual DbSet AuditDocument20240305 { get; set; } = default!; public virtual DbSet AuditCompanySsiDetail20240419 { get; set; } = default!; + public virtual DbSet AuditCompanySsiDetail20240902 { get; set; } = default!; + public virtual DbSet AuditDocument20240305 { get; set; } = default!; public virtual DbSet AuditDocument20240419 { get; set; } = default!; public virtual DbSet CompanySsiDetails { get; set; } = default!; public virtual DbSet CompanySsiDetailAssignedDocuments { get; set; } = default!; @@ -102,6 +103,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(t => t.VerifiedCredentialExternalTypeDetailVersionId) .OnDelete(DeleteBehavior.ClientSetNull); + entity.HasOne(c => c.ReissuedCredential) + .WithOne() + .HasForeignKey(c => c.ReissuedCredentialId); + entity.HasMany(t => t.Documents) .WithMany(o => o.CompanySsiDetails) .UsingEntity( @@ -120,7 +125,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) j.HasKey(e => new { e.DocumentId, e.CompanySsiDetailId }); }); - entity.HasAuditV2Triggers(); + entity.HasAuditV2Triggers(); }); modelBuilder.Entity() diff --git a/src/database/SsiCredentialIssuer.Migrations/Migrations/20240902130619_291-AddReissuance.Designer.cs b/src/database/SsiCredentialIssuer.Migrations/Migrations/20240902130619_291-AddReissuance.Designer.cs new file mode 100644 index 00000000..6798ac75 --- /dev/null +++ b/src/database/SsiCredentialIssuer.Migrations/Migrations/20240902130619_291-AddReissuance.Designer.cs @@ -0,0 +1,1772 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// +using System; +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities; + +#nullable disable + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Migrations.Migrations +{ + [DbContext(typeof(IssuerDbContext))] + [Migration("20240902130619_291-AddReissuance")] + partial class _291AddReissuance + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("issuer") + .UseCollation("en_US.utf8") + .HasAnnotation("ProductVersion", "8.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.AuditEntities.AuditCompanySsiDetail20240228", b => + { + b.Property("AuditV1Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("audit_v1id"); + + b.Property("AuditV1DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("audit_v1date_last_changed"); + + b.Property("AuditV1LastEditorId") + .HasColumnType("uuid") + .HasColumnName("audit_v1last_editor_id"); + + b.Property("AuditV1OperationId") + .HasColumnType("integer") + .HasColumnName("audit_v1operation_id"); + + b.Property("Bpnl") + .IsRequired() + .HasColumnType("text") + .HasColumnName("bpnl"); + + b.Property("CompanySsiDetailStatusId") + .HasColumnType("integer") + .HasColumnName("company_ssi_detail_status_id"); + + b.Property("CreatorUserId") + .HasColumnType("uuid") + .HasColumnName("creator_user_id"); + + b.Property("Credential") + .HasColumnType("text") + .HasColumnName("credential"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_last_changed"); + + b.Property("ExpiryCheckTypeId") + .HasColumnType("integer") + .HasColumnName("expiry_check_type_id"); + + b.Property("ExpiryDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiry_date"); + + b.Property("ExternalCredentialId") + .HasColumnType("uuid") + .HasColumnName("external_credential_id"); + + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("IssuerBpn") + .IsRequired() + .HasColumnType("text") + .HasColumnName("issuer_bpn"); + + b.Property("LastEditorId") + .HasColumnType("uuid") + .HasColumnName("last_editor_id"); + + b.Property("ProcessId") + .HasColumnType("uuid") + .HasColumnName("process_id"); + + b.Property("VerifiedCredentialExternalTypeDetailVersionId") + .HasColumnType("uuid") + .HasColumnName("verified_credential_external_type_detail_version_id"); + + b.Property("VerifiedCredentialTypeId") + .HasColumnType("integer") + .HasColumnName("verified_credential_type_id"); + + b.HasKey("AuditV1Id") + .HasName("pk_audit_company_ssi_detail20240228"); + + b.ToTable("audit_company_ssi_detail20240228", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.AuditEntities.AuditCompanySsiDetail20240419", b => + { + b.Property("AuditV2Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("audit_v2id"); + + b.Property("AuditV2DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("audit_v2date_last_changed"); + + b.Property("AuditV2LastEditorId") + .HasColumnType("text") + .HasColumnName("audit_v2last_editor_id"); + + b.Property("AuditV2OperationId") + .HasColumnType("integer") + .HasColumnName("audit_v2operation_id"); + + b.Property("Bpnl") + .IsRequired() + .HasColumnType("text") + .HasColumnName("bpnl"); + + b.Property("CompanySsiDetailStatusId") + .HasColumnType("integer") + .HasColumnName("company_ssi_detail_status_id"); + + b.Property("CreatorUserId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("creator_user_id"); + + b.Property("Credential") + .HasColumnType("text") + .HasColumnName("credential"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_last_changed"); + + b.Property("ExpiryCheckTypeId") + .HasColumnType("integer") + .HasColumnName("expiry_check_type_id"); + + b.Property("ExpiryDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiry_date"); + + b.Property("ExternalCredentialId") + .HasColumnType("uuid") + .HasColumnName("external_credential_id"); + + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("IssuerBpn") + .IsRequired() + .HasColumnType("text") + .HasColumnName("issuer_bpn"); + + b.Property("LastEditorId") + .HasColumnType("text") + .HasColumnName("last_editor_id"); + + b.Property("ProcessId") + .HasColumnType("uuid") + .HasColumnName("process_id"); + + b.Property("VerifiedCredentialExternalTypeDetailVersionId") + .HasColumnType("uuid") + .HasColumnName("verified_credential_external_type_detail_version_id"); + + b.Property("VerifiedCredentialTypeId") + .HasColumnType("integer") + .HasColumnName("verified_credential_type_id"); + + b.HasKey("AuditV2Id") + .HasName("pk_audit_company_ssi_detail20240419"); + + b.ToTable("audit_company_ssi_detail20240419", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.AuditEntities.AuditCompanySsiDetail20240902", b => + { + b.Property("AuditV2Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("audit_v2id"); + + b.Property("AuditV2DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("audit_v2date_last_changed"); + + b.Property("AuditV2LastEditorId") + .HasColumnType("text") + .HasColumnName("audit_v2last_editor_id"); + + b.Property("AuditV2OperationId") + .HasColumnType("integer") + .HasColumnName("audit_v2operation_id"); + + b.Property("Bpnl") + .IsRequired() + .HasColumnType("text") + .HasColumnName("bpnl"); + + b.Property("CompanySsiDetailStatusId") + .HasColumnType("integer") + .HasColumnName("company_ssi_detail_status_id"); + + b.Property("CreatorUserId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("creator_user_id"); + + b.Property("Credential") + .HasColumnType("text") + .HasColumnName("credential"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_last_changed"); + + b.Property("ExpiryCheckTypeId") + .HasColumnType("integer") + .HasColumnName("expiry_check_type_id"); + + b.Property("ExpiryDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiry_date"); + + b.Property("ExternalCredentialId") + .HasColumnType("uuid") + .HasColumnName("external_credential_id"); + + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("IssuerBpn") + .IsRequired() + .HasColumnType("text") + .HasColumnName("issuer_bpn"); + + b.Property("LastEditorId") + .HasColumnType("text") + .HasColumnName("last_editor_id"); + + b.Property("ProcessId") + .HasColumnType("uuid") + .HasColumnName("process_id"); + + b.Property("ReissuedCredentialId") + .HasColumnType("uuid") + .HasColumnName("reissued_credential_id"); + + b.Property("VerifiedCredentialExternalTypeDetailVersionId") + .HasColumnType("uuid") + .HasColumnName("verified_credential_external_type_detail_version_id"); + + b.Property("VerifiedCredentialTypeId") + .HasColumnType("integer") + .HasColumnName("verified_credential_type_id"); + + b.HasKey("AuditV2Id") + .HasName("pk_audit_company_ssi_detail20240902"); + + b.ToTable("audit_company_ssi_detail20240902", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.AuditEntities.AuditDocument20240305", b => + { + b.Property("AuditV1Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("audit_v1id"); + + b.Property("AuditV1DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("audit_v1date_last_changed"); + + b.Property("AuditV1LastEditorId") + .HasColumnType("uuid") + .HasColumnName("audit_v1last_editor_id"); + + b.Property("AuditV1OperationId") + .HasColumnType("integer") + .HasColumnName("audit_v1operation_id"); + + b.Property("CompanyUserId") + .HasColumnType("uuid") + .HasColumnName("company_user_id"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_last_changed"); + + b.Property("DocumentContent") + .HasColumnType("bytea") + .HasColumnName("document_content"); + + b.Property("DocumentHash") + .HasColumnType("bytea") + .HasColumnName("document_hash"); + + b.Property("DocumentName") + .HasColumnType("text") + .HasColumnName("document_name"); + + b.Property("DocumentStatusId") + .HasColumnType("integer") + .HasColumnName("document_status_id"); + + b.Property("DocumentTypeId") + .HasColumnType("integer") + .HasColumnName("document_type_id"); + + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("LastEditorId") + .HasColumnType("uuid") + .HasColumnName("last_editor_id"); + + b.Property("MediaTypeId") + .HasColumnType("integer") + .HasColumnName("media_type_id"); + + b.HasKey("AuditV1Id") + .HasName("pk_audit_document20240305"); + + b.ToTable("audit_document20240305", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.AuditEntities.AuditDocument20240419", b => + { + b.Property("AuditV2Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("audit_v2id"); + + b.Property("AuditV2DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("audit_v2date_last_changed"); + + b.Property("AuditV2LastEditorId") + .HasColumnType("text") + .HasColumnName("audit_v2last_editor_id"); + + b.Property("AuditV2OperationId") + .HasColumnType("integer") + .HasColumnName("audit_v2operation_id"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_last_changed"); + + b.Property("DocumentContent") + .HasColumnType("bytea") + .HasColumnName("document_content"); + + b.Property("DocumentHash") + .HasColumnType("bytea") + .HasColumnName("document_hash"); + + b.Property("DocumentName") + .HasColumnType("text") + .HasColumnName("document_name"); + + b.Property("DocumentStatusId") + .HasColumnType("integer") + .HasColumnName("document_status_id"); + + b.Property("DocumentTypeId") + .HasColumnType("integer") + .HasColumnName("document_type_id"); + + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("IdentityId") + .HasColumnType("text") + .HasColumnName("identity_id"); + + b.Property("LastEditorId") + .HasColumnType("text") + .HasColumnName("last_editor_id"); + + b.Property("MediaTypeId") + .HasColumnType("integer") + .HasColumnName("media_type_id"); + + b.HasKey("AuditV2Id") + .HasName("pk_audit_document20240419"); + + b.ToTable("audit_document20240419", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Bpnl") + .IsRequired() + .HasColumnType("text") + .HasColumnName("bpnl"); + + b.Property("CompanySsiDetailStatusId") + .HasColumnType("integer") + .HasColumnName("company_ssi_detail_status_id"); + + b.Property("CreatorUserId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("creator_user_id"); + + b.Property("Credential") + .HasColumnType("text") + .HasColumnName("credential"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_last_changed"); + + b.Property("ExpiryCheckTypeId") + .HasColumnType("integer") + .HasColumnName("expiry_check_type_id"); + + b.Property("ExpiryDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiry_date"); + + b.Property("ExternalCredentialId") + .HasColumnType("uuid") + .HasColumnName("external_credential_id"); + + b.Property("IssuerBpn") + .IsRequired() + .HasColumnType("text") + .HasColumnName("issuer_bpn"); + + b.Property("LastEditorId") + .HasColumnType("text") + .HasColumnName("last_editor_id"); + + b.Property("ProcessId") + .HasColumnType("uuid") + .HasColumnName("process_id"); + + b.Property("ReissuedCredentialId") + .HasColumnType("uuid") + .HasColumnName("reissued_credential_id"); + + b.Property("VerifiedCredentialExternalTypeDetailVersionId") + .HasColumnType("uuid") + .HasColumnName("verified_credential_external_type_detail_version_id"); + + b.Property("VerifiedCredentialTypeId") + .HasColumnType("integer") + .HasColumnName("verified_credential_type_id"); + + b.HasKey("Id") + .HasName("pk_company_ssi_details"); + + b.HasIndex("CompanySsiDetailStatusId") + .HasDatabaseName("ix_company_ssi_details_company_ssi_detail_status_id"); + + b.HasIndex("ExpiryCheckTypeId") + .HasDatabaseName("ix_company_ssi_details_expiry_check_type_id"); + + b.HasIndex("ProcessId") + .HasDatabaseName("ix_company_ssi_details_process_id"); + + b.HasIndex("ReissuedCredentialId") + .IsUnique() + .HasDatabaseName("ix_company_ssi_details_reissued_credential_id"); + + b.HasIndex("VerifiedCredentialExternalTypeDetailVersionId") + .HasDatabaseName("ix_company_ssi_details_verified_credential_external_type_detai"); + + b.HasIndex("VerifiedCredentialTypeId") + .HasDatabaseName("ix_company_ssi_details_verified_credential_type_id"); + + b.ToTable("company_ssi_details", "issuer", t => + { + t.HasTrigger("LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL"); + + t.HasTrigger("LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL"); + }); + + b + .HasAnnotation("LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL", "CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"() RETURNS trigger as $LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_company_ssi_detail20240902\" (\"id\", \"bpnl\", \"issuer_bpn\", \"verified_credential_type_id\", \"company_ssi_detail_status_id\", \"date_created\", \"creator_user_id\", \"expiry_date\", \"verified_credential_external_type_detail_version_id\", \"expiry_check_type_id\", \"process_id\", \"external_credential_id\", \"credential\", \"reissued_credential_id\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"bpnl\", \r\n NEW.\"issuer_bpn\", \r\n NEW.\"verified_credential_type_id\", \r\n NEW.\"company_ssi_detail_status_id\", \r\n NEW.\"date_created\", \r\n NEW.\"creator_user_id\", \r\n NEW.\"expiry_date\", \r\n NEW.\"verified_credential_external_type_detail_version_id\", \r\n NEW.\"expiry_check_type_id\", \r\n NEW.\"process_id\", \r\n NEW.\"external_credential_id\", \r\n NEW.\"credential\", \r\n NEW.\"reissued_credential_id\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 1, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL AFTER INSERT\r\nON \"issuer\".\"company_ssi_details\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"();") + .HasAnnotation("LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL", "CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"() RETURNS trigger as $LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_company_ssi_detail20240902\" (\"id\", \"bpnl\", \"issuer_bpn\", \"verified_credential_type_id\", \"company_ssi_detail_status_id\", \"date_created\", \"creator_user_id\", \"expiry_date\", \"verified_credential_external_type_detail_version_id\", \"expiry_check_type_id\", \"process_id\", \"external_credential_id\", \"credential\", \"reissued_credential_id\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"bpnl\", \r\n NEW.\"issuer_bpn\", \r\n NEW.\"verified_credential_type_id\", \r\n NEW.\"company_ssi_detail_status_id\", \r\n NEW.\"date_created\", \r\n NEW.\"creator_user_id\", \r\n NEW.\"expiry_date\", \r\n NEW.\"verified_credential_external_type_detail_version_id\", \r\n NEW.\"expiry_check_type_id\", \r\n NEW.\"process_id\", \r\n NEW.\"external_credential_id\", \r\n NEW.\"credential\", \r\n NEW.\"reissued_credential_id\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 2, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL AFTER UPDATE\r\nON \"issuer\".\"company_ssi_details\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"();"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetailAssignedDocument", b => + { + b.Property("DocumentId") + .HasColumnType("uuid") + .HasColumnName("document_id"); + + b.Property("CompanySsiDetailId") + .HasColumnType("uuid") + .HasColumnName("company_ssi_detail_id"); + + b.HasKey("DocumentId", "CompanySsiDetailId") + .HasName("pk_company_ssi_detail_assigned_documents"); + + b.HasIndex("CompanySsiDetailId") + .HasDatabaseName("ix_company_ssi_detail_assigned_documents_company_ssi_detail_id"); + + b.ToTable("company_ssi_detail_assigned_documents", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetailStatus", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_company_ssi_detail_statuses"); + + b.ToTable("company_ssi_detail_statuses", "issuer"); + + b.HasData( + new + { + Id = 1, + Label = "PENDING" + }, + new + { + Id = 2, + Label = "ACTIVE" + }, + new + { + Id = 3, + Label = "REVOKED" + }, + new + { + Id = 4, + Label = "INACTIVE" + }); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiProcessData", b => + { + b.Property("CompanySsiDetailId") + .HasColumnType("uuid") + .HasColumnName("company_ssi_detail_id"); + + b.Property("CallbackUrl") + .HasColumnType("text") + .HasColumnName("callback_url"); + + b.Property("ClientId") + .HasColumnType("text") + .HasColumnName("client_id"); + + b.Property("ClientSecret") + .HasColumnType("bytea") + .HasColumnName("client_secret"); + + b.Property("CredentialTypeKindId") + .HasColumnType("integer") + .HasColumnName("credential_type_kind_id"); + + b.Property("EncryptionMode") + .HasColumnType("integer") + .HasColumnName("encryption_mode"); + + b.Property("HolderWalletUrl") + .HasColumnType("text") + .HasColumnName("holder_wallet_url"); + + b.Property("InitializationVector") + .HasColumnType("bytea") + .HasColumnName("initialization_vector"); + + b.Property("Schema") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("schema"); + + b.HasKey("CompanySsiDetailId") + .HasName("pk_company_ssi_process_data"); + + b.HasIndex("CredentialTypeKindId") + .HasDatabaseName("ix_company_ssi_process_data_credential_type_kind_id"); + + b.ToTable("company_ssi_process_data", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_last_changed"); + + b.Property("DocumentContent") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("document_content"); + + b.Property("DocumentHash") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("document_hash"); + + b.Property("DocumentName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("document_name"); + + b.Property("DocumentStatusId") + .HasColumnType("integer") + .HasColumnName("document_status_id"); + + b.Property("DocumentTypeId") + .HasColumnType("integer") + .HasColumnName("document_type_id"); + + b.Property("IdentityId") + .HasColumnType("text") + .HasColumnName("identity_id"); + + b.Property("LastEditorId") + .HasColumnType("text") + .HasColumnName("last_editor_id"); + + b.Property("MediaTypeId") + .HasColumnType("integer") + .HasColumnName("media_type_id"); + + b.HasKey("Id") + .HasName("pk_documents"); + + b.HasIndex("DocumentStatusId") + .HasDatabaseName("ix_documents_document_status_id"); + + b.HasIndex("DocumentTypeId") + .HasDatabaseName("ix_documents_document_type_id"); + + b.HasIndex("MediaTypeId") + .HasDatabaseName("ix_documents_media_type_id"); + + b.ToTable("documents", "issuer", t => + { + t.HasTrigger("LC_TRIGGER_AFTER_INSERT_DOCUMENT"); + + t.HasTrigger("LC_TRIGGER_AFTER_UPDATE_DOCUMENT"); + }); + + b + .HasAnnotation("LC_TRIGGER_AFTER_INSERT_DOCUMENT", "CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_INSERT_DOCUMENT\"() RETURNS trigger as $LC_TRIGGER_AFTER_INSERT_DOCUMENT$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_document20240419\" (\"id\", \"date_created\", \"document_hash\", \"document_content\", \"document_name\", \"media_type_id\", \"document_type_id\", \"document_status_id\", \"identity_id\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"date_created\", \r\n NEW.\"document_hash\", \r\n NEW.\"document_content\", \r\n NEW.\"document_name\", \r\n NEW.\"media_type_id\", \r\n NEW.\"document_type_id\", \r\n NEW.\"document_status_id\", \r\n NEW.\"identity_id\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 1, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_INSERT_DOCUMENT$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_INSERT_DOCUMENT AFTER INSERT\r\nON \"issuer\".\"documents\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_INSERT_DOCUMENT\"();") + .HasAnnotation("LC_TRIGGER_AFTER_UPDATE_DOCUMENT", "CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_DOCUMENT\"() RETURNS trigger as $LC_TRIGGER_AFTER_UPDATE_DOCUMENT$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_document20240419\" (\"id\", \"date_created\", \"document_hash\", \"document_content\", \"document_name\", \"media_type_id\", \"document_type_id\", \"document_status_id\", \"identity_id\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"date_created\", \r\n NEW.\"document_hash\", \r\n NEW.\"document_content\", \r\n NEW.\"document_name\", \r\n NEW.\"media_type_id\", \r\n NEW.\"document_type_id\", \r\n NEW.\"document_status_id\", \r\n NEW.\"identity_id\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 2, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_UPDATE_DOCUMENT$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_UPDATE_DOCUMENT AFTER UPDATE\r\nON \"issuer\".\"documents\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_DOCUMENT\"();"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.DocumentStatus", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_document_status"); + + b.ToTable("document_status", "issuer"); + + b.HasData( + new + { + Id = 2, + Label = "ACTIVE" + }, + new + { + Id = 3, + Label = "INACTIVE" + }); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.DocumentType", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_document_types"); + + b.ToTable("document_types", "issuer"); + + b.HasData( + new + { + Id = 1, + Label = "PRESENTATION" + }, + new + { + Id = 2, + Label = "CREDENTIAL" + }, + new + { + Id = 3, + Label = "VERIFIED_CREDENTIAL" + }); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ExpiryCheckType", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_expiry_check_types"); + + b.ToTable("expiry_check_types", "issuer"); + + b.HasData( + new + { + Id = 1, + Label = "ONE_MONTH" + }, + new + { + Id = 2, + Label = "TWO_WEEKS" + }, + new + { + Id = 3, + Label = "ONE_DAY" + }); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.MediaType", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_media_types"); + + b.ToTable("media_types", "issuer"); + + b.HasData( + new + { + Id = 1, + Label = "JPEG" + }, + new + { + Id = 2, + Label = "GIF" + }, + new + { + Id = 3, + Label = "PNG" + }, + new + { + Id = 4, + Label = "SVG" + }, + new + { + Id = 5, + Label = "TIFF" + }, + new + { + Id = 6, + Label = "PDF" + }, + new + { + Id = 7, + Label = "JSON" + }, + new + { + Id = 8, + Label = "PEM" + }, + new + { + Id = 9, + Label = "CA_CERT" + }, + new + { + Id = 10, + Label = "PKX_CER" + }, + new + { + Id = 11, + Label = "OCTET" + }); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.Process", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("LockExpiryDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("lock_expiry_date"); + + b.Property("ProcessTypeId") + .HasColumnType("integer") + .HasColumnName("process_type_id"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uuid") + .HasColumnName("version"); + + b.HasKey("Id") + .HasName("pk_processes"); + + b.HasIndex("ProcessTypeId") + .HasDatabaseName("ix_processes_process_type_id"); + + b.ToTable("processes", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ProcessStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_last_changed"); + + b.Property("Message") + .HasColumnType("text") + .HasColumnName("message"); + + b.Property("ProcessId") + .HasColumnType("uuid") + .HasColumnName("process_id"); + + b.Property("ProcessStepStatusId") + .HasColumnType("integer") + .HasColumnName("process_step_status_id"); + + b.Property("ProcessStepTypeId") + .HasColumnType("integer") + .HasColumnName("process_step_type_id"); + + b.HasKey("Id") + .HasName("pk_process_steps"); + + b.HasIndex("ProcessId") + .HasDatabaseName("ix_process_steps_process_id"); + + b.HasIndex("ProcessStepStatusId") + .HasDatabaseName("ix_process_steps_process_step_status_id"); + + b.HasIndex("ProcessStepTypeId") + .HasDatabaseName("ix_process_steps_process_step_type_id"); + + b.ToTable("process_steps", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ProcessStepStatus", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_process_step_statuses"); + + b.ToTable("process_step_statuses", "issuer"); + + b.HasData( + new + { + Id = 1, + Label = "TODO" + }, + new + { + Id = 2, + Label = "DONE" + }, + new + { + Id = 3, + Label = "SKIPPED" + }, + new + { + Id = 4, + Label = "FAILED" + }, + new + { + Id = 5, + Label = "DUPLICATE" + }); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ProcessStepType", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_process_step_types"); + + b.ToTable("process_step_types", "issuer"); + + b.HasData( + new + { + Id = 1, + Label = "CREATE_CREDENTIAL" + }, + new + { + Id = 2, + Label = "SIGN_CREDENTIAL" + }, + new + { + Id = 3, + Label = "SAVE_CREDENTIAL_DOCUMENT" + }, + new + { + Id = 4, + Label = "CREATE_CREDENTIAL_FOR_HOLDER" + }, + new + { + Id = 5, + Label = "TRIGGER_CALLBACK" + }, + new + { + Id = 6, + Label = "REVOKE_REISSUED_CREDENTIAL" + }, + new + { + Id = 100, + Label = "REVOKE_CREDENTIAL" + }, + new + { + Id = 101, + Label = "TRIGGER_NOTIFICATION" + }, + new + { + Id = 102, + Label = "TRIGGER_MAIL" + }); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ProcessType", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_process_types"); + + b.ToTable("process_types", "issuer"); + + b.HasData( + new + { + Id = 1, + Label = "CREATE_CREDENTIAL" + }, + new + { + Id = 2, + Label = "DECLINE_CREDENTIAL" + }); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.UseCase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("name"); + + b.Property("Shortname") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("shortname"); + + b.HasKey("Id") + .HasName("pk_use_cases"); + + b.ToTable("use_cases", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialExternalType", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_verified_credential_external_types"); + + b.ToTable("verified_credential_external_types", "issuer"); + + b.HasData( + new + { + Id = 1, + Label = "TRACEABILITY_CREDENTIAL" + }, + new + { + Id = 2, + Label = "PCF_CREDENTIAL" + }, + new + { + Id = 3, + Label = "BEHAVIOR_TWIN_CREDENTIAL" + }, + new + { + Id = 4, + Label = "MEMBERSHIP_CREDENTIAL" + }, + new + { + Id = 5, + Label = "CIRCULAR_ECONOMY" + }, + new + { + Id = 6, + Label = "QUALITY_CREDENTIAL" + }, + new + { + Id = 7, + Label = "BUSINESS_PARTNER_NUMBER" + }, + new + { + Id = 8, + Label = "DEMAND_AND_CAPACITY_MANAGEMENT" + }, + new + { + Id = 9, + Label = "DEMAND_AND_CAPACITY_MANAGEMENT_PURIS" + }, + new + { + Id = 10, + Label = "BUSINESS_PARTNER_DATA_MANAGEMENT" + }, + new + { + Id = 11, + Label = "FRAMEWORK_AGREEMENT" + }, + new + { + Id = 12, + Label = "DATA_EXCHANGE_GOVERNANCE_CREDENTIAL" + }); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialExternalTypeDetailVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Expiry") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiry"); + + b.Property("Template") + .HasColumnType("text") + .HasColumnName("template"); + + b.Property("ValidFrom") + .HasColumnType("timestamp with time zone") + .HasColumnName("valid_from"); + + b.Property("VerifiedCredentialExternalTypeId") + .HasColumnType("integer") + .HasColumnName("verified_credential_external_type_id"); + + b.Property("Version") + .HasColumnType("text") + .HasColumnName("version"); + + b.HasKey("Id") + .HasName("pk_verified_credential_external_type_detail_versions"); + + b.HasIndex("VerifiedCredentialExternalTypeId", "Version") + .IsUnique() + .HasDatabaseName("ix_verified_credential_external_type_detail_versions_verified_"); + + b.ToTable("verified_credential_external_type_detail_versions", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialType", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_verified_credential_types"); + + b.ToTable("verified_credential_types", "issuer"); + + b.HasData( + new + { + Id = 1, + Label = "TRACEABILITY_FRAMEWORK" + }, + new + { + Id = 2, + Label = "PCF_FRAMEWORK" + }, + new + { + Id = 3, + Label = "BEHAVIOR_TWIN_FRAMEWORK" + }, + new + { + Id = 4, + Label = "MEMBERSHIP" + }, + new + { + Id = 5, + Label = "CIRCULAR_ECONOMY" + }, + new + { + Id = 6, + Label = "FRAMEWORK_AGREEMENT_QUALITY" + }, + new + { + Id = 7, + Label = "BUSINESS_PARTNER_NUMBER" + }, + new + { + Id = 8, + Label = "DEMAND_AND_CAPACITY_MANAGEMENT" + }, + new + { + Id = 9, + Label = "DEMAND_AND_CAPACITY_MANAGEMENT_PURIS" + }, + new + { + Id = 10, + Label = "BUSINESS_PARTNER_DATA_MANAGEMENT" + }, + new + { + Id = 11, + Label = "FRAMEWORK_AGREEMENT" + }, + new + { + Id = 12, + Label = "DATA_EXCHANGE_GOVERNANCE_CREDENTIAL" + }); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeAssignedExternalType", b => + { + b.Property("VerifiedCredentialTypeId") + .HasColumnType("integer") + .HasColumnName("verified_credential_type_id"); + + b.Property("VerifiedCredentialExternalTypeId") + .HasColumnType("integer") + .HasColumnName("verified_credential_external_type_id"); + + b.HasKey("VerifiedCredentialTypeId", "VerifiedCredentialExternalTypeId") + .HasName("pk_verified_credential_type_assigned_external_types"); + + b.HasIndex("VerifiedCredentialExternalTypeId") + .HasDatabaseName("ix_verified_credential_type_assigned_external_types_verified_c"); + + b.HasIndex("VerifiedCredentialTypeId") + .IsUnique() + .HasDatabaseName("ix_verified_credential_type_assigned_external_types_verified_c1"); + + b.ToTable("verified_credential_type_assigned_external_types", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeAssignedKind", b => + { + b.Property("VerifiedCredentialTypeId") + .HasColumnType("integer") + .HasColumnName("verified_credential_type_id"); + + b.Property("VerifiedCredentialTypeKindId") + .HasColumnType("integer") + .HasColumnName("verified_credential_type_kind_id"); + + b.HasKey("VerifiedCredentialTypeId", "VerifiedCredentialTypeKindId") + .HasName("pk_verified_credential_type_assigned_kinds"); + + b.HasIndex("VerifiedCredentialTypeId") + .HasDatabaseName("ix_verified_credential_type_assigned_kinds_verified_credential"); + + b.HasIndex("VerifiedCredentialTypeKindId") + .HasDatabaseName("ix_verified_credential_type_assigned_kinds_verified_credential1"); + + b.ToTable("verified_credential_type_assigned_kinds", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeAssignedUseCase", b => + { + b.Property("VerifiedCredentialTypeId") + .HasColumnType("integer") + .HasColumnName("verified_credential_type_id"); + + b.Property("UseCaseId") + .HasColumnType("uuid") + .HasColumnName("use_case_id"); + + b.HasKey("VerifiedCredentialTypeId", "UseCaseId") + .HasName("pk_verified_credential_type_assigned_use_cases"); + + b.HasIndex("UseCaseId") + .IsUnique() + .HasDatabaseName("ix_verified_credential_type_assigned_use_cases_use_case_id"); + + b.HasIndex("VerifiedCredentialTypeId") + .IsUnique() + .HasDatabaseName("ix_verified_credential_type_assigned_use_cases_verified_creden"); + + b.ToTable("verified_credential_type_assigned_use_cases", "issuer"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeKind", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_verified_credential_type_kinds"); + + b.ToTable("verified_credential_type_kinds", "issuer"); + + b.HasData( + new + { + Id = 1, + Label = "FRAMEWORK" + }, + new + { + Id = 2, + Label = "MEMBERSHIP" + }, + new + { + Id = 3, + Label = "BPN" + }); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetail", b => + { + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetailStatus", "CompanySsiDetailStatus") + .WithMany("CompanySsiDetails") + .HasForeignKey("CompanySsiDetailStatusId") + .IsRequired() + .HasConstraintName("fk_company_ssi_details_company_ssi_detail_statuses_company_ssi"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ExpiryCheckType", "ExpiryCheckType") + .WithMany("CompanySsiDetails") + .HasForeignKey("ExpiryCheckTypeId") + .HasConstraintName("fk_company_ssi_details_expiry_check_types_expiry_check_type_id"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.Process", "Process") + .WithMany("CompanySsiDetails") + .HasForeignKey("ProcessId") + .HasConstraintName("fk_company_ssi_details_processes_process_id"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetail", "ReissuedCredential") + .WithOne() + .HasForeignKey("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetail", "ReissuedCredentialId") + .HasConstraintName("fk_company_ssi_details_company_ssi_details_reissued_credential"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialExternalTypeDetailVersion", "VerifiedCredentialExternalTypeDetailVersion") + .WithMany("CompanySsiDetails") + .HasForeignKey("VerifiedCredentialExternalTypeDetailVersionId") + .HasConstraintName("fk_company_ssi_details_verified_credential_external_type_detai"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialType", "VerifiedCredentialType") + .WithMany("CompanySsiDetails") + .HasForeignKey("VerifiedCredentialTypeId") + .IsRequired() + .HasConstraintName("fk_company_ssi_details_verified_credential_types_verified_cred"); + + b.Navigation("CompanySsiDetailStatus"); + + b.Navigation("ExpiryCheckType"); + + b.Navigation("Process"); + + b.Navigation("ReissuedCredential"); + + b.Navigation("VerifiedCredentialExternalTypeDetailVersion"); + + b.Navigation("VerifiedCredentialType"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetailAssignedDocument", b => + { + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetail", "CompanySsiDetail") + .WithMany() + .HasForeignKey("CompanySsiDetailId") + .IsRequired() + .HasConstraintName("fk_company_ssi_detail_assigned_documents_company_ssi_details_c"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .IsRequired() + .HasConstraintName("fk_company_ssi_detail_assigned_documents_documents_document_id"); + + b.Navigation("CompanySsiDetail"); + + b.Navigation("Document"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiProcessData", b => + { + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetail", "CompanySsiDetail") + .WithOne("CompanySsiProcessData") + .HasForeignKey("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiProcessData", "CompanySsiDetailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_company_ssi_process_data_company_ssi_details_company_ssi_de"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeKind", "CredentialTypeKind") + .WithMany("CompanySsiProcessData") + .HasForeignKey("CredentialTypeKindId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_company_ssi_process_data_verified_credential_type_kinds_cre"); + + b.Navigation("CompanySsiDetail"); + + b.Navigation("CredentialTypeKind"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.Document", b => + { + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.DocumentStatus", "DocumentStatus") + .WithMany("Documents") + .HasForeignKey("DocumentStatusId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_documents_document_status_document_status_id"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.DocumentType", "DocumentType") + .WithMany("Documents") + .HasForeignKey("DocumentTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_documents_document_types_document_type_id"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.MediaType", "MediaType") + .WithMany("Documents") + .HasForeignKey("MediaTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_documents_media_types_media_type_id"); + + b.Navigation("DocumentStatus"); + + b.Navigation("DocumentType"); + + b.Navigation("MediaType"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.Process", b => + { + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ProcessType", "ProcessType") + .WithMany("Processes") + .HasForeignKey("ProcessTypeId") + .IsRequired() + .HasConstraintName("fk_processes_process_types_process_type_id"); + + b.Navigation("ProcessType"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ProcessStep", b => + { + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.Process", "Process") + .WithMany("ProcessSteps") + .HasForeignKey("ProcessId") + .IsRequired() + .HasConstraintName("fk_process_steps_processes_process_id"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ProcessStepStatus", "ProcessStepStatus") + .WithMany("ProcessSteps") + .HasForeignKey("ProcessStepStatusId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_process_steps_process_step_statuses_process_step_status_id"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ProcessStepType", "ProcessStepType") + .WithMany("ProcessSteps") + .HasForeignKey("ProcessStepTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_process_steps_process_step_types_process_step_type_id"); + + b.Navigation("Process"); + + b.Navigation("ProcessStepStatus"); + + b.Navigation("ProcessStepType"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialExternalTypeDetailVersion", b => + { + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialExternalType", "VerifiedCredentialExternalType") + .WithMany("VerifiedCredentialExternalTypeDetailVersions") + .HasForeignKey("VerifiedCredentialExternalTypeId") + .IsRequired() + .HasConstraintName("fk_verified_credential_external_type_detail_versions_verified_"); + + b.Navigation("VerifiedCredentialExternalType"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeAssignedExternalType", b => + { + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialExternalType", "VerifiedCredentialExternalType") + .WithMany("VerifiedCredentialTypeAssignedExternalTypes") + .HasForeignKey("VerifiedCredentialExternalTypeId") + .IsRequired() + .HasConstraintName("fk_verified_credential_type_assigned_external_types_verified_c"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialType", "VerifiedCredentialType") + .WithOne("VerifiedCredentialTypeAssignedExternalType") + .HasForeignKey("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeAssignedExternalType", "VerifiedCredentialTypeId") + .IsRequired() + .HasConstraintName("fk_verified_credential_type_assigned_external_types_verified_c1"); + + b.Navigation("VerifiedCredentialExternalType"); + + b.Navigation("VerifiedCredentialType"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeAssignedKind", b => + { + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialType", "VerifiedCredentialType") + .WithOne("VerifiedCredentialTypeAssignedKind") + .HasForeignKey("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeAssignedKind", "VerifiedCredentialTypeId") + .IsRequired() + .HasConstraintName("fk_verified_credential_type_assigned_kinds_verified_credential"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeKind", "VerifiedCredentialTypeKind") + .WithMany("VerifiedCredentialTypeAssignedKinds") + .HasForeignKey("VerifiedCredentialTypeKindId") + .IsRequired() + .HasConstraintName("fk_verified_credential_type_assigned_kinds_verified_credential1"); + + b.Navigation("VerifiedCredentialType"); + + b.Navigation("VerifiedCredentialTypeKind"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeAssignedUseCase", b => + { + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.UseCase", "UseCase") + .WithOne("VerifiedCredentialAssignedUseCase") + .HasForeignKey("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeAssignedUseCase", "UseCaseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_verified_credential_type_assigned_use_cases_use_cases_use_c"); + + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialType", "VerifiedCredentialType") + .WithOne("VerifiedCredentialTypeAssignedUseCase") + .HasForeignKey("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeAssignedUseCase", "VerifiedCredentialTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_verified_credential_type_assigned_use_cases_verified_creden"); + + b.Navigation("UseCase"); + + b.Navigation("VerifiedCredentialType"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetail", b => + { + b.Navigation("CompanySsiProcessData"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetailStatus", b => + { + b.Navigation("CompanySsiDetails"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.DocumentStatus", b => + { + b.Navigation("Documents"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.DocumentType", b => + { + b.Navigation("Documents"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ExpiryCheckType", b => + { + b.Navigation("CompanySsiDetails"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.MediaType", b => + { + b.Navigation("Documents"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.Process", b => + { + b.Navigation("CompanySsiDetails"); + + b.Navigation("ProcessSteps"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ProcessStepStatus", b => + { + b.Navigation("ProcessSteps"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ProcessStepType", b => + { + b.Navigation("ProcessSteps"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.ProcessType", b => + { + b.Navigation("Processes"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.UseCase", b => + { + b.Navigation("VerifiedCredentialAssignedUseCase"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialExternalType", b => + { + b.Navigation("VerifiedCredentialExternalTypeDetailVersions"); + + b.Navigation("VerifiedCredentialTypeAssignedExternalTypes"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialExternalTypeDetailVersion", b => + { + b.Navigation("CompanySsiDetails"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialType", b => + { + b.Navigation("CompanySsiDetails"); + + b.Navigation("VerifiedCredentialTypeAssignedExternalType"); + + b.Navigation("VerifiedCredentialTypeAssignedKind"); + + b.Navigation("VerifiedCredentialTypeAssignedUseCase"); + }); + + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialTypeKind", b => + { + b.Navigation("CompanySsiProcessData"); + + b.Navigation("VerifiedCredentialTypeAssignedKinds"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/database/SsiCredentialIssuer.Migrations/Migrations/20240902130619_291-AddReissuance.cs b/src/database/SsiCredentialIssuer.Migrations/Migrations/20240902130619_291-AddReissuance.cs new file mode 100644 index 00000000..a00af888 --- /dev/null +++ b/src/database/SsiCredentialIssuer.Migrations/Migrations/20240902130619_291-AddReissuance.cs @@ -0,0 +1,139 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Microsoft.EntityFrameworkCore.Migrations; +using System; + +#nullable disable + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Migrations.Migrations +{ + /// + public partial class _291AddReissuance : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"() CASCADE;"); + + migrationBuilder.AddColumn( + name: "reissued_credential_id", + schema: "issuer", + table: "company_ssi_details", + type: "uuid", + nullable: true); + + migrationBuilder.CreateTable( + name: "audit_company_ssi_detail20240902", + schema: "issuer", + columns: table => new + { + audit_v2id = table.Column(type: "uuid", nullable: false), + id = table.Column(type: "uuid", nullable: false), + bpnl = table.Column(type: "text", nullable: false), + issuer_bpn = table.Column(type: "text", nullable: false), + verified_credential_type_id = table.Column(type: "integer", nullable: false), + company_ssi_detail_status_id = table.Column(type: "integer", nullable: false), + date_created = table.Column(type: "timestamp with time zone", nullable: false), + creator_user_id = table.Column(type: "text", nullable: false), + expiry_date = table.Column(type: "timestamp with time zone", nullable: true), + verified_credential_external_type_detail_version_id = table.Column(type: "uuid", nullable: true), + expiry_check_type_id = table.Column(type: "integer", nullable: true), + process_id = table.Column(type: "uuid", nullable: true), + external_credential_id = table.Column(type: "uuid", nullable: true), + credential = table.Column(type: "text", nullable: true), + reissued_credential_id = table.Column(type: "uuid", nullable: true), + date_last_changed = table.Column(type: "timestamp with time zone", nullable: true), + last_editor_id = table.Column(type: "text", nullable: true), + audit_v2last_editor_id = table.Column(type: "text", nullable: true), + audit_v2operation_id = table.Column(type: "integer", nullable: false), + audit_v2date_last_changed = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_audit_company_ssi_detail20240902", x => x.audit_v2id); + }); + + migrationBuilder.InsertData( + schema: "issuer", + table: "process_step_types", + columns: new[] { "id", "label" }, + values: new object[] { 6, "REVOKE_REISSUED_CREDENTIAL" }); + + migrationBuilder.CreateIndex( + name: "ix_company_ssi_details_reissued_credential_id", + schema: "issuer", + table: "company_ssi_details", + column: "reissued_credential_id", + unique: true); + + migrationBuilder.AddForeignKey( + name: "fk_company_ssi_details_company_ssi_details_reissued_credential", + schema: "issuer", + table: "company_ssi_details", + column: "reissued_credential_id", + principalSchema: "issuer", + principalTable: "company_ssi_details", + principalColumn: "id"); + + migrationBuilder.Sql("CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"() RETURNS trigger as $LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_company_ssi_detail20240902\" (\"id\", \"bpnl\", \"issuer_bpn\", \"verified_credential_type_id\", \"company_ssi_detail_status_id\", \"date_created\", \"creator_user_id\", \"expiry_date\", \"verified_credential_external_type_detail_version_id\", \"expiry_check_type_id\", \"process_id\", \"external_credential_id\", \"credential\", \"reissued_credential_id\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"bpnl\", \r\n NEW.\"issuer_bpn\", \r\n NEW.\"verified_credential_type_id\", \r\n NEW.\"company_ssi_detail_status_id\", \r\n NEW.\"date_created\", \r\n NEW.\"creator_user_id\", \r\n NEW.\"expiry_date\", \r\n NEW.\"verified_credential_external_type_detail_version_id\", \r\n NEW.\"expiry_check_type_id\", \r\n NEW.\"process_id\", \r\n NEW.\"external_credential_id\", \r\n NEW.\"credential\", \r\n NEW.\"reissued_credential_id\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 1, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL AFTER INSERT\r\nON \"issuer\".\"company_ssi_details\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"() RETURNS trigger as $LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_company_ssi_detail20240902\" (\"id\", \"bpnl\", \"issuer_bpn\", \"verified_credential_type_id\", \"company_ssi_detail_status_id\", \"date_created\", \"creator_user_id\", \"expiry_date\", \"verified_credential_external_type_detail_version_id\", \"expiry_check_type_id\", \"process_id\", \"external_credential_id\", \"credential\", \"reissued_credential_id\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"bpnl\", \r\n NEW.\"issuer_bpn\", \r\n NEW.\"verified_credential_type_id\", \r\n NEW.\"company_ssi_detail_status_id\", \r\n NEW.\"date_created\", \r\n NEW.\"creator_user_id\", \r\n NEW.\"expiry_date\", \r\n NEW.\"verified_credential_external_type_detail_version_id\", \r\n NEW.\"expiry_check_type_id\", \r\n NEW.\"process_id\", \r\n NEW.\"external_credential_id\", \r\n NEW.\"credential\", \r\n NEW.\"reissued_credential_id\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 2, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL AFTER UPDATE\r\nON \"issuer\".\"company_ssi_details\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"();"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"() CASCADE;"); + + migrationBuilder.DropForeignKey( + name: "fk_company_ssi_details_company_ssi_details_reissued_credential", + schema: "issuer", + table: "company_ssi_details"); + + migrationBuilder.DropTable( + name: "audit_company_ssi_detail20240902", + schema: "issuer"); + + migrationBuilder.DropIndex( + name: "ix_company_ssi_details_reissued_credential_id", + schema: "issuer", + table: "company_ssi_details"); + + migrationBuilder.DeleteData( + schema: "issuer", + table: "process_step_types", + keyColumn: "id", + keyValue: 6); + + migrationBuilder.DropColumn( + name: "reissued_credential_id", + schema: "issuer", + table: "company_ssi_details"); + + migrationBuilder.Sql("CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"() RETURNS trigger as $LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_company_ssi_detail20240419\" (\"id\", \"bpnl\", \"issuer_bpn\", \"verified_credential_type_id\", \"company_ssi_detail_status_id\", \"date_created\", \"creator_user_id\", \"expiry_date\", \"verified_credential_external_type_detail_version_id\", \"expiry_check_type_id\", \"process_id\", \"external_credential_id\", \"credential\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"bpnl\", \r\n NEW.\"issuer_bpn\", \r\n NEW.\"verified_credential_type_id\", \r\n NEW.\"company_ssi_detail_status_id\", \r\n NEW.\"date_created\", \r\n NEW.\"creator_user_id\", \r\n NEW.\"expiry_date\", \r\n NEW.\"verified_credential_external_type_detail_version_id\", \r\n NEW.\"expiry_check_type_id\", \r\n NEW.\"process_id\", \r\n NEW.\"external_credential_id\", \r\n NEW.\"credential\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 1, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL AFTER INSERT\r\nON \"issuer\".\"company_ssi_details\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"() RETURNS trigger as $LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_company_ssi_detail20240419\" (\"id\", \"bpnl\", \"issuer_bpn\", \"verified_credential_type_id\", \"company_ssi_detail_status_id\", \"date_created\", \"creator_user_id\", \"expiry_date\", \"verified_credential_external_type_detail_version_id\", \"expiry_check_type_id\", \"process_id\", \"external_credential_id\", \"credential\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"bpnl\", \r\n NEW.\"issuer_bpn\", \r\n NEW.\"verified_credential_type_id\", \r\n NEW.\"company_ssi_detail_status_id\", \r\n NEW.\"date_created\", \r\n NEW.\"creator_user_id\", \r\n NEW.\"expiry_date\", \r\n NEW.\"verified_credential_external_type_detail_version_id\", \r\n NEW.\"expiry_check_type_id\", \r\n NEW.\"process_id\", \r\n NEW.\"external_credential_id\", \r\n NEW.\"credential\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 2, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL AFTER UPDATE\r\nON \"issuer\".\"company_ssi_details\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"();"); + } + } +} diff --git a/src/database/SsiCredentialIssuer.Migrations/Migrations/IssuerDbContextModelSnapshot.cs b/src/database/SsiCredentialIssuer.Migrations/Migrations/IssuerDbContextModelSnapshot.cs index e0972ada..98e865ee 100644 --- a/src/database/SsiCredentialIssuer.Migrations/Migrations/IssuerDbContextModelSnapshot.cs +++ b/src/database/SsiCredentialIssuer.Migrations/Migrations/IssuerDbContextModelSnapshot.cs @@ -35,7 +35,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder .HasDefaultSchema("issuer") .UseCollation("en_US.utf8") - .HasAnnotation("ProductVersion", "8.0.5") + .HasAnnotation("ProductVersion", "8.0.7") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -215,6 +215,98 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("audit_company_ssi_detail20240419", "issuer"); }); + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.AuditEntities.AuditCompanySsiDetail20240902", b => + { + b.Property("AuditV2Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("audit_v2id"); + + b.Property("AuditV2DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("audit_v2date_last_changed"); + + b.Property("AuditV2LastEditorId") + .HasColumnType("text") + .HasColumnName("audit_v2last_editor_id"); + + b.Property("AuditV2OperationId") + .HasColumnType("integer") + .HasColumnName("audit_v2operation_id"); + + b.Property("Bpnl") + .IsRequired() + .HasColumnType("text") + .HasColumnName("bpnl"); + + b.Property("CompanySsiDetailStatusId") + .HasColumnType("integer") + .HasColumnName("company_ssi_detail_status_id"); + + b.Property("CreatorUserId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("creator_user_id"); + + b.Property("Credential") + .HasColumnType("text") + .HasColumnName("credential"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_last_changed"); + + b.Property("ExpiryCheckTypeId") + .HasColumnType("integer") + .HasColumnName("expiry_check_type_id"); + + b.Property("ExpiryDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiry_date"); + + b.Property("ExternalCredentialId") + .HasColumnType("uuid") + .HasColumnName("external_credential_id"); + + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("IssuerBpn") + .IsRequired() + .HasColumnType("text") + .HasColumnName("issuer_bpn"); + + b.Property("LastEditorId") + .HasColumnType("text") + .HasColumnName("last_editor_id"); + + b.Property("ProcessId") + .HasColumnType("uuid") + .HasColumnName("process_id"); + + b.Property("ReissuedCredentialId") + .HasColumnType("uuid") + .HasColumnName("reissued_credential_id"); + + b.Property("VerifiedCredentialExternalTypeDetailVersionId") + .HasColumnType("uuid") + .HasColumnName("verified_credential_external_type_detail_version_id"); + + b.Property("VerifiedCredentialTypeId") + .HasColumnType("integer") + .HasColumnName("verified_credential_type_id"); + + b.HasKey("AuditV2Id") + .HasName("pk_audit_company_ssi_detail20240902"); + + b.ToTable("audit_company_ssi_detail20240902", "issuer"); + }); + modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.AuditEntities.AuditDocument20240305", b => { b.Property("AuditV1Id") @@ -411,6 +503,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasColumnName("process_id"); + b.Property("ReissuedCredentialId") + .HasColumnType("uuid") + .HasColumnName("reissued_credential_id"); + b.Property("VerifiedCredentialExternalTypeDetailVersionId") .HasColumnType("uuid") .HasColumnName("verified_credential_external_type_detail_version_id"); @@ -431,6 +527,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ProcessId") .HasDatabaseName("ix_company_ssi_details_process_id"); + b.HasIndex("ReissuedCredentialId") + .IsUnique() + .HasDatabaseName("ix_company_ssi_details_reissued_credential_id"); + b.HasIndex("VerifiedCredentialExternalTypeDetailVersionId") .HasDatabaseName("ix_company_ssi_details_verified_credential_external_type_detai"); @@ -445,8 +545,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); b - .HasAnnotation("LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL", "CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"() RETURNS trigger as $LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_company_ssi_detail20240419\" (\"id\", \"bpnl\", \"issuer_bpn\", \"verified_credential_type_id\", \"company_ssi_detail_status_id\", \"date_created\", \"creator_user_id\", \"expiry_date\", \"verified_credential_external_type_detail_version_id\", \"expiry_check_type_id\", \"process_id\", \"external_credential_id\", \"credential\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"bpnl\", \r\n NEW.\"issuer_bpn\", \r\n NEW.\"verified_credential_type_id\", \r\n NEW.\"company_ssi_detail_status_id\", \r\n NEW.\"date_created\", \r\n NEW.\"creator_user_id\", \r\n NEW.\"expiry_date\", \r\n NEW.\"verified_credential_external_type_detail_version_id\", \r\n NEW.\"expiry_check_type_id\", \r\n NEW.\"process_id\", \r\n NEW.\"external_credential_id\", \r\n NEW.\"credential\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 1, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL AFTER INSERT\r\nON \"issuer\".\"company_ssi_details\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"();") - .HasAnnotation("LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL", "CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"() RETURNS trigger as $LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_company_ssi_detail20240419\" (\"id\", \"bpnl\", \"issuer_bpn\", \"verified_credential_type_id\", \"company_ssi_detail_status_id\", \"date_created\", \"creator_user_id\", \"expiry_date\", \"verified_credential_external_type_detail_version_id\", \"expiry_check_type_id\", \"process_id\", \"external_credential_id\", \"credential\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"bpnl\", \r\n NEW.\"issuer_bpn\", \r\n NEW.\"verified_credential_type_id\", \r\n NEW.\"company_ssi_detail_status_id\", \r\n NEW.\"date_created\", \r\n NEW.\"creator_user_id\", \r\n NEW.\"expiry_date\", \r\n NEW.\"verified_credential_external_type_detail_version_id\", \r\n NEW.\"expiry_check_type_id\", \r\n NEW.\"process_id\", \r\n NEW.\"external_credential_id\", \r\n NEW.\"credential\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 2, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL AFTER UPDATE\r\nON \"issuer\".\"company_ssi_details\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"();"); + .HasAnnotation("LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL", "CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"() RETURNS trigger as $LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_company_ssi_detail20240902\" (\"id\", \"bpnl\", \"issuer_bpn\", \"verified_credential_type_id\", \"company_ssi_detail_status_id\", \"date_created\", \"creator_user_id\", \"expiry_date\", \"verified_credential_external_type_detail_version_id\", \"expiry_check_type_id\", \"process_id\", \"external_credential_id\", \"credential\", \"reissued_credential_id\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"bpnl\", \r\n NEW.\"issuer_bpn\", \r\n NEW.\"verified_credential_type_id\", \r\n NEW.\"company_ssi_detail_status_id\", \r\n NEW.\"date_created\", \r\n NEW.\"creator_user_id\", \r\n NEW.\"expiry_date\", \r\n NEW.\"verified_credential_external_type_detail_version_id\", \r\n NEW.\"expiry_check_type_id\", \r\n NEW.\"process_id\", \r\n NEW.\"external_credential_id\", \r\n NEW.\"credential\", \r\n NEW.\"reissued_credential_id\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 1, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL AFTER INSERT\r\nON \"issuer\".\"company_ssi_details\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_INSERT_COMPANYSSIDETAIL\"();") + .HasAnnotation("LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL", "CREATE FUNCTION \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"() RETURNS trigger as $LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL$\r\nBEGIN\r\n INSERT INTO \"issuer\".\"audit_company_ssi_detail20240902\" (\"id\", \"bpnl\", \"issuer_bpn\", \"verified_credential_type_id\", \"company_ssi_detail_status_id\", \"date_created\", \"creator_user_id\", \"expiry_date\", \"verified_credential_external_type_detail_version_id\", \"expiry_check_type_id\", \"process_id\", \"external_credential_id\", \"credential\", \"reissued_credential_id\", \"date_last_changed\", \"last_editor_id\", \"audit_v2id\", \"audit_v2operation_id\", \"audit_v2date_last_changed\", \"audit_v2last_editor_id\") SELECT NEW.\"id\", \r\n NEW.\"bpnl\", \r\n NEW.\"issuer_bpn\", \r\n NEW.\"verified_credential_type_id\", \r\n NEW.\"company_ssi_detail_status_id\", \r\n NEW.\"date_created\", \r\n NEW.\"creator_user_id\", \r\n NEW.\"expiry_date\", \r\n NEW.\"verified_credential_external_type_detail_version_id\", \r\n NEW.\"expiry_check_type_id\", \r\n NEW.\"process_id\", \r\n NEW.\"external_credential_id\", \r\n NEW.\"credential\", \r\n NEW.\"reissued_credential_id\", \r\n NEW.\"date_last_changed\", \r\n NEW.\"last_editor_id\", \r\n gen_random_uuid(), \r\n 2, \r\n CURRENT_TIMESTAMP, \r\n NEW.\"last_editor_id\";\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL AFTER UPDATE\r\nON \"issuer\".\"company_ssi_details\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"issuer\".\"LC_TRIGGER_AFTER_UPDATE_COMPANYSSIDETAIL\"();"); }); modelBuilder.Entity("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetailAssignedDocument", b => @@ -970,6 +1070,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) Label = "TRIGGER_CALLBACK" }, new + { + Id = 6, + Label = "REVOKE_REISSUED_CREDENTIAL" + }, + new { Id = 100, Label = "REVOKE_CREDENTIAL" @@ -1359,6 +1464,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("ProcessId") .HasConstraintName("fk_company_ssi_details_processes_process_id"); + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetail", "ReissuedCredential") + .WithOne() + .HasForeignKey("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.CompanySsiDetail", "ReissuedCredentialId") + .HasConstraintName("fk_company_ssi_details_company_ssi_details_reissued_credential"); + b.HasOne("Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities.VerifiedCredentialExternalTypeDetailVersion", "VerifiedCredentialExternalTypeDetailVersion") .WithMany("CompanySsiDetails") .HasForeignKey("VerifiedCredentialExternalTypeDetailVersionId") @@ -1376,6 +1486,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Process"); + b.Navigation("ReissuedCredential"); + b.Navigation("VerifiedCredentialExternalTypeDetailVersion"); b.Navigation("VerifiedCredentialType"); diff --git a/src/database/SsiCredentialIssuer.Migrations/Seeder/Data/verified_credential_type_assigned_external_types.json b/src/database/SsiCredentialIssuer.Migrations/Seeder/Data/verified_credential_type_assigned_external_types.json index b4e48a92..cfa667b2 100644 --- a/src/database/SsiCredentialIssuer.Migrations/Seeder/Data/verified_credential_type_assigned_external_types.json +++ b/src/database/SsiCredentialIssuer.Migrations/Seeder/Data/verified_credential_type_assigned_external_types.json @@ -23,6 +23,10 @@ "verified_credential_external_type_id": 6, "verified_credential_type_id": 6 }, + { + "verified_credential_external_type_id": 7, + "verified_credential_type_id": 7 + }, { "verified_credential_external_type_id": 8, "verified_credential_type_id": 8 @@ -35,6 +39,10 @@ "verified_credential_external_type_id": 10, "verified_credential_type_id": 10 }, + { + "verified_credential_external_type_id": 11, + "verified_credential_type_id": 11 + }, { "verified_credential_external_type_id": 12, "verified_credential_type_id": 12 diff --git a/src/externalservices/Portal.Service/Models/NotificationTypeId.cs b/src/externalservices/Portal.Service/Models/NotificationTypeId.cs index e6d3409d..e334f8ca 100644 --- a/src/externalservices/Portal.Service/Models/NotificationTypeId.cs +++ b/src/externalservices/Portal.Service/Models/NotificationTypeId.cs @@ -152,5 +152,10 @@ public enum NotificationTypeId /// /// Notification when a credential got rejected /// - CREDENTIAL_EXPIRY = 26 + CREDENTIAL_EXPIRY = 26, + + /// + /// Notification when a credential got Reissued + /// + CREDENTIAL_RENEWAL = 27 } diff --git a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs index b1b52548..3d5a77dd 100644 --- a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs +++ b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs @@ -24,6 +24,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Credential.Library.Context; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Models; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; @@ -47,7 +48,6 @@ public class IssuerBusinessLogic : IIssuerBusinessLogic { private const string StatusList = "StatusList2021"; private static readonly JsonSerializerOptions Options = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - private static readonly IEnumerable Context = new[] { "https://www.w3.org/2018/credentials/v1", "https://w3id.org/catenax/credentials/v1.0.0" }; private static readonly Regex UrlPathInvalidCharsRegex = new("""[""<>#%{}|\\^~\[\]`]+""", RegexOptions.Compiled, TimeSpan.FromSeconds(1)); private readonly IIssuerRepositories _repositories; @@ -341,7 +341,7 @@ public async Task CreateBpnCredential(CreateBpnCredentialRequest requestDa var expiryDate = DateTimeOffset.UtcNow.AddMonths(12); var schemaData = new BpnCredential( Guid.NewGuid(), - Context, + CredentialContext.Context, new[] { "VerifiableCredential", "BpnCredential" }, "BpnCredential", "Bpn Credential", @@ -369,7 +369,7 @@ public async Task CreateMembershipCredential(CreateMembershipCredentialReq var expiryDate = DateTimeOffset.UtcNow.AddMonths(12); var schemaData = new MembershipCredential( Guid.NewGuid(), - Context, + CredentialContext.Context, new[] { "VerifiableCredential", "MembershipCredential" }, "MembershipCredential", "Membership Credential", @@ -437,7 +437,7 @@ public async Task CreateFrameworkCredential(CreateFrameworkCredentialReque var holderDid = await GetHolderInformation(requestData.Holder, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); var schemaData = new FrameworkCredential( Guid.NewGuid(), - Context, + CredentialContext.Context, new[] { "VerifiableCredential", externalTypeId }, DateTimeOffset.UtcNow, GetExpiryDate(result.Expiry), diff --git a/src/issuer/SsiCredentialIssuer.Service/SsiCredentialIssuer.Service.csproj b/src/issuer/SsiCredentialIssuer.Service/SsiCredentialIssuer.Service.csproj index 8061431b..04df391e 100644 --- a/src/issuer/SsiCredentialIssuer.Service/SsiCredentialIssuer.Service.csproj +++ b/src/issuer/SsiCredentialIssuer.Service/SsiCredentialIssuer.Service.csproj @@ -67,6 +67,7 @@ + diff --git a/src/processes/CredentialProcess.Library/Creation/CredentialCreationProcessHandler.cs b/src/processes/CredentialProcess.Library/Creation/CredentialCreationProcessHandler.cs index e898ea10..3b9a01d3 100644 --- a/src/processes/CredentialProcess.Library/Creation/CredentialCreationProcessHandler.cs +++ b/src/processes/CredentialProcess.Library/Creation/CredentialCreationProcessHandler.cs @@ -54,7 +54,7 @@ public CredentialCreationProcessHandler(IIssuerRepositories issuerRepositories, public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SignCredential(Guid credentialId, CancellationToken cancellationToken) { - var externalCredentialId = await _issuerRepositories.GetInstance().GetWalletCredentialId(credentialId).ConfigureAwait(ConfigureAwaitOptions.None); + var (externalCredentialId, isReissuance) = await _issuerRepositories.GetInstance().GetSigningData(credentialId).ConfigureAwait(ConfigureAwaitOptions.None); if (externalCredentialId is null) { throw new ConflictException("ExternalCredentialId must be set here"); @@ -62,7 +62,7 @@ public CredentialCreationProcessHandler(IIssuerRepositories issuerRepositories, await _walletBusinessLogic.SignCredential(credentialId, externalCredentialId!.Value, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); return ( - Enumerable.Repeat(ProcessStepTypeId.SAVE_CREDENTIAL_DOCUMENT, 1), + Enumerable.Repeat(isReissuance ? ProcessStepTypeId.REVOKE_REISSUED_CREDENTIAL : ProcessStepTypeId.SAVE_CREDENTIAL_DOCUMENT, 1), ProcessStepStatusId.DONE, false, null); diff --git a/src/processes/CredentialProcess.Library/DependencyInjection/CredentialHandlerExtensions.cs b/src/processes/CredentialProcess.Library/DependencyInjection/CredentialHandlerExtensions.cs index e4d243eb..95634a06 100644 --- a/src/processes/CredentialProcess.Library/DependencyInjection/CredentialHandlerExtensions.cs +++ b/src/processes/CredentialProcess.Library/DependencyInjection/CredentialHandlerExtensions.cs @@ -20,6 +20,7 @@ using Microsoft.Extensions.DependencyInjection; using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.Creation; using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.Expiry; +using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.Reissuance; namespace Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.DependencyInjection; @@ -40,4 +41,12 @@ public static IServiceCollection AddCredentialExpiryProcessHandler(this IService return services; } + + public static IServiceCollection AddCredentialReissuedProcessHandler(this IServiceCollection services) + { + services + .AddTransient(); + + return services; + } } diff --git a/src/processes/CredentialProcess.Library/Expiry/CredentialExpiryProcessHandler.cs b/src/processes/CredentialProcess.Library/Expiry/CredentialExpiryProcessHandler.cs index 1660e571..40c8d81b 100644 --- a/src/processes/CredentialProcess.Library/Expiry/CredentialExpiryProcessHandler.cs +++ b/src/processes/CredentialProcess.Library/Expiry/CredentialExpiryProcessHandler.cs @@ -82,11 +82,12 @@ public CredentialExpiryProcessHandler(IIssuerRepositories repositories, IWalletS public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> TriggerNotification(Guid credentialId, CancellationToken cancellationToken) { - var (typeId, requesterId) = await _repositories.GetInstance().GetCredentialNotificationData(credentialId).ConfigureAwait(ConfigureAwaitOptions.None); + var (typeId, requesterId, isReissuance) = await _repositories.GetInstance().GetCredentialNotificationData(credentialId).ConfigureAwait(ConfigureAwaitOptions.None); + if (Guid.TryParse(requesterId, out var companyUserId)) { var content = JsonSerializer.Serialize(new { Type = typeId, CredentialId = credentialId }, Options); - await _portalService.AddNotification(content, companyUserId, NotificationTypeId.CREDENTIAL_REJECTED, cancellationToken); + await _portalService.AddNotification(content, companyUserId, isReissuance ? NotificationTypeId.CREDENTIAL_RENEWAL : NotificationTypeId.CREDENTIAL_REJECTED, cancellationToken); } return ( @@ -98,16 +99,21 @@ public CredentialExpiryProcessHandler(IIssuerRepositories repositories, IWalletS public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> TriggerMail(Guid credentialId, CancellationToken cancellationToken) { - var (typeId, requesterId) = await _repositories.GetInstance().GetCredentialNotificationData(credentialId).ConfigureAwait(ConfigureAwaitOptions.None); - + var (typeId, requesterId, isReissuance) = await _repositories.GetInstance().GetCredentialNotificationData(credentialId).ConfigureAwait(ConfigureAwaitOptions.None); var typeValue = typeId.GetEnumValue() ?? throw new UnexpectedConditionException($"VerifiedCredentialType {typeId} does not exists"); + if (Guid.TryParse(requesterId, out var companyUserId)) { - var mailParameters = new MailParameter[] + if (isReissuance) + { + var mailParameters = CreateEmailParameters(typeValue, "The credential about to expiry is revoked and new credential was reissued"); + await _portalService.TriggerMail("CredentialRenewal", companyUserId, mailParameters, cancellationToken); + } + else { - new("requestName", typeValue), new("reason", "The credential is already expired") - }; - await _portalService.TriggerMail("CredentialRejected", companyUserId, mailParameters, cancellationToken); + var mailParameters = CreateEmailParameters(typeValue, "The credential is already expired"); + await _portalService.TriggerMail("CredentialRejected", companyUserId, mailParameters, cancellationToken); + } } return ( @@ -116,4 +122,9 @@ public CredentialExpiryProcessHandler(IIssuerRepositories repositories, IWalletS false, null); } + + private static MailParameter[] CreateEmailParameters(string typeValue, string reason) + { + return [new("requestName", typeValue), new("reason", reason)]; + } } diff --git a/src/processes/CredentialProcess.Library/Reissuance/CredentialReissuanceProcessHandler.cs b/src/processes/CredentialProcess.Library/Reissuance/CredentialReissuanceProcessHandler.cs new file mode 100644 index 00000000..5f71fa60 --- /dev/null +++ b/src/processes/CredentialProcess.Library/Reissuance/CredentialReissuanceProcessHandler.cs @@ -0,0 +1,59 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.Reissuance; + +public class CredentialReissuanceProcessHandler(IIssuerRepositories issuerRepositories) + : ICredentialReissuanceProcessHandler +{ + public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> RevokeReissuedCredential(Guid credentialId) + { + var companySsiRepository = issuerRepositories.GetInstance(); + var processStepRepository = issuerRepositories.GetInstance(); + var credentialToRevokeId = await issuerRepositories.GetInstance().GetCredentialToRevoke(credentialId).ConfigureAwait(ConfigureAwaitOptions.None); + + if (credentialToRevokeId == null) + { + throw new ConflictException("Id of the credential to revoke should always be set here"); + } + + var processId = processStepRepository.CreateProcess(ProcessTypeId.DECLINE_CREDENTIAL).Id; + processStepRepository.CreateProcessStep(ProcessStepTypeId.REVOKE_CREDENTIAL, ProcessStepStatusId.TODO, processId); + companySsiRepository.AttachAndModifyCompanySsiDetails(credentialToRevokeId.Value, c => + { + c.ProcessId = null; + }, + c => + { + c.ProcessId = processId; + }); + + return ( + Enumerable.Repeat(ProcessStepTypeId.SAVE_CREDENTIAL_DOCUMENT, 1), + ProcessStepStatusId.DONE, + false, + null); + } +} + diff --git a/src/processes/CredentialProcess.Library/Reissuance/ICredentialReissuanceProcessHandler.cs b/src/processes/CredentialProcess.Library/Reissuance/ICredentialReissuanceProcessHandler.cs new file mode 100644 index 00000000..36c578a5 --- /dev/null +++ b/src/processes/CredentialProcess.Library/Reissuance/ICredentialReissuanceProcessHandler.cs @@ -0,0 +1,27 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.Reissuance; + +public interface ICredentialReissuanceProcessHandler +{ + Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> RevokeReissuedCredential(Guid credentialId); +} diff --git a/src/processes/CredentialProcess.Worker/Creation/CredentialCreationProcessTypeExecutor.cs b/src/processes/CredentialProcess.Worker/Creation/CredentialCreationProcessTypeExecutor.cs index 69357b87..5bc5ce68 100644 --- a/src/processes/CredentialProcess.Worker/Creation/CredentialCreationProcessTypeExecutor.cs +++ b/src/processes/CredentialProcess.Worker/Creation/CredentialCreationProcessTypeExecutor.cs @@ -18,7 +18,9 @@ ********************************************************************************/ using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library; using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.Creation; +using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.Reissuance; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; @@ -31,10 +33,12 @@ public class CredentialCreationProcessTypeExecutor : IProcessTypeExecutor { private readonly IIssuerRepositories _issuerRepositories; private readonly ICredentialCreationProcessHandler _credentialCreationProcessHandler; + private readonly ICredentialReissuanceProcessHandler _credentialReissuanceProcessHandler; private readonly IEnumerable _executableProcessSteps = ImmutableArray.Create( ProcessStepTypeId.CREATE_CREDENTIAL, ProcessStepTypeId.SIGN_CREDENTIAL, + ProcessStepTypeId.REVOKE_REISSUED_CREDENTIAL, ProcessStepTypeId.SAVE_CREDENTIAL_DOCUMENT, ProcessStepTypeId.CREATE_CREDENTIAL_FOR_HOLDER, ProcessStepTypeId.TRIGGER_CALLBACK); @@ -43,10 +47,12 @@ public class CredentialCreationProcessTypeExecutor : IProcessTypeExecutor public CredentialCreationProcessTypeExecutor( IIssuerRepositories issuerRepositories, - ICredentialCreationProcessHandler credentialCreationProcessHandler) + ICredentialCreationProcessHandler credentialCreationProcessHandler, + ICredentialReissuanceProcessHandler credentialReissuanceProcessHandler) { _issuerRepositories = issuerRepositories; _credentialCreationProcessHandler = credentialCreationProcessHandler; + _credentialReissuanceProcessHandler = credentialReissuanceProcessHandler; } public ProcessTypeId GetProcessTypeId() => ProcessTypeId.CREATE_CREDENTIAL; @@ -86,6 +92,8 @@ public CredentialCreationProcessTypeExecutor( .ConfigureAwait(ConfigureAwaitOptions.None), ProcessStepTypeId.SIGN_CREDENTIAL => await _credentialCreationProcessHandler.SignCredential(_credentialId, cancellationToken) .ConfigureAwait(ConfigureAwaitOptions.None), + ProcessStepTypeId.REVOKE_REISSUED_CREDENTIAL => await _credentialReissuanceProcessHandler.RevokeReissuedCredential(_credentialId) + .ConfigureAwait(ConfigureAwaitOptions.None), ProcessStepTypeId.SAVE_CREDENTIAL_DOCUMENT => await _credentialCreationProcessHandler.SaveCredentialDocument(_credentialId, cancellationToken) .ConfigureAwait(ConfigureAwaitOptions.None), ProcessStepTypeId.CREATE_CREDENTIAL_FOR_HOLDER => await _credentialCreationProcessHandler.CreateCredentialForHolder(_credentialId, cancellationToken) diff --git a/src/processes/Processes.Worker/Program.cs b/src/processes/Processes.Worker/Program.cs index 77400dd0..59382a75 100644 --- a/src/processes/Processes.Worker/Program.cs +++ b/src/processes/Processes.Worker/Program.cs @@ -22,6 +22,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Framework.Logging; using Org.Eclipse.TractusX.Portal.Backend.Framework.Token; using Org.Eclipse.TractusX.SsiCredentialIssuer.Callback.Service.DependencyInjection; +using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.DependencyInjection; using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Worker.DependencyInjection; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; using Org.Eclipse.TractusX.SsiCredentialIssuer.Portal.Service.DependencyInjection; @@ -45,7 +46,8 @@ .AddCallbackService(hostContext.Configuration.GetSection("Callback")) .AddWalletService(hostContext.Configuration) .AddCredentialCreationProcessExecutor() - .AddCredentialExpiryProcessExecutor(); + .AddCredentialExpiryProcessExecutor() + .AddCredentialReissuedProcessHandler(); }) .AddLogging() .Build(); diff --git a/tests/credentials/SsiCredentialIssuer.Reissuance.App.Tests/Handler/CredentialIssuerHandlerTests.cs b/tests/credentials/SsiCredentialIssuer.Reissuance.App.Tests/Handler/CredentialIssuerHandlerTests.cs new file mode 100644 index 00000000..a5ec2fec --- /dev/null +++ b/tests/credentials/SsiCredentialIssuer.Reissuance.App.Tests/Handler/CredentialIssuerHandlerTests.cs @@ -0,0 +1,132 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using FakeItEasy; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.DependencyInjection; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Handler; +using System.Text; +using System.Text.Json; +using Xunit; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Tests.Handler; + +public class CredentialIssuerHandlerTests +{ + private readonly IOptions _options; + private readonly IIssuerRepositories _issuerRepositories; + private readonly IDocumentRepository _documentRepository; + private readonly ICompanySsiDetailsRepository _companySsiDetailsRepository; + private readonly ICredentialIssuerHandler _credentialIssuerHandler; + + public CredentialIssuerHandlerTests() + { + var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + fixture.Behaviors.OfType().ToList() + .ForEach(b => fixture.Behaviors.Remove(b)); + + fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + _issuerRepositories = A.Fake(); + _documentRepository = A.Fake(); + _companySsiDetailsRepository = A.Fake(); + + _options = A.Fake>(); + + A.CallTo(() => _issuerRepositories.GetInstance()).Returns(_documentRepository); + A.CallTo(() => _issuerRepositories.GetInstance()).Returns(_companySsiDetailsRepository); + + var credentialSettings = new ReissuanceSettings { IssuerBpn = "BPNL000000000000" }; + A.CallTo(() => _options.Value).Returns(credentialSettings); + + _credentialIssuerHandler = new CredentialIssuerHandler(_issuerRepositories, _options); + } + + [Fact] + public async Task HandleCredentialProcessCreation_ValidateProcessCreation_NoErrorExpected() + { + // Arrange + var documentId = Guid.NewGuid(); + var ssiCredentialDetails = new List(); + var schema = "{\"id\": \"21a1aa1f-b2f9-43bb-9c71-00b62bd1f8e0\", \"name\": \"BpnCredential\"}"; + var processStepRepository = A.Fake(); + var process = new Process(Guid.NewGuid(), ProcessTypeId.CREATE_CREDENTIAL, Guid.NewGuid()); + var companySsiDetail = new CompanySsiDetail( + Guid.NewGuid(), + null!, + VerifiedCredentialTypeId.BUSINESS_PARTNER_NUMBER, + CompanySsiDetailStatusId.ACTIVE, + _options.Value.IssuerBpn, + Guid.NewGuid().ToString(), + DateTimeOffset.UtcNow); + + var request = new IssuerCredentialRequest( + Guid.NewGuid(), + "BPNL000000000000", + VerifiedCredentialTypeKindId.BPN, + VerifiedCredentialTypeId.BUSINESS_PARTNER_NUMBER, + DateTimeOffset.Now, + "BPNL000000000000", + schema, + "holderWalletUrl", + Guid.NewGuid(), + "example.callback.cofinity" + ); + var documentContent = Encoding.ASCII.GetBytes("document content"); + var hash = Encoding.ASCII.GetBytes(documentContent.GetHashCode().ToString()); + var document = new Document(documentId, documentContent, hash, "document", MediaTypeId.JSON, DateTimeOffset.Now, DocumentStatusId.ACTIVE, DocumentTypeId.PRESENTATION); + + A.CallTo(() => _documentRepository.CreateDocument(A._, A._, A._, MediaTypeId.JSON, DocumentTypeId.PRESENTATION, A?>._)).Returns(document); + A.CallTo(() => _issuerRepositories.GetInstance()).Returns(processStepRepository); + A.CallTo(() => processStepRepository.CreateProcess(ProcessTypeId.CREATE_CREDENTIAL)).Returns(process); + A.CallTo(() => _companySsiDetailsRepository.CreateSsiDetails( + request.Bpnl, + request.TypeId, + CompanySsiDetailStatusId.ACTIVE, + _options.Value.IssuerBpn, + request.IdentiyId, + A?>._)) + .Invokes((string bpnl, VerifiedCredentialTypeId verifiedCredentialTypeId, + CompanySsiDetailStatusId companySsiDetailStatusId, string issuerBpn, string userId, + Action? setOptionalFields) => + { + var detail = new CompanySsiDetail(Guid.NewGuid(), bpnl, verifiedCredentialTypeId, companySsiDetailStatusId, issuerBpn, userId, DateTimeOffset.UtcNow); + setOptionalFields?.Invoke(detail); + ssiCredentialDetails.Add(detail); + }) + .Returns(companySsiDetail); + + // Act + await _credentialIssuerHandler.HandleCredentialProcessCreation(request); + + // Assert + ssiCredentialDetails.Should().ContainSingle().And.Satisfy( + c => c.ReissuedCredentialId == request.Id); + A.CallTo(() => _documentRepository.AssignDocumentToCompanySsiDetails(A._, companySsiDetail.Id)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _companySsiDetailsRepository.CreateProcessData(companySsiDetail.Id, A._, A._, A?>._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _issuerRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); + } +} diff --git a/tests/credentials/SsiCredentialIssuer.Reissuance.App.Tests/Services/ReissuanceServiceTests.cs b/tests/credentials/SsiCredentialIssuer.Reissuance.App.Tests/Services/ReissuanceServiceTests.cs new file mode 100644 index 00000000..3c73e89d --- /dev/null +++ b/tests/credentials/SsiCredentialIssuer.Reissuance.App.Tests/Services/ReissuanceServiceTests.cs @@ -0,0 +1,149 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using FakeItEasy; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Models; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.DependencyInjection; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Handler; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Services; +using System.Text.Json; +using Xunit; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Tests.Services; + +public class ReissuanceServiceTests +{ + private readonly ICredentialIssuerHandler _credentialIssuerHandler; + private readonly IReissuanceService _reissuanceService; + private readonly ICompanySsiDetailsRepository _companySsiDetailsRepository; + private readonly ILogger _logger; + + public ReissuanceServiceTests() + { + var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + fixture.Behaviors.OfType().ToList() + .ForEach(b => fixture.Behaviors.Remove(b)); + fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + var serviceScopeFactory = fixture.Create(); + var serviceScope = fixture.Create(); + var options = A.Fake>(); + _logger = A.Fake>(); + var settings = new ReissuanceSettings(); + var serviceProvider = fixture.Create(); + var dateTimeProvider = A.Fake(); + + _credentialIssuerHandler = A.Fake(); + var issuerRepositories = A.Fake(); + _companySsiDetailsRepository = A.Fake(); + _credentialIssuerHandler = A.Fake(); + + A.CallTo(() => options.Value).Returns(settings); + A.CallTo(() => serviceProvider.GetService(typeof(IDateTimeProvider))).Returns(dateTimeProvider); + A.CallTo(() => serviceProvider.GetService(typeof(IIssuerRepositories))).Returns(issuerRepositories); + A.CallTo(() => issuerRepositories.GetInstance()).Returns(_companySsiDetailsRepository); + A.CallTo(() => serviceScope.ServiceProvider).Returns(serviceProvider); + A.CallTo(() => serviceScopeFactory.CreateScope()).Returns(serviceScope); + A.CallTo(() => dateTimeProvider.OffsetNow).Returns(DateTimeOffset.UtcNow); + + _reissuanceService = new ReissuanceService(serviceScopeFactory, _credentialIssuerHandler, options, _logger); + } + + [Fact] + public async Task ExecuteAsync_ProcessCredentials_NoCredentialsAboutToExpire() + { + // Act + await _reissuanceService.ExecuteAsync(CancellationToken.None); + + // Assert + A.CallTo(() => _credentialIssuerHandler.HandleCredentialProcessCreation(A._)).MustNotHaveHappened(); + } + + [Fact] + public async Task ExecuteAsync_ProcessCredentials_CreateBpnCredential() + { + // Arrange + var schema = "{\"id\":\"6f05cac6-c073-4562-8540-8fc883807808\",\"name\":\"BpnCredential\",\"type\":[\"VerifiableCredential\",\"BpnCredential\"],\"issuer\":\"did:web:localhost:BPNL000000000000\",\"@context\":[\"https://www.w3.org/2018/credentials/v1\",\"https://w3id.org/catenax/credentials/v1.0.0\"],\"description\":\"BpnCredential\",\"issuanceDate\":\"2024-08-19T07:32:37.598099+00:00\",\"expirationDate\":\"2025-08-19T07:32:37.598079+00:00\",\"credentialStatus\":{\"id\":\"example.com\",\"type\":\"StatusList2021\"},\"credentialSubject\":{\"id\":\"did:web:localhost:BPNL000000000000\",\"bpn\":\"BPNL000000000000\",\"holderIdentifier\":\"BPNL000000000000\"}}"; + IssuerCredentialRequest? issuerCredentialRequest = null; + var credentialsAboutToExpire = new CredentialAboutToExpireData( + Guid.NewGuid(), + "BPNL000000000000", + Entities.Enums.VerifiedCredentialTypeId.BUSINESS_PARTNER_NUMBER, + Entities.Enums.VerifiedCredentialTypeKindId.BPN, + JsonDocument.Parse(schema), + "BPNL000000000000", + "http://localhost", + Guid.NewGuid(), + "callback.com" + ); + + A.CallTo(() => _companySsiDetailsRepository.GetCredentialsAboutToExpire(A._)).Returns((new[] { credentialsAboutToExpire }.ToAsyncEnumerable())); + A.CallTo(() => _credentialIssuerHandler.HandleCredentialProcessCreation(A._)) + .Invokes((IssuerCredentialRequest credentialRequest) => + { + issuerCredentialRequest = credentialRequest; + }); + + // Act + await _reissuanceService.ExecuteAsync(CancellationToken.None); + + // Assert + A.CallTo(() => _credentialIssuerHandler.HandleCredentialProcessCreation(A._)).MustHaveHappenedOnceExactly(); + Assert.NotNull(issuerCredentialRequest); + issuerCredentialRequest.Id.Should().Be(credentialsAboutToExpire.Id); + issuerCredentialRequest.Bpnl.Should().Be(credentialsAboutToExpire.HolderBpn); + issuerCredentialRequest.TypeId.Should().Be(credentialsAboutToExpire.VerifiedCredentialTypeId); + } + + [Fact] + public async Task ExecuteAsync_ProcessCredentials_HandleException() + { + // Arrange + var schema = "{\"id\":\"6f05cac6-c073-4562-8540-8fc883807808\",\"name\":\"BpnCredential\",\"type\":[\"VerifiableCredential\",\"BpnCredential\"],\"issuer\":\"did:web:localhost:BPNL000000000000\",\"@context\":[\"https://www.w3.org/2018/credentials/v1\",\"https://w3id.org/catenax/credentials/v1.0.0\"],\"description\":\"BpnCredential\",\"issuanceDate\":\"2024-08-19T07:32:37.598099+00:00\",\"expirationDate\":\"2025-08-19T07:32:37.598079+00:00\",\"credentialStatus\":{\"id\":\"example.com\",\"type\":\"StatusList2021\"},\"credentialSubject\":{\"id\":\"did:web:localhost:BPNL000000000000\",\"bpn\":\"BPNL000000000000\",\"holderIdentifier\":\"BPNL000000000000\"}}"; + var credentialsAboutToExpire = new CredentialAboutToExpireData( + Guid.NewGuid(), + "BPNL000000000000", + Entities.Enums.VerifiedCredentialTypeId.MEMBERSHIP, + Entities.Enums.VerifiedCredentialTypeKindId.MEMBERSHIP, + JsonDocument.Parse(schema), + "BPNL000000000000", + "http://localhost", + Guid.NewGuid(), + "callback.com" + ); + + A.CallTo(() => _companySsiDetailsRepository.GetCredentialsAboutToExpire(A._)).Returns((new[] { credentialsAboutToExpire }.ToAsyncEnumerable())); + A.CallTo(() => _credentialIssuerHandler.HandleCredentialProcessCreation(A._)).Throws(new Exception()); + + // Act + await _reissuanceService.ExecuteAsync(CancellationToken.None); + + // Assert + A.CallTo(() => _credentialIssuerHandler.HandleCredentialProcessCreation(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(_logger).Where(call => call.Method.Name == "Log" && call.GetArgument(0) == LogLevel.Error).MustHaveHappened(1, Times.Exactly); + } +} diff --git a/tests/credentials/SsiCredentialIssuer.Reissuance.App.Tests/SsiCredentialIssuer.Reissuance.App.Tests.csproj b/tests/credentials/SsiCredentialIssuer.Reissuance.App.Tests/SsiCredentialIssuer.Reissuance.App.Tests.csproj new file mode 100644 index 00000000..6d00864b --- /dev/null +++ b/tests/credentials/SsiCredentialIssuer.Reissuance.App.Tests/SsiCredentialIssuer.Reissuance.App.Tests.csproj @@ -0,0 +1,31 @@ + + + + Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Tests + Org.Eclipse.TractusX.SsiCredentialIssuer.Reissuance.App.Tests + net8.0 + enable + enable + false + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/tests/database/SsiCredentialIssuer.DbAccess.Tests/CredentialRepositoryTests.cs b/tests/database/SsiCredentialIssuer.DbAccess.Tests/CredentialRepositoryTests.cs index a87e85a5..775cda56 100644 --- a/tests/database/SsiCredentialIssuer.DbAccess.Tests/CredentialRepositoryTests.cs +++ b/tests/database/SsiCredentialIssuer.DbAccess.Tests/CredentialRepositoryTests.cs @@ -85,19 +85,20 @@ public async Task GetCredentialData_ReturnsExpectedDocument() #endregion - #region GetWalletCredentialId + #region GetSigningData [Fact] - public async Task GetWalletCredentialId_ReturnsExpectedDocument() + public async Task GetSigningData_ReturnsExpectedDocument() { // Arrange var sut = await CreateSut(); // Act - var result = await sut.GetWalletCredentialId(new Guid("9f5b9934-4014-4099-91e9-7b1aee696b03")); + var result = await sut.GetSigningData(new Guid("9f5b9934-4014-4099-91e9-7b1aee696b03")); // Assert - result.Should().Be(new Guid("bd474c60-e7ce-450f-bdf4-73604546fc5e")); + result.ExternalCredentialId.Should().Be(new Guid("bd474c60-e7ce-450f-bdf4-73604546fc5e")); + result.IsReissuance.Should().BeFalse(); } #endregion diff --git a/tests/database/SsiCredentialIssuer.DbAccess.Tests/IssuerDbContextTests.cs b/tests/database/SsiCredentialIssuer.DbAccess.Tests/IssuerDbContextTests.cs index 0acb8021..4e43c22e 100644 --- a/tests/database/SsiCredentialIssuer.DbAccess.Tests/IssuerDbContextTests.cs +++ b/tests/database/SsiCredentialIssuer.DbAccess.Tests/IssuerDbContextTests.cs @@ -73,8 +73,8 @@ public async Task SaveCreatedAuditableEntity_SetsLastEditorId() // Assert ca.LastEditorId.Should().NotBeNull().And.Be("ac1cf001-7fbc-1f2f-817f-bce058020001"); ca.DateLastChanged.Should().Be(now); - var auditEntries = await sut.AuditCompanySsiDetail20240419.Where(x => x.Id == id).ToListAsync(); - auditEntries.Should().ContainSingle().Which.Should().Match( + var auditEntries = await sut.AuditCompanySsiDetail20240902.Where(x => x.Id == id).ToListAsync(); + auditEntries.Should().ContainSingle().Which.Should().Match( x => x.CompanySsiDetailStatusId == CompanySsiDetailStatusId.ACTIVE && (x.DateCreated - before) < TimeSpan.FromSeconds(1) && x.AuditV2OperationId == AuditOperationId.INSERT && (x.AuditV2DateLastChanged - now) < TimeSpan.FromSeconds(1) && x.LastEditorId == "ac1cf001-7fbc-1f2f-817f-bce058020001"); await trans.RollbackAsync(); } @@ -103,7 +103,7 @@ public async Task SaveDeletedAuditableEntity_SetsLastEditorId() // Assert ca.LastEditorId.Should().NotBeNull().And.Be("ac1cf001-7fbc-1f2f-817f-bce058020001"); ca.DateLastChanged.Should().Be(later); - var auditEntries = await sut.AuditCompanySsiDetail20240419.Where(x => x.Id == id).ToListAsync(); + var auditEntries = await sut.AuditCompanySsiDetail20240902.Where(x => x.Id == id).ToListAsync(); auditEntries.Should().HaveCount(2).And.Satisfy( x => x.CompanySsiDetailStatusId == CompanySsiDetailStatusId.ACTIVE && (x.DateCreated - before) < TimeSpan.FromSeconds(1) && x.AuditV2OperationId == AuditOperationId.INSERT && x.LastEditorId == "ac1cf001-7fbc-1f2f-817f-bce058020001", x => x.CompanySsiDetailStatusId == CompanySsiDetailStatusId.ACTIVE && (x.DateCreated - before) < TimeSpan.FromSeconds(1) && x.AuditV2OperationId == AuditOperationId.DELETE && (x.AuditV2DateLastChanged - later) < TimeSpan.FromSeconds(1) && x.LastEditorId == "ac1cf001-7fbc-1f2f-817f-bce058020001"); diff --git a/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/IssuerBusinessLogicTests.cs b/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/IssuerBusinessLogicTests.cs index caeccca6..fde01c0b 100644 --- a/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/IssuerBusinessLogicTests.cs +++ b/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/IssuerBusinessLogicTests.cs @@ -21,6 +21,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Credential.Library.Context; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Models; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; @@ -40,7 +41,6 @@ namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Service.Tests.BusinessLogic; public class IssuerBusinessLogicTests { - private static readonly IEnumerable Context = new[] { "https://www.w3.org/2018/credentials/v1", "https://w3id.org/catenax/credentials/v1.0.0" }; private static readonly JsonSerializerOptions Options = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; private static readonly Guid CredentialId = Guid.NewGuid(); private static readonly string Bpnl = "BPNL00000001TEST"; @@ -470,7 +470,7 @@ private static string CreateSchema() { var schemaData = new FrameworkCredential( Guid.NewGuid(), - Context, + CredentialContext.Context, new[] { "VerifiableCredential", VerifiedCredentialExternalTypeId.TRACEABILITY_CREDENTIAL.ToString() }, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, diff --git a/tests/issuer/SsiCredentialIssuer.Service.Tests/Identity/MandatoryIdentityClaimHandlerTests.cs b/tests/issuer/SsiCredentialIssuer.Service.Tests/Identity/MandatoryIdentityClaimHandlerTests.cs index 15d695e6..53eaf422 100644 --- a/tests/issuer/SsiCredentialIssuer.Service.Tests/Identity/MandatoryIdentityClaimHandlerTests.cs +++ b/tests/issuer/SsiCredentialIssuer.Service.Tests/Identity/MandatoryIdentityClaimHandlerTests.cs @@ -20,7 +20,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; -using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Processes.Worker.Library.Tests; using Org.Eclipse.TractusX.SsiCredentialIssuer.Service.Identity; using System.Security.Claims; diff --git a/tests/issuer/SsiCredentialIssuer.Service.Tests/SsiCredentialIssuer.Service.Tests.csproj b/tests/issuer/SsiCredentialIssuer.Service.Tests/SsiCredentialIssuer.Service.Tests.csproj index cf8f6f53..558791aa 100644 --- a/tests/issuer/SsiCredentialIssuer.Service.Tests/SsiCredentialIssuer.Service.Tests.csproj +++ b/tests/issuer/SsiCredentialIssuer.Service.Tests/SsiCredentialIssuer.Service.Tests.csproj @@ -58,5 +58,6 @@ + diff --git a/tests/processes/CredentialProcess.Library.Tests/CredentialCreationProcessHandlerTests.cs b/tests/processes/CredentialProcess.Library.Tests/CredentialCreationProcessHandlerTests.cs index 57862757..0cba7665 100644 --- a/tests/processes/CredentialProcess.Library.Tests/CredentialCreationProcessHandlerTests.cs +++ b/tests/processes/CredentialProcess.Library.Tests/CredentialCreationProcessHandlerTests.cs @@ -41,7 +41,6 @@ public class CredentialCreationProcessHandlerTests private readonly Guid _credentialId = Guid.NewGuid(); private readonly IWalletBusinessLogic _walletBusinessLogic; - private readonly IIssuerRepositories _issuerRepositories; private readonly ICredentialRepository _credentialRepository; private readonly CredentialCreationProcessHandler _sut; @@ -55,15 +54,15 @@ public CredentialCreationProcessHandlerTests() .ForEach(b => _fixture.Behaviors.Remove(b)); _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); - _issuerRepositories = A.Fake(); + var issuerRepositories = A.Fake(); _credentialRepository = A.Fake(); - A.CallTo(() => _issuerRepositories.GetInstance()).Returns(_credentialRepository); + A.CallTo(() => issuerRepositories.GetInstance()).Returns(_credentialRepository); _walletBusinessLogic = A.Fake(); _callbackService = A.Fake(); - _sut = new CredentialCreationProcessHandler(_issuerRepositories, _walletBusinessLogic, _callbackService); + _sut = new CredentialCreationProcessHandler(issuerRepositories, _walletBusinessLogic, _callbackService); } #region CreateCredential @@ -96,8 +95,8 @@ public async Task CreateCredential_WithValidData_ReturnsExpected() public async Task SignCredential_WithNotExisting_ReturnsExpected() { // Arrange - A.CallTo(() => _credentialRepository.GetWalletCredentialId(_credentialId)) - .Returns(null); + A.CallTo(() => _credentialRepository.GetSigningData(_credentialId)) + .Returns(new ValueTuple(null, false)); Task Act() => _sut.SignCredential(_credentialId, CancellationToken.None); // Act @@ -107,13 +106,15 @@ public async Task SignCredential_WithNotExisting_ReturnsExpected() ex.Message.Should().Be("ExternalCredentialId must be set here"); } - [Fact] - public async Task SignCredential_WithValidData_ReturnsExpected() + [Theory] + [InlineData(true, ProcessStepTypeId.REVOKE_REISSUED_CREDENTIAL)] + [InlineData(false, ProcessStepTypeId.SAVE_CREDENTIAL_DOCUMENT)] + public async Task SignCredential_WithValidData_ReturnsExpected(bool reissuanceProcess, ProcessStepTypeId nextStep) { // Arrange var externalCredentialId = Guid.NewGuid(); - A.CallTo(() => _credentialRepository.GetWalletCredentialId(_credentialId)) - .Returns(externalCredentialId); + A.CallTo(() => _credentialRepository.GetSigningData(_credentialId)) + .Returns(new ValueTuple(externalCredentialId, reissuanceProcess)); // Act var result = await _sut.SignCredential(_credentialId, CancellationToken.None); @@ -125,7 +126,7 @@ public async Task SignCredential_WithValidData_ReturnsExpected() result.modified.Should().BeFalse(); result.processMessage.Should().BeNull(); result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.SAVE_CREDENTIAL_DOCUMENT); + result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(nextStep); } #endregion diff --git a/tests/processes/CredentialProcess.Library.Tests/CredentialExpiryProcessHandlerTests.cs b/tests/processes/CredentialProcess.Library.Tests/CredentialExpiryProcessHandlerTests.cs index 56f95cb7..1b0f8a7d 100644 --- a/tests/processes/CredentialProcess.Library.Tests/CredentialExpiryProcessHandlerTests.cs +++ b/tests/processes/CredentialProcess.Library.Tests/CredentialExpiryProcessHandlerTests.cs @@ -162,12 +162,12 @@ public async Task RevokeCredential_WithEmptyExternalCredentialId_ThrowsConflictE #region TriggerNotification [Fact] - public async Task TriggerNotification_WithValid_CallsExpected() + public async Task TriggerNotification_CredentialRejected_WithValid_CallsExpected() { // Arrange var requesterId = Guid.NewGuid(); A.CallTo(() => _credentialRepository.GetCredentialNotificationData(_credentialId)) - .Returns((VerifiedCredentialExternalTypeId.PCF_CREDENTIAL, requesterId.ToString())); + .Returns((VerifiedCredentialExternalTypeId.PCF_CREDENTIAL, requesterId.ToString(), false)); // Act var result = await _sut.TriggerNotification(_credentialId, CancellationToken.None); @@ -181,17 +181,37 @@ public async Task TriggerNotification_WithValid_CallsExpected() result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.TRIGGER_MAIL); } + [Fact] + public async Task TriggerNotification_CredentialRenewal_WithValid_CallsExpected() + { + // Arrange + var requesterId = Guid.NewGuid(); + A.CallTo(() => _credentialRepository.GetCredentialNotificationData(_credentialId)) + .Returns((VerifiedCredentialExternalTypeId.BUSINESS_PARTNER_NUMBER, requesterId.ToString(), true)); + + // Act + var result = await _sut.TriggerNotification(_credentialId, CancellationToken.None); + + // Assert + A.CallTo(() => _portalService.AddNotification(A._, requesterId, NotificationTypeId.CREDENTIAL_RENEWAL, A._)) + .MustHaveHappenedOnceExactly(); + result.modified.Should().BeFalse(); + result.processMessage.Should().BeNull(); + result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); + result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.TRIGGER_MAIL); + } + #endregion #region TriggerMail [Fact] - public async Task TriggerMail_WithValid_CallsExpected() + public async Task TriggerMail_CredentialRejected_WithValid_CallsExpected() { // Arrange var requesterId = Guid.NewGuid(); A.CallTo(() => _credentialRepository.GetCredentialNotificationData(_credentialId)) - .Returns((VerifiedCredentialExternalTypeId.PCF_CREDENTIAL, requesterId.ToString())); + .Returns((VerifiedCredentialExternalTypeId.PCF_CREDENTIAL, requesterId.ToString(), false)); // Act var result = await _sut.TriggerMail(_credentialId, CancellationToken.None); @@ -205,5 +225,24 @@ public async Task TriggerMail_WithValid_CallsExpected() result.nextStepTypeIds.Should().BeNull(); } + [Fact] + public async Task TriggerMail_CredentialRenewal_WithValid_CallsExpected() + { + // Arrange + var requesterId = Guid.NewGuid(); + A.CallTo(() => _credentialRepository.GetCredentialNotificationData(_credentialId)) + .Returns((VerifiedCredentialExternalTypeId.MEMBERSHIP_CREDENTIAL, requesterId.ToString(), true)); + + // Act + var result = await _sut.TriggerMail(_credentialId, CancellationToken.None); + + // Assert + A.CallTo(() => _portalService.TriggerMail("CredentialRenewal", requesterId, A>._, A._)) + .MustHaveHappenedOnceExactly(); + result.modified.Should().BeFalse(); + result.processMessage.Should().BeNull(); + result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); + result.nextStepTypeIds.Should().BeNull(); + } #endregion } diff --git a/tests/processes/CredentialProcess.Worker.Tests/CredentialCreationProcessTypeExecutorTests.cs b/tests/processes/CredentialProcess.Worker.Tests/CredentialCreationProcessTypeExecutorTests.cs index 16a6220d..e8886d63 100644 --- a/tests/processes/CredentialProcess.Worker.Tests/CredentialCreationProcessTypeExecutorTests.cs +++ b/tests/processes/CredentialProcess.Worker.Tests/CredentialCreationProcessTypeExecutorTests.cs @@ -22,7 +22,9 @@ using FakeItEasy; using FluentAssertions; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library; using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.Creation; +using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.Reissuance; using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Worker.Creation; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; @@ -36,6 +38,7 @@ public class CredentialCreationProcessTypeExecutorTests private readonly CredentialCreationProcessTypeExecutor _sut; private readonly ICredentialCreationProcessHandler _credentialCreationProcessHandler; private readonly ICredentialRepository _credentialRepository; + private readonly ICredentialReissuanceProcessHandler _credentialReissuanceProcessHandler; public CredentialCreationProcessTypeExecutorTests() { @@ -46,12 +49,12 @@ public CredentialCreationProcessTypeExecutorTests() var issuerRepositories = A.Fake(); _credentialCreationProcessHandler = A.Fake(); - + _credentialReissuanceProcessHandler = A.Fake(); _credentialRepository = A.Fake(); A.CallTo(() => issuerRepositories.GetInstance()).Returns(_credentialRepository); - _sut = new CredentialCreationProcessTypeExecutor(issuerRepositories, _credentialCreationProcessHandler); + _sut = new CredentialCreationProcessTypeExecutor(issuerRepositories, _credentialCreationProcessHandler, _credentialReissuanceProcessHandler); } [Fact] @@ -68,13 +71,21 @@ public void IsExecutableStepTypeId_WithValid_ReturnsExpected() _sut.IsExecutableStepTypeId(ProcessStepTypeId.SIGN_CREDENTIAL).Should().BeTrue(); } + [Fact] + public void IsExecutableStepTypeId_Withdalid_RevokeReissuedCredential_ReturnsExpected() + { + // Assert + _sut.IsExecutableStepTypeId(ProcessStepTypeId.REVOKE_REISSUED_CREDENTIAL).Should().BeTrue(); + } + [Fact] public void GetExecutableStepTypeIds_ReturnsExpected() { // Assert - _sut.GetExecutableStepTypeIds().Should().HaveCount(5).And.Satisfy( + _sut.GetExecutableStepTypeIds().Should().HaveCount(6).And.Satisfy( x => x == ProcessStepTypeId.CREATE_CREDENTIAL, x => x == ProcessStepTypeId.SIGN_CREDENTIAL, + x => x == ProcessStepTypeId.REVOKE_REISSUED_CREDENTIAL, x => x == ProcessStepTypeId.SAVE_CREDENTIAL_DOCUMENT, x => x == ProcessStepTypeId.CREATE_CREDENTIAL_FOR_HOLDER, x => x == ProcessStepTypeId.TRIGGER_CALLBACK); @@ -232,5 +243,36 @@ public async Task ExecuteProcessStep_WithServiceException_ReturnsFailedAndRetrig result.SkipStepTypeIds.Should().BeNull(); } + [Fact] + public async Task ExecuteProcessStep_RevokeIssuedCredential_WithValidData_CallsExpected() + { + // Arrange InitializeProcess + var validProcessId = Guid.NewGuid(); + var credentialId = Guid.NewGuid(); + A.CallTo(() => _credentialRepository.GetDataForProcessId(validProcessId)) + .Returns((true, credentialId)); + + // Act InitializeProcess + var initializeResult = await _sut.InitializeProcess(validProcessId, Enumerable.Empty()); + + // Assert InitializeProcess + initializeResult.Modified.Should().BeFalse(); + initializeResult.ScheduleStepTypeIds.Should().BeNull(); + + // Arrange + A.CallTo(() => _credentialReissuanceProcessHandler.RevokeReissuedCredential(credentialId)) + .Returns((null, ProcessStepStatusId.DONE, false, null)); + + // Act + var result = await _sut.ExecuteProcessStep(ProcessStepTypeId.REVOKE_REISSUED_CREDENTIAL, Enumerable.Empty(), CancellationToken.None); + + // Assert + result.Modified.Should().BeFalse(); + result.ScheduleStepTypeIds.Should().BeNull(); + result.ProcessStepStatusId.Should().Be(ProcessStepStatusId.DONE); + result.ProcessMessage.Should().BeNull(); + result.SkipStepTypeIds.Should().BeNull(); + } + #endregion } diff --git a/tests/processes/CredentialProcess.Worker.Tests/CredentialReissuanceProcessHandlerTests.cs b/tests/processes/CredentialProcess.Worker.Tests/CredentialReissuanceProcessHandlerTests.cs new file mode 100644 index 00000000..4525b209 --- /dev/null +++ b/tests/processes/CredentialProcess.Worker.Tests/CredentialReissuanceProcessHandlerTests.cs @@ -0,0 +1,100 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using FakeItEasy; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library; +using Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Library.Reissuance; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; +using Xunit; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.CredentialProcess.Worker.Tests; + +public class CredentialReissuanceProcessHandlerTests +{ + private readonly ICredentialReissuanceProcessHandler _sut; + private readonly IIssuerRepositories _issuerRepositories; + private readonly ICompanySsiDetailsRepository _companySsiDetailsRepository; + private readonly IProcessStepRepository _processStepRepository; + + public CredentialReissuanceProcessHandlerTests() + { + _issuerRepositories = A.Fake(); + _companySsiDetailsRepository = A.Fake(); + _processStepRepository = A.Fake(); + + A.CallTo(() => _issuerRepositories.GetInstance()).Returns(_companySsiDetailsRepository); + A.CallTo(() => _issuerRepositories.GetInstance()).Returns(_processStepRepository); + + _sut = new CredentialReissuanceProcessHandler(_issuerRepositories); + } + + [Fact] + public async Task RevokeReissuedCredential_WithNoCredentialFound_ThrowsConflictException() + { + // Arrange + var credentialId = Guid.NewGuid(); + A.CallTo(() => _companySsiDetailsRepository.GetCredentialToRevoke(credentialId)).Returns(null); + Task Act() => _sut.RevokeReissuedCredential(credentialId); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be("Id of the credential to revoke should always be set here"); + } + + [Fact] + public async Task RevokeReissuedCredential_CreateProcessStep_ReturnsExpected() + { + // Arrage Ids + var credentialId = Guid.NewGuid(); + var credentialIdToRevoke = Guid.NewGuid(); + var process = new Process(Guid.NewGuid(), ProcessTypeId.DECLINE_CREDENTIAL, Guid.NewGuid()); + var processStep = A.Fake(); + var companySsiDetail = new CompanySsiDetail(credentialIdToRevoke, "BPNL000001TEST", VerifiedCredentialTypeId.MEMBERSHIP, CompanySsiDetailStatusId.ACTIVE, "BPNL00001ISSUER", "test", DateTimeOffset.UtcNow); + + // Arrange + A.CallTo(() => _processStepRepository.CreateProcess(ProcessTypeId.DECLINE_CREDENTIAL)).Returns(process); + A.CallTo(() => _processStepRepository.CreateProcessStep(ProcessStepTypeId.REVOKE_CREDENTIAL, ProcessStepStatusId.TODO, process.Id)).Returns(processStep); + A.CallTo(() => _companySsiDetailsRepository.GetCredentialToRevoke(credentialId)).Returns(credentialIdToRevoke); + A.CallTo(() => _companySsiDetailsRepository.AttachAndModifyCompanySsiDetails(credentialIdToRevoke, A>._, A>._)) + .Invokes((Guid _, Action? initialize, Action modify) => + { + initialize?.Invoke(companySsiDetail); + modify(companySsiDetail); + }); + + //Act + var result = await _sut.RevokeReissuedCredential(credentialId); + + // Assert + companySsiDetail.ProcessId.Should().Be(process.Id); + result.nextStepTypeIds.Should().HaveCount(1).And.Satisfy( + x => x == ProcessStepTypeId.SAVE_CREDENTIAL_DOCUMENT); + result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); + result.modified.Should().BeFalse(); + result.processMessage.Should().BeNull(); + } +} diff --git a/tests/processes/Processes.Worker.Library.Tests/MockLogger.cs b/tests/processes/Processes.Worker.Library.Tests/MockLogger.cs index 7db4706d..3ea9c3e4 100644 --- a/tests/processes/Processes.Worker.Library.Tests/MockLogger.cs +++ b/tests/processes/Processes.Worker.Library.Tests/MockLogger.cs @@ -19,7 +19,7 @@ using Microsoft.Extensions.Logging; -namespace Org.Eclipse.TractusX.Portal.Backend.Tests.Shared; +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Processes.Worker.Library.Tests; public interface IMockLogger { diff --git a/tests/processes/Processes.Worker.Library.Tests/ProcessExecutionServiceTests.cs b/tests/processes/Processes.Worker.Library.Tests/ProcessExecutionServiceTests.cs index c395fb96..77db80ac 100644 --- a/tests/processes/Processes.Worker.Library.Tests/ProcessExecutionServiceTests.cs +++ b/tests/processes/Processes.Worker.Library.Tests/ProcessExecutionServiceTests.cs @@ -21,7 +21,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; -using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities;