diff --git a/.github/dependabot.yml b/.github/dependabot.yml index de89d4ce..f1c70b06 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -33,6 +33,11 @@ updates: ignore: - dependency-name: "*" update-types: ["version-update:semver-major"] + groups: + production-dependencies: + dependency-type: "production" + development-dependencies: + dependency-type: "development" # Github Actions - @@ -44,6 +49,9 @@ updates: - "github-actions" schedule: interval: "weekly" + groups: + dependencies: + dependency-type: "production" # Docker - @@ -58,3 +66,6 @@ updates: ignore: - dependency-name: "*" update-types: ["version-update:semver-major"] + groups: + dependencies: + dependency-type: "production" diff --git a/.github/workflows/chart-test.yml b/.github/workflows/chart-test.yml index 40833956..c69bed2d 100644 --- a/.github/workflows/chart-test.yml +++ b/.github/workflows/chart-test.yml @@ -97,7 +97,7 @@ jobs: tags: kind-registry:5000/credential-issuer-processes-worker:testing - name: Set up Helm - uses: azure/setup-helm@b7246b12e77f7134dc2d460a3d5bad15bbe29390 # v4 + uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4 with: version: v3.9.3 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index f63bdd96..5bb170fe 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -73,7 +73,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@d39d31e687223d841ef683f52467bd88e9b21c14 # v2.227 + uses: github/codeql-action/init@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v2.227 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -87,7 +87,7 @@ jobs: # Automates dependency installation for Python, Ruby, and JavaScript, optimizing the CodeQL analysis setup. # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@d39d31e687223d841ef683f52467bd88e9b21c14 # v2.227 + uses: github/codeql-action/autobuild@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v2.227 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -100,6 +100,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@d39d31e687223d841ef683f52467bd88e9b21c14 # v2.227 + uses: github/codeql-action/analyze@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v2.227 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index e79e53ba..f9d4e5a0 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -59,8 +59,11 @@ jobs: - name: List packages run: dotnet list src package --include-transitive --interactive | grep ">" | grep -Pv "\s(Org.Eclipse.TractusX|Microsoft|NuGet|System|runtime|docker|Docker|NETStandard)" | sed -E -e "s/\s+> ([a-zA-Z\.\-]+).+\s([0-9]+\.[0-9]+\.[0-9]+)\s*/nuget\/nuget\/\-\/\1\/\2/g" | awk '!seen[$0]++' > PACKAGES + - name: Download Eclipse Dash Tool + run: curl -L --output ./org.eclipse.dash.licenses.jar 'https://repo.eclipse.org/service/local/artifact/maven/redirect?r=dash-licenses&g=org.eclipse.dash&a=org.eclipse.dash.licenses&v=LATEST' + - name: Generate Dependencies file - run: java -jar ./scripts/download/org.eclipse.dash.licenses-1.1.1.jar PACKAGES -project automotive.tractusx -summary DEPENDENCIES || true + run: java -jar ./org.eclipse.dash.licenses.jar PACKAGES -project automotive.tractusx -summary DEPENDENCIES || true - name: Check if dependencies were changed id: dependencies-changed diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml index fe5ffb61..6af04cbc 100644 --- a/.github/workflows/kics.yml +++ b/.github/workflows/kics.yml @@ -45,7 +45,7 @@ jobs: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: KICS scan - uses: checkmarx/kics-github-action@8a44970e3d2eca668be41abe9d4e06709c3b3609 # v1.7.0 + uses: checkmarx/kics-github-action@d1b692d84c536f4e8696954ce7aab6818f95f5bc # v2.0.0 with: # Scanning directory . path: "." @@ -69,7 +69,7 @@ jobs: # Upload findings to GitHub Advanced Security Dashboard - name: Upload SARIF file for GitHub Advanced Security Dashboard if: always() - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: sarif_file: kicsResults/results.sarif diff --git a/.github/workflows/owasp-zap.yml b/.github/workflows/owasp-zap.yml index 622fd001..9c8fb41f 100644 --- a/.github/workflows/owasp-zap.yml +++ b/.github/workflows/owasp-zap.yml @@ -56,7 +56,7 @@ jobs: version: v0.20.0 - name: Set up Helm - uses: azure/setup-helm@b7246b12e77f7134dc2d460a3d5bad15bbe29390 # v4.1.0 + uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0 with: version: v3.5.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 47f031d3..5cff5c07 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,7 +49,7 @@ jobs: git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Install Helm - uses: azure/setup-helm@b7246b12e77f7134dc2d460a3d5bad15bbe29390 # v4 + uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4 - name: Update helm dependencies for ssi-credential-issuer run: | diff --git a/.github/workflows/trivy-dev.yml b/.github/workflows/trivy-dev.yml index 58f50ed1..18ae888e 100644 --- a/.github/workflows/trivy-dev.yml +++ b/.github/workflows/trivy-dev.yml @@ -53,7 +53,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@d710430a6722f083d3b36b8339ff66b32f22ee55 # v0.19.0 + uses: aquasecurity/trivy-action@b2933f565dbc598b29947660e66259e3c7bc8561 # v0.20.0 with: scan-type: "config" hide-progress: false @@ -63,7 +63,7 @@ jobs: timeout: "3600s" - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 if: always() with: sarif_file: "trivy-results1.sarif" @@ -86,7 +86,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@d710430a6722f083d3b36b8339ff66b32f22ee55 # v0.19.0 + uses: aquasecurity/trivy-action@b2933f565dbc598b29947660e66259e3c7bc8561 # v0.20.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/ssi-credential-issuer-service:dev" @@ -96,7 +96,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: sarif_file: "trivy-results3.sarif" @@ -118,7 +118,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@d710430a6722f083d3b36b8339ff66b32f22ee55 # v0.19.0 + uses: aquasecurity/trivy-action@b2933f565dbc598b29947660e66259e3c7bc8561 # v0.20.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/ssi-credential-issuer-migrations:dev" @@ -129,7 +129,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: sarif_file: "trivy-results9.sarif" @@ -151,7 +151,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@d710430a6722f083d3b36b8339ff66b32f22ee55 # v0.19.0 + uses: aquasecurity/trivy-action@b2933f565dbc598b29947660e66259e3c7bc8561 # v0.20.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/ssi-credential-expiry-app:dev" @@ -162,7 +162,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: sarif_file: "trivy-results9.sarif" @@ -184,7 +184,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@d710430a6722f083d3b36b8339ff66b32f22ee55 # v0.19.0 + uses: aquasecurity/trivy-action@b2933f565dbc598b29947660e66259e3c7bc8561 # v0.20.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/ssi-credential-issuer-processes-worker:dev" @@ -195,7 +195,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: sarif_file: "trivy-results9.sarif" \ No newline at end of file diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index cb1ecde1..ffa039a0 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -53,7 +53,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@d710430a6722f083d3b36b8339ff66b32f22ee55 # v0.19.0 + uses: aquasecurity/trivy-action@b2933f565dbc598b29947660e66259e3c7bc8561 # v0.20.0 with: scan-type: "config" hide-progress: false @@ -64,7 +64,7 @@ jobs: timeout: "3600s" - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 if: always() with: sarif_file: "trivy-results1.sarif" @@ -87,7 +87,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@d710430a6722f083d3b36b8339ff66b32f22ee55 # v0.19.0 + uses: aquasecurity/trivy-action@b2933f565dbc598b29947660e66259e3c7bc8561 # v0.20.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/ssi-credential-issuer-service:latest" @@ -97,7 +97,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: sarif_file: "trivy-results3.sarif" @@ -119,7 +119,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@d710430a6722f083d3b36b8339ff66b32f22ee55 # v0.19.0 + uses: aquasecurity/trivy-action@b2933f565dbc598b29947660e66259e3c7bc8561 # v0.20.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/ssi-credential-issuer-migrations:latest" @@ -129,7 +129,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: sarif_file: "trivy-results9.sarif" @@ -151,7 +151,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@d710430a6722f083d3b36b8339ff66b32f22ee55 # v0.19.0 + uses: aquasecurity/trivy-action@b2933f565dbc598b29947660e66259e3c7bc8561 # v0.20.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/ssi-credential-expiry-app:latest" @@ -161,7 +161,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: sarif_file: "trivy-results9.sarif" @@ -183,7 +183,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@d710430a6722f083d3b36b8339ff66b32f22ee55 # v0.19.0 + uses: aquasecurity/trivy-action@b2933f565dbc598b29947660e66259e3c7bc8561 # v0.20.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/ssi-credential-issuer-processes-worker:latest" @@ -193,7 +193,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: sarif_file: "trivy-results9.sarif" \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 29e9f7cd..71f627db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## [1.0.0-rc.5](https://github.com/eclipse-tractusx/ssi-credential-issuer/compare/v1.0.0-rc.4...v1.0.0-rc.5) (2024-05-23) + + +### Features + +* **issuer:** add filter to /api/issuer ([#120](https://github.com/eclipse-tractusx/ssi-credential-issuer/issues/120)) ([ea5d91a](https://github.com/eclipse-tractusx/ssi-credential-issuer/commit/ea5d91a30b18d70c0bcc46555141db6762f6af56)) + +### Bug Fixes + +* **credential:** adjust puris credential naming ([#148](https://github.com/eclipse-tractusx/ssi-credential-issuer/issues/148)) ([57c36b1](https://github.com/eclipse-tractusx/ssi-credential-issuer/commit/57c36b15f126a32a5d20321b7d7d72910d2f5f3c)), closes [#147](https://github.com/eclipse-tractusx/ssi-credential-issuer/issues/147) +* **framework:** adjust framework credential creation ([#138](https://github.com/eclipse-tractusx/ssi-credential-issuer/issues/138)) ([d07abc5](https://github.com/eclipse-tractusx/ssi-credential-issuer/commit/d07abc51da218889eb9c09869b7659a878a9166a)) +* **helm:** change ingress to work without tls enabled ([377cd66](https://github.com/eclipse-tractusx/ssi-credential-issuer/commit/377cd6634245e9f6a7f650e61064c97f46e10a37)) +* **ownedCredential:** add credential id to response ([#149](https://github.com/eclipse-tractusx/ssi-credential-issuer/issues/149)) ([f4c1bca](https://github.com/eclipse-tractusx/ssi-credential-issuer/commit/f4c1bca5374e40900e45969b5b1a2a56a5879f56)) + + +### Miscellaneous Chores + +* release 1.0.0-rc.5 ([46e479e](https://github.com/eclipse-tractusx/ssi-credential-issuer/commit/46e479efeed064503484d74b94ac13d9e1951bb1)) + ## [1.0.0-rc.4](https://github.com/eclipse-tractusx/ssi-credential-issuer/compare/v1.0.0-rc.3...v1.0.0-rc.4) (2024-05-13) diff --git a/DEPENDENCIES b/DEPENDENCIES index 08405a02..bd2541fd 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -15,8 +15,8 @@ nuget/nuget/-/Laraue.EfCoreTriggers.PostgreSql/8.0.3, MIT, approved, #13984 nuget/nuget/-/Mono.TextTemplating/2.2.1, MIT, approved, clearlydefined nuget/nuget/-/Newtonsoft.Json/13.0.1, MIT AND BSD-3-Clause, approved, #3266 nuget/nuget/-/Newtonsoft.Json/13.0.3, MIT AND BSD-3-Clause, approved, #3266 -nuget/nuget/-/Npgsql.EntityFrameworkCore.PostgreSQL/8.0.2, PostgreSQL AND MIT, approved, #13972 -nuget/nuget/-/Npgsql/8.0.2, PostgreSQL, approved, #13963 +nuget/nuget/-/Npgsql.EntityFrameworkCore.PostgreSQL/8.0.4, PostgreSQL AND MIT, approved, #13972 +nuget/nuget/-/Npgsql/8.0.3, PostgreSQL, approved, #13963 nuget/nuget/-/PasswordGenerator/2.1.0, MIT, approved, #3192 nuget/nuget/-/Portable.BouncyCastle/1.9.0, MIT, approved, clearlydefined nuget/nuget/-/SSH.NET/2020.0.2, MIT AND ISC AND LicenseRef-Public-domain AND (MIT AND MS-PL), approved, #10073 diff --git a/FILEHEADER.md b/FILEHEADER.md index 7b905c2c..0416fddf 100644 --- a/FILEHEADER.md +++ b/FILEHEADER.md @@ -13,3 +13,11 @@ Every time you create a new file or edit a file that you created and doesn't yet Currently the following templates are available: * cx_header_default * cx_header_with_# + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer \ No newline at end of file diff --git a/NOTICE.md b/NOTICE.md index cfe3af48..2ec90dc2 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -28,7 +28,7 @@ The project maintains the following source code repositories in the GitHub organ This project leverages the following third party content. -See DEPENDENCIES file. +See [DEPENDENCIES](./DEPENDENCIES) file. ## Cryptography diff --git a/README.md b/README.md index 9e60314f..6f0245ed 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ This repository contains the backend code for the SSI Credential Issuer written in C#. +For **information about the SSI Credential Issuer**, please refer to the documentation, especially the context and scope section in the [architecture documentation](./docs/architecture). + +For **installation** details, please refer to the [README.md](./charts/ssi-credential-issuer/README.md) of the provided helm chart. + ## How to build and run Install the [.NET 8.0 SDK](https://www.microsoft.com/net/download). @@ -30,6 +34,10 @@ See Docker notice files for more information: - [credential-expiry-app](./docker/notice-credential-expiry-app.md) - [credential-issuer-migrations](./docker/notice-credential-issuer-migrations.md) +## Contributing + +See [Contribution details](/docs/technical-documentation/dev-process/How%20to%20contribute.md). + ## License Distributed under the Apache 2.0 License. diff --git a/charts/ssi-credential-issuer/Chart.yaml b/charts/ssi-credential-issuer/Chart.yaml index 3868ed65..2cd6f76b 100644 --- a/charts/ssi-credential-issuer/Chart.yaml +++ b/charts/ssi-credential-issuer/Chart.yaml @@ -20,8 +20,8 @@ apiVersion: v2 name: ssi-credential-issuer type: application -version: 1.0.0-rc.4 -appVersion: 1.0.0-rc.4 +version: 1.0.0-rc.5 +appVersion: 1.0.0-rc.5 description: Helm chart for SSI Credential Issuer home: https://github.com/eclipse-tractusx/ssi-credential-issuer dependencies: diff --git a/charts/ssi-credential-issuer/README.md b/charts/ssi-credential-issuer/README.md index 34c8d632..dc44e95e 100644 --- a/charts/ssi-credential-issuer/README.md +++ b/charts/ssi-credential-issuer/README.md @@ -27,7 +27,7 @@ To use the helm chart as a dependency: dependencies: - name: ssi-credential-issuer repository: https://eclipse-tractusx.github.io/charts/dev - version: 1.0.0-rc.4 + version: 1.0.0-rc.5 ``` ## Requirements @@ -134,8 +134,7 @@ dependencies: | centralidp.address | string | `"https://centralidp.example.org"` | Provide centralidp base address (CX IAM), without trailing '/auth'. | | centralidp.useAuthTrail | bool | `true` | Flag if the api should be used with an leading /auth path | | ingress.enabled | bool | `false` | SSI Credential Issuer ingress parameters, enable ingress record generation for ssi-credential-issuer. | -| ingress.tls[0] | object | `{"hosts":[""],"secretName":""}` | Provide tls secret. | -| ingress.tls[0].hosts | list | `[""]` | Provide host for tls secret. | +| ingress.tls | list | `[]` | Ingress TLS configuration | | ingress.hosts[0] | object | `{"host":"","paths":[{"backend":{"port":8080},"path":"/api/issuer","pathType":"Prefix"}]}` | Provide default path for the ingress record. | | portContainer | int | `8080` | | | portService | int | `8080` | | diff --git a/charts/ssi-credential-issuer/values.yaml b/charts/ssi-credential-issuer/values.yaml index 80940f40..ef3b72fc 100644 --- a/charts/ssi-credential-issuer/values.yaml +++ b/charts/ssi-credential-issuer/values.yaml @@ -251,12 +251,11 @@ ingress: # nginx.ingress.kubernetes.io/proxy-body-size: "8m" # # -- Provide CORS allowed origin. # nginx.ingress.kubernetes.io/cors-allow-origin: "https://*.example.org" - tls: - # -- Provide tls secret. - - secretName: "" - # -- Provide host for tls secret. - hosts: - - "" + # -- Ingress TLS configuration + tls: [] + # - secretName: "" + # hosts: + # - "" hosts: # -- Provide default path for the ingress record. - host: "" diff --git a/consortia/argocd-app-templates/appsetup-beta.yaml b/consortia/argocd-app-templates/appsetup-beta.yaml index e2c1676b..6e6a6b18 100644 --- a/consortia/argocd-app-templates/appsetup-beta.yaml +++ b/consortia/argocd-app-templates/appsetup-beta.yaml @@ -28,7 +28,7 @@ spec: source: path: charts/ssi-credential-issuer repoURL: 'https://github.com/eclipse-tractusx/ssi-credential-issuer.git' - targetRevision: ssi-credential-issuer-1.0.0 + targetRevision: ssi-credential-issuer-1.0.0-rc.5 plugin: env: - name: AVP_SECRET diff --git a/consortia/argocd-app-templates/appsetup-int.yaml b/consortia/argocd-app-templates/appsetup-int.yaml index 7d4d167e..1fc63429 100644 --- a/consortia/argocd-app-templates/appsetup-int.yaml +++ b/consortia/argocd-app-templates/appsetup-int.yaml @@ -28,7 +28,7 @@ spec: source: path: charts/ssi-credential-issuer repoURL: 'https://github.com/eclipse-tractusx/ssi-credential-issuer.git' - targetRevision: ssi-credential-issuer-1.0.0-rc.4 + targetRevision: ssi-credential-issuer-1.0.0-rc.5 plugin: env: - name: AVP_SECRET diff --git a/consortia/argocd-app-templates/appsetup-pen.yaml b/consortia/argocd-app-templates/appsetup-pen.yaml index d8663ab7..2c627d7e 100644 --- a/consortia/argocd-app-templates/appsetup-pen.yaml +++ b/consortia/argocd-app-templates/appsetup-pen.yaml @@ -28,7 +28,7 @@ spec: source: path: charts/ssi-credential-issuer repoURL: 'https://github.com/eclipse-tractusx/ssi-credential-issuer.git' - targetRevision: ssi-credential-issuer-1.0.0-2 + targetRevision: ssi-credential-issuer-1.0.0-rc.5 plugin: env: - name: AVP_SECRET diff --git a/consortia/argocd-app-templates/appsetup-stable.yaml b/consortia/argocd-app-templates/appsetup-stable.yaml index 9f1dae8a..1c5227b0 100644 --- a/consortia/argocd-app-templates/appsetup-stable.yaml +++ b/consortia/argocd-app-templates/appsetup-stable.yaml @@ -29,7 +29,7 @@ spec: source: path: '' repoURL: 'https://eclipse-tractusx.github.io/charts/dev' - targetRevision: ssi-credential-issuer-1.0.0-rc.4 + targetRevision: ssi-credential-issuer-1.0.0-rc.5 plugin: env: - name: HELM_VALUES diff --git a/consortia/argocd-app-templates/appsetup-upgrade.yaml b/consortia/argocd-app-templates/appsetup-upgrade.yaml index 1b6c83fd..12f07f99 100644 --- a/consortia/argocd-app-templates/appsetup-upgrade.yaml +++ b/consortia/argocd-app-templates/appsetup-upgrade.yaml @@ -28,7 +28,7 @@ spec: source: path: charts/ssi-credential-issuer repoURL: 'https://github.com/eclipse-tractusx/ssi-credential-issuer.git' - targetRevision: ssi-credential-issuer-1.0.0 + targetRevision: ssi-credential-issuer-1.0.0-rc.5 plugin: env: - name: AVP_SECRET diff --git a/docker/notice-credential-issuer-service.md b/docker/notice-credential-issuer-service.md index baa55a8e..f5915022 100644 --- a/docker/notice-credential-issuer-service.md +++ b/docker/notice-credential-issuer-service.md @@ -4,7 +4,7 @@ DockerHub: [https://hub.docker.com/r/tractusx/ssi-credential-issuer-service](htt Eclipse Tractus-X product(s) installed within the image: -__Policy Hub Service__ +__SSI Credential Issuer Service__ - GitHub: https://github.com/eclipse-tractusx/ssi-credential-issuer - Project home: https://projects.eclipse.org/projects/automotive.tractusx diff --git a/docs/architecture/Architecture Constraints.md b/docs/architecture/Architecture Constraints.md new file mode 100644 index 00000000..830e8112 --- /dev/null +++ b/docs/architecture/Architecture Constraints.md @@ -0,0 +1,38 @@ +# Architecture Constraints + +## General + +- The SSI Credential Issuer is a central API for the handling of credentials. It handles the wallet communication for the creation and revocation of credentials of the issuer and holder. Another purpose is the expiry handling and automatic revocation of already expired credentials. There is no plan to implement an UI at the current stage. + +- Run anywhere: can be deployed as a docker image, e. g. on Kubernetes (platform-independent, cloud, on prem or local). + +## Developer + +- OpenSource software first - FOSS licenses approved by the eclipse foundation has to be used. It could represent the initial set that the CX community agrees on to regulate the content contribution under FOSS licenses. + +- Coding guidelines for FE and BE are defined and are to be followed for all portal related developments. + +- Apache License 2.0 - Apache License 2.0 is one of the approved licenses which should be used to respect and guarantee Intellectual property (IP). + +- Code Analysis, Linting and Code Coverage - Consistent style increases readability and maintainability of the code base. Hence, we use analyzers to enforce consistency and style rules. We enforce the code style and rules in the CI to avoid merging code that does not comply with standards. + +## Code analysis, linting and code coverage + +As part of the standard reviews, following code analysis and security checks have been executed: + +- SonarCloud Code Analysis +- Thread Modelling Analysis +- Static Application Security Testing (SAST) +- Dynamic Application Security Testing (DAST) +- Secret Scans +- Software Composition Analysis (SCA) +- Container Scans +- Infrastructure as Code (IaC) + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/docs/architecture/Context and scope.md b/docs/architecture/Context and scope.md new file mode 100644 index 00000000..f0db4d4f --- /dev/null +++ b/docs/architecture/Context and scope.md @@ -0,0 +1,18 @@ +# Content and Scope + +## Business Context + +The SSI credential issuer is created to enable the communication with wallets and handle the creation, revocation and expiry of credentials. +Additionally it gives the user a overview of available use case credentials. + +## Technical Context + +The SSI credential issuer comprise the technical foundation for interaction, monitoring, auditing and further functionalities. They are state of the art in terms of technology portfolio, consist of open-source components whenever possible and are open-sourced themselves 100%. + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/docs/architecture/Development Concept.md b/docs/architecture/Development Concept.md new file mode 100644 index 00000000..c5c0acf6 --- /dev/null +++ b/docs/architecture/Development Concept.md @@ -0,0 +1,143 @@ +# Development Concept + +## Build, test, deploy + +Details to the build, test and deploy process can get found under the following md file: [Release Process](/docs/technical-documentation/release-process/Release%20Process.md) + +## Development Guidelines + +The SSI credential issuer is using following key frameworks: + +- .Net +- Entity Framework + +### Swagger + +The API uses OpenAPI annotations to describe the endpoints with all necessary information. The annotations are then used to automatically generate the OpenAPI specification file, which can be viewed in the Swagger UI that is deployed with the application. + +#### API Dev Guidelines + +##### Implement authorization + +API's need to ensure that they only grant access to the authorized requester. For example, a user might be approved to access the API, but if they’re not allowed to add information to the application’s database via the POST method, any request to do so should be rejected. Authorization information can also be contained within a request as a token. + +Unlike some other API types, REST APIs must authenticate and authorize each request made to the server, even if multiple requests come from the same user. This is because REST communications are stateless — that is, each request can be understood by the API in isolation, without information from previous requests. + +Authorization can be governed by user roles, where each role comes with different permissions. Generally, API developers should adhere to the principle of least privilege, which states that users should only have access to the resources and methods necessary for their role, and nothing more. Predefined roles make it easier to oversee and change user permissions, reducing the chance that a bad actor can access sensitive data. + +In terms of implementation all endpoints should be secured with the highest restrictions as default. Restrictions should only be lessened through explicit exemptions. This ensures that in case of oversights an endpoint can be more secured than intended but never less secured. + +##### Validate all requests + +As mentioned, sometimes requests from perfectly valid sources may be hacking attempts. Therefore, APIs need rules to determine whether a request is friendly, friendly but invalid, or harmful, like an attempt to inject harmful code. + +An API request is only processed once its contents pass a thorough validation check — otherwise, the request should never reach the application data layer. + +Validation also includes sanity checks: Define sensible value ranges for the parameters a user provides. This especially is valid for the size of the request and the response. APIs should limit the possible number of records to process in order to prevent intentional or unintentional overloads of the system. + +##### Encrypt all requests and responses + +To prevent MITM attacks, any data transfer from the user to the API server or vice versa must be properly encrypted. This way, any intercepted requests or responses are useless to the intruder without the right decryption method. + +Since REST APIs use HTTP, encryption can be achieved by using the Transport Layer Security (TLS) protocol or Secure Sockets Layer (SSL) protocol. These protocols supply the S in “HTTPS” (“S” meaning “secure'') and are the standard for encrypting web pages and REST API communications. + +TLS/SSL only encrypts data when that data is being transferred. It doesn’t encrypt data sitting behind your API, which is why sensitive data should also be encrypted in the database layer as well. + +##### Only include necessary information in responses + +Like you might unintentionally let a secret slip when telling a story to a friend, it’s possible for an API response to expose information hackers can use. To prevent this, all responses sent to the end-user should include only the information to communicate the success or failure of the request, the resource requested (if any), and any other information directly related to these resources. + +In other words, avoid “oversharing” data — the response is a chance for you to inadvertently expose private data, either through the returned resources or verbose status messages. + +=> in the ownership of every API Developer + +##### Throttle API requests and establish quotas + +To prevent brute-force attacks like DDoS, an API can impose rate-limiting, a way to control the number of requests to the API server at any given time. + +There are two main ways to rate-limit API requests, quotas and throttling. Quotas limit the number of requests allowed from a user over a span of time, while throttling slows a user’s connection while still allowing them to use your API. + +Both methods should allow normal API requests but prevent floods of traffic intended to disrupt, as well as unexpected request spikes in general. + +##### Log API activity + +Logging API activities is extremely important when it comes to tracing user activity and in worst case hack activity. + +###### Conduct security tests + +=> see [Test Section](#tests) below + +##### Error Handling + +The simplest way we handle errors is to respond with an appropriate status code. + +Common agreed response codes: + +- 400 Bad Request – client sent an invalid request, such as lacking required request body or parameter. + Example: The same constraint has been configured multiple times in the request +- 401 Unauthorized – user authenticated but doesn't have permission to access the requested resource. + Example: User token doesn't have the access on the resource. +- 403 Forbidden – client failed to authenticate with the server. + Example: token expired oder invalid login. +- 404 Not Found – the requested resource does not exist. + Example: A specific credential was requested which does not exist in the database. +- 500 Internal Server Error – a generic error occurred in the internal system logic. + Example: Unexpected server-side issue during credential validation. + Additionally to the generic error code, a detailed message/error is needed to ensure that the issue can get validated and resolved quickly. + +##### Repository Pattern + +The repositories are used via the Factory HubRepositories, which ensures that the same database instance is used for all repositories. + +Furthermore, it provides an implicit transaction functionality. + +The repositories themselves must not be registered for dependency injection in the corresponding startup; the method IssuerRepositories.GetInstance provides the instance of a requested repository. + +In the repository itself, you should not work with SaveChanges, it should only be called via the IssuerRepositories.SaveChanges to ensure that any transaction dependencies can be rolled back. + +#### Tests + +##### User Authentication Test + +If authentication mechanisms are implemented incorrectly, attackers can compromise authentication tokens or exploit implementation flaws to assume other users’ identities and gain access to your API’s endpoints. + +To test your authentication mechanisms, try sending API requests without proper authentication (either no tokens or credentials, or incorrect ones) and see if your API responds with the correct error and messaging. + +##### Parameter Tampering Test + +To run a parameter tampering test, try various combinations of invalid query parameters in your API requests and see if it responds with the correct error codes. If not, then your API likely has some backend validation errors that need to be resolved. + +##### Injection Test + +To test if your API is vulnerable to injections, try injecting SQL, NoSQL, LDAP, OS, or other commands in API inputs and see if your API executes them. These commands should be harmless, like reboot commands or cat commands. + +##### Unhandled HTTP Methods Test + +Most APIs have various HTTP methods that are used to retrieve, store, or delete data. Sometimes web servers will give access to unsupported HTTP methods by default, which makes your API vulnerable. + +To test for this vulnerability, you should try all the common HTTP methods (POST, GET, PUT, PATCH, and DELETE) as well as a few uncommon ones. TRY sending an API request with the HEAD verb instead of GET, for example, or a request with an arbitrary method like FOO. You should get an error code, but if you get a 200 OK response, then your API has a vulnerability. + +##### Load Test + +Load testing should be one of the last steps of your API security auditing process. This type is pushing the API to its limits in order to discover any functional or security issues that have yet to be revealed. + +To achieve this, send a large number of randomized requests, including SQL queries, system commands, arbitrary numbers, and other non-text characters, and see if your API responds with errors, processes any of these inputs incorrectly, or crashes. This type of testing will mimic Overflow and DDoS attacks. + +An API manager or gateway tool will handle or help address the API security guidelines described above (including testing). + +## Migration + +To run the SSI credential issuer, migrations are needed to load the initial data inside the SSI credential issuer db to enable the SSI credential issuer to work. +The migration will consist of an initial migration as well as delta migration files with future releases. As part of a new release, a migration file (if applicable) will get released and can get loaded via a delta load. + +## Configurability + +SSI Credential Issuer configuration is mainly possible via the appsettings files as well as the static data migration files. + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/docs/architecture/Requirements.md b/docs/architecture/Requirements.md new file mode 100644 index 00000000..0c072a9a --- /dev/null +++ b/docs/architecture/Requirements.md @@ -0,0 +1,21 @@ +# Requirements overview + +## What is the Portal & Marketplace Product? + +The SSI Credential Issuer is a central API for the handling of credentials. It handles the wallet communication for the creation and revocation of credentials of the issuer and holder. Another purpose is the expiry handling and automatic revocation of already expired credentials. There is no plan to implement an UI at the current stage. + +## Requirements + + +For Catena-X Member Companies +|ID|Title|Requirement| +|--------|--------|--------| +|REQ|tbd|tbd| + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/docs/architecture/Security_Assessment.md b/docs/architecture/Security_Assessment.md index b3bf3f99..35b448ee 100644 --- a/docs/architecture/Security_Assessment.md +++ b/docs/architecture/Security_Assessment.md @@ -1,16 +1,16 @@ # Security Assessment SSI Credential Issuer -| | | -| ------------------------- | ---------------------------------------------------------------------------------------------- | -| Contact for product | [@evegufy](https://github.com/evegufy)
[@jjeroch](https://github.com/jjeroch) | -| Security responsible | tbd | -| Version number of product | 1.0.0 | -| Dates of assessment | tbd: Assessment | -| Status of assessment | Assessment Report | +| | | +| :------------------------ | :------------------------------------------------------------------------------------------------------- | +| Contact for product | [@evegufy](https://github.com/evegufy)
[@jjeroch](https://github.com/jjeroch) | +| Security responsible | [@szymonkowalczykzf](https://github.com/szymonkowalczykzf)
[@pablosec](https://github.com/pablosec) | +| Version number of product | 1.0.0 | +| Dates of assessment | 2024-05-15: Initial Assessment | +| Status of assessment | Done & Approved | ## Product Description -The SSI Credential Issuer product is an REST API project with two Process Worker processes, so a pure backend component (without implementation of an user interface). +The SSI Credential Issuer product is a REST API project with two Process Worker processes, so a pure backend component (without implementation of an user interface). The main purpose of the product is to provide authenticated CX Users the possibility to create credentials inside the issuer and holder wallet. Furthermore, it handles the revocation and expiry handling for credentials. @@ -22,7 +22,8 @@ The SSI Credential Issuer is using following key frameworks: - .Net - Entity Framework -[Development Concept](/Development%20Concept.md) + +[Development Concept](./Development%20Concept.md) ## Data Flow Diagram @@ -32,12 +33,12 @@ flowchart LR CU(Company user or Service Account) K("Keycloak (REST API)") IS(Issuer Service) - CS(Credential Service) - RS(Revocation Service) EW(Expiry Worker) IW(Issuer Wallet) + PW(Process Worker) HW(3rd Party Holder Wallets) - P(Portal Backend) + PB(Portal Backend) + PF(Portal Frontend) PHD[(Issuer DB \n Postgres \n EF Core for mapping \n objects to SQL)] subgraph centralidp[centralidp Keycloak] @@ -50,36 +51,42 @@ flowchart LR subgraph SSI-Issuer-Component Product IS - CS - RS + PW EW PHD end subgraph External Systems - P + PB + PF IW HW end K-->|"Authentication & Authorization Data \n (Using JWT)"|IS - K-->|"Authentication & Authorization Data \n (Using JWT)"|CS - K-->|"Authentication & Authorization Data \n (Using JWT)"|RS - CU-->|"Consumption of central, read-only REST API \n [HTTPS]"|IS - CU-->|"Consumption of central, read-only REST API \n [HTTPS]"|CS - CU-->|"Consumption of central, read-only REST API \n [HTTPS]"|RS - IS-->|"Read and write credentials"|PHD - IS-->|"Read and write credentials"|IW - IS-->|"Read and write credentials"|HW - EW-->|"Read and write credentials"|IW - RS-->|"Read and write credentials"|IW - P-->|"Create and revoke credentials"|IS - IS-->|"Create notifications and mails"|P - CS-->|"Read credentials and document"|PHD - RS-->|"Read and update credential data"|PHD + IS-->|"Read and write credential data"|PHD + PF-->|"Read and Write Credentials"|IS + PW-->|"Read and Write Credentials"|HW + PW-->|"Write Credentials"|IW + PW-->|"Creates Mails & Notifications"|PB + PW-->|"Revoke Credential"|IW + EW-->|"Read credentials \n write process data"|PHD + PB-->|"Create and revoke credentials"|IS + PF-->|"Read and Write Credentials"|IS + IS-->|"Create notifications and mails"|PB CU-->|"IAM with OIDC \n [HTTPS]"|K ``` +### Additional information + +* The issuer stores the signed credentials in the database for holders to download their own credentials at any time. + * Unsigned credentials are stored to be compared with signed credentials before providing them to holders. +* The signing of credentials is done externally, in the "Issuer Wallet". This application does not hold its own private signing key. +* The issuer’s DID document is created by Issuer Wallet and published by the Portal itself, not the SSI Credential Issuer application. +* The "Credential Service" can be used by a 3rd party (e.g., certificate holder) to retrieve the status of credential issuance. The "Issuer Service" is used as a starting point for requesting the issuance of a credential. +* Credentials of technical users are stored in an encrypted manner. +* All actions are logged within the Issuer DB. + ### Changes compared to last Security Assessment N/A @@ -90,7 +97,22 @@ N/A ## Threats & Risks -TBD +Only low–medium risks were identified during the security assessment: + +| 1 | Storage of credentials | +| :------ | :------ | +| Element | Issuer DB | +| Threat | (Privacy) Storing credentials of multiple parties violates best practices described by the W3C or recommended by *privacy by design* principles.
Consider only storing credentials as long as necessary (usually, until they are submitted to the holder).
See also [W3C: Verifiable Credentials Data Model v2.0, 8.12 Storage Providers and Data Mining](https://www.w3.org/TR/vc-data-model-2.0/#storage-providers-and-data-mining) and [8.16 Data Theft](https://www.w3.org/TR/vc-data-model-2.0/#data-theft) as well as *data minimization* in general. | + +| 2 | No technical limitation for batch-revocation of credentials | +| :------ | :------ | +| Element | Revocation Service | +| Threat | A single person with the appropriate permissions is able to revoke all credentials. Consider the implementation of a two-person rule for critical actions. | + +| 3 | Revocation of mismatched credentials | +| :------ | :------ | +| Element | Issuer Service/Revocation Service | +| Threat | If a difference between the unsigned and signed credential is identified, the credential should be revoked even if it was never provided to the holder. | ### Mitigated Threats diff --git a/docs/architecture/Solution strategy.md b/docs/architecture/Solution strategy.md new file mode 100644 index 00000000..5dd0f4e1 --- /dev/null +++ b/docs/architecture/Solution strategy.md @@ -0,0 +1,14 @@ +# Solution Strategy + +- The technology portfolio and development stack are kept simple, based on commodity and oss components and products. +- APIs are always REST-based with token authentication. +- OIDC is used for authentication and authorization. +- IaC is fully realized via helm charts. + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/docs/architecture/Whitebox Overall System.md b/docs/architecture/Whitebox Overall System.md new file mode 100644 index 00000000..a7b603b3 --- /dev/null +++ b/docs/architecture/Whitebox Overall System.md @@ -0,0 +1,55 @@ +# Whitebox Overall System + +## Summary + +In the following image you see the overall system overview of the SSI Credential Issuer + +```mermaid +flowchart LR + + C(Customer) + ING(Ingress) + IS(Issuer Service) + ES(Expiry Service) + PW(Process Worker) + P(Portal) + HW(Holder Wallet) + IW(Issuer Wallet) + PHD[("Postgres Database \n \n (Base data created with \n application seeding)")] + + subgraph SSI Credential Issuer Product + ING + PHD + IS + ES + PW + end + + subgraph External Systems + P + HW + IW + end + + C-->|"Authentication & Authorization Data \n (Using JWT)"|ING + ING-->|"Forward Request"|IS + ES-->|"Revokes Credentials"|IW + ES-->|"Revokes Credentials"|HW + ES-->|"Creates Mails & Notifications"|P + ES-->|"Reads & updates credentials"|PHD + PW-->|"Read, Write & Sign Credentials"|IW + PW-->|"Write Credentials"|HW + PW-->|"Creates Mails & Notifications"|P + PW-->|"Reads credential requests, \n saves documents"|PHD + IS-->|"Read credentialTypes and versions, \n saves credential requests, \n revokes credential"|PHD + ES-->|"Updates credentials"|PHD + +``` + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/docs/architecture/operational-concept.md b/docs/architecture/operational-concept.md new file mode 100644 index 00000000..3e556b5d --- /dev/null +++ b/docs/architecture/operational-concept.md @@ -0,0 +1,47 @@ +# Operational concepts + +## SSI Credential Issuer Services + +### Configuration + +The SSI Credential Issuer services can be configured using two methods: + +### appsettings.json + +If you build the SSI Credential Issuer, you can modify the appsettings.json for each backend service, to individually configure to a certain extend. This file contains all possible config entries for the application. + +### Helm Chart + +The most relevant config properties are exposed as environment variables and must be set in the Helm chart so the application can run at all. Check the SSI Credential Issuer Helm chart in Git for all available variables. + +### DB Migration File + +Static Data migration files provide a certain configuration possibility by adding or deleting static data records before the deployment. Be aware that touching static data files will always impact the application business process. It is suggested to always test the application with the planned changes carefully in INT before releasing to a productive env. + +## Disaster-Recovery + +Note: will be added soon + +## Scaling + +If the number of consumers raises, the IRS can be scaled up by using more resources for the Deployment Pod. Those resources can be used to utilize more parallel threads to handle Job execution. + +## Clustering + +Note: will be added soon + +## Monitoring + +Currently all backend services write log entries as structural data in json format. These logs can easily be monitored. There are several options to provide a stable monitoring solution, one of them is to setup loki and grafana. In this solution loki is used as a datasource and custom dashboards can be setup in grafana to monitor the services. Some general Properties to query with grafana are: + +- StatusCode - contains the status code of the response +- Elapsed - contains the time a endpoint took to response in milliseconds +- RenderedMessage - contains the log message with possible errors + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/docs/database/auditing.md b/docs/database/auditing.md new file mode 100644 index 00000000..da97f041 --- /dev/null +++ b/docs/database/auditing.md @@ -0,0 +1,77 @@ +# Audit Logging + +For db audit logs, the following approach is used + +- Application Logging + +## Application Audit Logging + +Application Audit Logging is implemented to fulfill and enable extended audit log functionalities for the SSI Credential Issuer. + +In the implementation the possibility of pgAudit extension and db table logging inside the postgreSQL was validated. + +Due to the functional requirement to enable search, quick validations and audit traceability, the in-DB audit implementation got preferred. + +### Implementation Details + +What: for each audit relevant table, an audit_tablename_version table is created and storing any original table INSERT, DELETE or UPDATE records. Important, we always store: + +- Who changed +- What +- When + +To support auditing in the project we implemented a custom solution which overrides the `SaveChanges` and `SaveChangesAsync` methods within the `IssuerDbContext`. The specific auditing implementation can be found within the `AuditHandlerV1`. + +For all entities that implement the `IAuditableV1` and are inserted or modified the property marked with the `AuditLastEditorV1` Attribute will automatically be set to the current user, as well as the `DateLastChanged` property will be automatically set to the current dateTime. + +Additionally the AuditEntity defined in the `AuditEntityV1` Attribute will automatically be created and mapped with the values of the entity. After that the newly created audit entity will be saved in the specific Audit table. + +### Why we use versioning for the audit tables? + +To ensure that an audit table is never modified, but only entries are added, we have introduced versioning for audit table. + +Each audit table has version suffix in the name, this corresponds to the name of the migration in which the audit table was added. + +See the example below for further information: + +### How to enable an entity to be auditable in the code (Example: Document) + +1. Create Audit entity which should contains all properties that are no navigation properties from the auditable entity. The audit entity must implement the `IAuditEntityV1` interface and mark the with the `Key` Attribute. + +```c# +public class AuditDocument20240305 : IAuditEntityV1 +{ + [Key] + public Guid AuditV1Id { get; set; } + + ... +} +``` + +2. The auditable entity needs to implement interface 'IAuditableV1' and be marked with the `AuditEntityV1` Attribute + +```c# +[AuditEntityV1(typeof(AuditDocument20240305))] +public class Document : IAuditableV1 +``` + +3. Add DbSet in PortalDbContext for the newly created Entity + +```c# +public virtual DbSet AuditDocument20240305 { get; set; } = default!; +``` + +1. Add Migration + +Additional relevant information: + +- (warning) If not already existing in the original table, a uuid and last_editor_id attribute need to get added to the original entity. +- (warning) whenever the original table attributes are changed (e.g. adding an attribute or deleting and attribute) the already existing audit table will be set to frozen and a new audit table is getting created. All new audit relevant records will be stored in the new audit table + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/docs/database/db-view.md b/docs/database/db-view.md new file mode 100644 index 00000000..468f142e --- /dev/null +++ b/docs/database/db-view.md @@ -0,0 +1,197 @@ + +# Database View + +- [Database View](#database-view) + - [Database Overview](#database-overview) + - [Database Structure](#database-structure) + - [Enum Value Tables](#enum-value-tables) + - [Mapping Tables](#mapping-tables) + - [Credentials](#credentials) + - [Process Handling](#process-handling) + - [NOTICE](#notice) + +## Database Overview + +```mermaid +erDiagram + COMPANY_SSI_DETAIL_ASSIGNED_DOCUMENTS { + uuid document_id PK + uuid company_ssi_detail_id PK + } + COMPANY_SSI_DETAIL_STATUSES { + integer id PK + text label + } + COMPANY_SSI_DETAILS { + uuid id PK + text bpnl + text issuer_bpn + integer verified_credential_type_id FK + integer company_ssi_detail_status_id FK + timestamp date_created + text creator_user_id + timestamp expiry_date + uuid verified_credential_external_type_detail_version_id FK + integer expiry_check_type_id FK + uuid process_id + uuid external_credential_id + text credential + timestamp date_last_changed + text last_editor_id + } + COMPANY_SSI_PROCESS_DATA { + uuid company_ssi_detail_id PK + jsonb schema + integer credential_type_kind_id FK + text client_id + bytea client_secret + bytea initialization_vector + integer encryption_mode + text holder_wallet_url + text callback_url + } + DOCUMENT_STATUS { + integer id PK + text label + } + DOCUMENT_TYPES { + integer id PK + text label + } + DOCUMENTS { + uuid id PK + timestamp date_created + bytea document_hash + bytea document_content + text document_name + integer media_type_id FK + integer document_type_id FK + integer document_status_id FK + text identity_id + timestamp date_last_changed + text last_editor_id + } + EXPIRY_CHECK_TYPES { + integer id PK + text label + } + MEDIA_TYPES { + integer id PK + text label + } + PROCESS_STEP_STATUSES { + integer id PK + text label + } + PROCESS_STEP_TYPES { + integer id PK + text label + } + PROCESS_STEPS { + uuid id PK + integer process_step_type_id FK + integer process_step_status_id FK + uuid process_id FK + timestamp date_created + timestamp date_last_changed + text message + } + PROCESS_TYPES { + integer id PK + text label + } + PROCESSES { + uuid id PK + integer process_type_id FK + timestamp lock_expiry_date + uuid version + } + USE_CASES { + uuid id PK + text name + text shortname + } + VERIFIED_CREDENTIAL_EXTERNAL_TYPE_DETAIL_VERSIONS { + uuid id PK + integer verified_credential_external_type_id FK + text version + text template + timestamp valid_from + timestamp expiry + } + VERIFIED_CREDENTIAL_TYPE_ASSIGNED_EXTERNAL_TYPES { + int verified_credential_type_id PK + int verified_credential_external_type_id PK + } + VERIFIED_CREDENTIAL_EXTERNAL_TYPES { + integer id PK + text label + } + VERIFIED_CREDENTIAL_TYPE_ASSIGNED_KINDS { + int verified_credential_type_id PK + int verified_credential_type_kind_id PK + } + VERIFIED_CREDENTIAL_TYPE_ASSIGNED_USE_CASES { + int verified_credential_type_id PK + uuid use_case_id PK + } + VERIFIED_CREDENTIAL_TYPE_KINDS { + integer id PK + text label + } + VERIFIED_CREDENTIAL_TYPES { + integer id PK + text label + } + COMPANY_SSI_DETAIL_ASSIGNED_DOCUMENTS ||--|| COMPANY_SSI_DETAILS : company_ssi_detail_id + COMPANY_SSI_DETAIL_ASSIGNED_DOCUMENTS ||--|| DOCUMENTS : document_id + COMPANY_SSI_DETAILS ||--|| VERIFIED_CREDENTIAL_TYPES : verified_credential_type_id + COMPANY_SSI_DETAILS ||--|| COMPANY_SSI_DETAIL_STATUSES : company_ssi_detail_status_id + COMPANY_SSI_DETAILS ||--|| VERIFIED_CREDENTIAL_EXTERNAL_TYPE_DETAIL_VERSIONS : verified_credential_external_type_detail_version_id + COMPANY_SSI_DETAILS ||--|| EXPIRY_CHECK_TYPES : expiry_check_type_id + COMPANY_SSI_DETAILS ||--|| PROCESSES : process_id + COMPANY_SSI_PROCESS_DATA ||--|| COMPANY_SSI_DETAILS : company_ssi_detail_id + COMPANY_SSI_PROCESS_DATA ||--|| VERIFIED_CREDENTIAL_TYPE_KINDS : credential_type_kind_id + DOCUMENTS ||--|| DOCUMENT_STATUS : document_status_id + DOCUMENTS ||--|| DOCUMENT_TYPES : document_type_id + PROCESS_STEPS ||--|| PROCESS_STEP_STATUSES : process_step_status_id + PROCESS_STEPS ||--|| PROCESS_STEP_TYPES : process_step_type_id + PROCESS_STEPS ||--|| PROCESSES : process_id + PROCESSES ||--|| PROCESS_TYPES : process_type_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 + VERIFIED_CREDENTIAL_TYPE_ASSIGNED_KINDS ||--|| VERIFIED_CREDENTIAL_TYPES : verified_credential_type_id + VERIFIED_CREDENTIAL_TYPE_ASSIGNED_KINDS ||--|| VERIFIED_CREDENTIAL_TYPE_KINDS : verified_credential_type_kind_id + VERIFIED_CREDENTIAL_TYPE_ASSIGNED_USE_CASES ||--|| USE_CASES : use_case_id + VERIFIED_CREDENTIAL_TYPE_ASSIGNED_USE_CASES ||--|| VERIFIED_CREDENTIAL_TYPES : verified_credential_type_id + +``` + +## Database Structure + +The database is organized into several key tables, each serving a specific purpose: + +### Enum Value Tables + +`company_ssi_detail_status`, `document_status`, `document_types`, `expiry_check_types`, `media_types`, `process_step_statuses`, `process_step_types`, `process_steps`, `process_types`, `verified_credential_external_types`, `verified_credential_type_kinds`, `verified_credential_types` are tables designed to store enum values. They contain an id and label, derived from the backend enums. + +### Mapping Tables + +`company_ssi_detail_assigned_documents` and `verified_credential_type_assigned_external_types`, `verified_credential_type_assigned_kinds`, `verified_credential_type_assigned_use_cases` are used to map entities. + +### Credentials + +The `company_ssi_details` table is utilized to safe the credential requests and set their status. + +### Process Handling + +The tables `processes`, `process_steps` are used for the processing of the credential creation and revocation. + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/docs/database/seeding.md b/docs/database/seeding.md new file mode 100644 index 00000000..065e2c9d --- /dev/null +++ b/docs/database/seeding.md @@ -0,0 +1,42 @@ +# Seeding Mechanism + +## Database Seeding + +All data for the SSI Credential Issuer are stored in the database. A seeding process has been implemented to populate the database with initial data. + +## Execution + +The seeding process is triggered by the SsiCredentialIssuer.Migrations job. During this process, data is sourced from .json files located in a configurable directory. The default directory for the base setup is Seeder -> Data. + +## Configuration + +To specify the data to be seeded, configure the `BatchInsertSeeder`. This configuration includes the following details: + +- Database Table: Identify the target database table +- File Name: Specify the name of the .json file containing the data +- Primary Keys: The primary keys which check for existing entries + +To specify the data which should be modified within the seeding, configure the `BatchUpdateSeeder`. This configuration includes the following details: + +- Database Table: Identify the target database table +- File Name: Specify the name of the .json file containing the data +- Primary Keys: The primary keys which check for existing entries +- Where Clause: Check for the entries in the table that should be modified +- Update Entries: The function that should be executed to modify the entries. + +## Data Integrity + +The seeder includes a check to ensure that only data not yet existing in the database is written. This prevents duplication of records. + +## Limitations + +- While the seeder is designed to add new data, it does not support deletion. Any existing records in the database will not be removed by the seeding process. +- Modifications of the enum values must be made with a migration. + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/docs/requests/Org.Eclipse.TractusX.SsiCredentialIssuer.Service.postman_collection.json b/docs/requests/Org.Eclipse.TractusX.SsiCredentialIssuer.Service.postman_collection.json new file mode 100644 index 00000000..a9d61a88 --- /dev/null +++ b/docs/requests/Org.Eclipse.TractusX.SsiCredentialIssuer.Service.postman_collection.json @@ -0,0 +1,581 @@ +{ + "info": { + "_postman_id": "7cb5f0cb-ddce-4926-922f-aaf9307770b7", + "name": "Org.Eclipse.TractusX.SsiCredentialIssuer.Service", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "2223943" + }, + "item": [ + { + "name": "api", + "item": [ + { + "name": "issuer", + "item": [ + { + "name": "useCaseParticipation", + "item": [ + { + "name": "Gets all use case frameworks and the participation status of the acting company", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/issuer/useCaseParticipation", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "issuer", + "useCaseParticipation" + ] + }, + "description": "Example: GET: api/issuer/useCaseParticipation" + }, + "response": [] + } + ] + }, + { + "name": "certificates", + "item": [ + { + "name": "Gets all company certificate requests and their status", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/issuer/certificates", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "issuer", + "certificates" + ] + }, + "description": "Example: GET: api/issuer/certificates" + }, + "response": [] + } + ] + }, + { + "name": "certificateTypes", + "item": [ + { + "name": "Gets the certificate types for which the company can apply for", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/issuer/certificateTypes", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "issuer", + "certificateTypes" + ] + }, + "description": "Example: GET: api/issuer/certificateTypes" + }, + "response": [] + } + ] + }, + { + "name": "owned-credentials", + "item": [ + { + "name": "Gets all outstanding, existing and inactive credentials for the company of the user", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/issuer/owned-credentials", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "issuer", + "owned-credentials" + ] + }, + "description": "Example: GET: /api/issuer/owned-credentials" + }, + "response": [] + } + ] + }, + { + "name": "bpn", + "item": [ + { + "name": "Creates a bpn credential for the given data", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"holder\": \"\",\n \"businessPartnerNumber\": \"\",\n \"technicalUserDetails\": {\n \"walletUrl\": \"\",\n \"clientId\": \"\",\n \"clientSecret\": \"\"\n },\n \"callbackUrl\": \"\"\n}", + "options": { + "raw": { + "headerFamily": "json", + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/issuer/bpn", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "issuer", + "bpn" + ] + }, + "description": "POST: api/issuer/bpn" + }, + "response": [] + } + ] + }, + { + "name": "membership", + "item": [ + { + "name": "Creates a membership credential for the given data", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"holder\": \"\",\n \"businessPartnerNumber\": \"\",\n \"memberOf\": \"\",\n \"technicalUserDetails\": {\n \"walletUrl\": \"\",\n \"clientId\": \"\",\n \"clientSecret\": \"\"\n },\n \"callbackUrl\": \"\"\n}", + "options": { + "raw": { + "headerFamily": "json", + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/issuer/membership", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "issuer", + "membership" + ] + }, + "description": "POST: api/issuer/membership" + }, + "response": [] + } + ] + }, + { + "name": "framework", + "item": [ + { + "name": "Creates a framework credential for the given data", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"holder\": \"\",\n \"businessPartnerNumber\": \"\",\n \"useCaseFrameworkId\": \"BUSINESS_PARTNER_NUMBER\",\n \"useCaseFrameworkVersionId\": \"\",\n \"technicalUserDetails\": {\n \"walletUrl\": \"\",\n \"clientId\": \"\",\n \"clientSecret\": \"\"\n },\n \"callbackUrl\": \"\"\n}", + "options": { + "raw": { + "headerFamily": "json", + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/issuer/framework", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "issuer", + "framework" + ] + }, + "description": "POST: api/issuer/framework" + }, + "response": [] + } + ] + }, + { + "name": "{credentialId}", + "item": [ + { + "name": "approval", + "item": [ + { + "name": "Approves the given credential and triggers the verified credential creation", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/issuer/:credentialId/approval", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "issuer", + ":credentialId", + "approval" + ], + "variable": [ + { + "key": "credentialId", + "value": "", + "description": "(Required) Id of the entry that should be approved" + } + ] + }, + "description": "PUT: api/issuer/{credentialId}/approval" + }, + "response": [] + } + ] + }, + { + "name": "reject", + "item": [ + { + "name": "Rejects the given credential", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/issuer/:credentialId/reject", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "issuer", + ":credentialId", + "reject" + ], + "variable": [ + { + "key": "credentialId", + "value": "", + "description": "(Required) Id of the entry that should be rejected" + } + ] + }, + "description": "PUT: api/issuer/{credentialId}/reject" + }, + "response": [] + } + ] + } + ] + }, + { + "name": "Gets all outstanding, existing and inactive credentials", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/issuer?page=&size=&companySsiDetailStatusId=INACTIVE&credentialTypeId=CIRCULAR_ECONOMY&sorting=BpnlAsc", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "issuer" + ], + "query": [ + { + "key": "page", + "value": "", + "description": "The page to get" + }, + { + "key": "size", + "value": "", + "description": "Amount of entries" + }, + { + "key": "companySsiDetailStatusId", + "value": "INACTIVE", + "description": "OPTIONAL: Filter for the status" + }, + { + "key": "credentialTypeId", + "value": "CIRCULAR_ECONOMY", + "description": "OPTIONAL: The type of the credential that should be returned" + }, + { + "key": "sorting", + "value": "BpnlAsc", + "description": "OPTIONAL: Search string for the company name" + } + ] + }, + "description": "Example: GET: /api/issuer" + }, + "response": [] + } + ] + }, + { + "name": "revocation", + "item": [ + { + "name": "issuer", + "item": [ + { + "name": "credentials", + "item": [ + { + "name": "{credentialId}", + "item": [ + { + "name": "Revokes an credential which was issued by the given issuer", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/revocation/issuer/credentials/:credentialId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "revocation", + "issuer", + "credentials", + ":credentialId" + ], + "variable": [ + { + "key": "credentialId", + "value": "", + "description": "(Required) Id of the credential that should be revoked" + } + ] + }, + "description": "POST: api/revocation/issuer/credentials/{credentialId}" + }, + "response": [] + } + ] + } + ] + } + ] + }, + { + "name": "credentials", + "item": [ + { + "name": "{credentialId}", + "item": [ + { + "name": "Revokes an credential of an holder", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/revocation/credentials/:credentialId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "revocation", + "credentials", + ":credentialId" + ], + "variable": [ + { + "key": "credentialId", + "value": "", + "description": "(Required) Id of the credential that should be revoked" + } + ] + }, + "description": "POST: api/revocation/credentials/{credentialId}" + }, + "response": [] + } + ] + } + ] + } + ] + }, + { + "name": "credential", + "item": [ + { + "name": "{credentialId}", + "item": [ + { + "name": "The endpoint enables users to download the credential (full json) of their own company.", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/credential/:credentialId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "credential", + ":credentialId" + ], + "variable": [ + { + "key": "credentialId", + "value": "", + "description": "(Required) " + } + ] + }, + "description": "Example: GET: api/credential/{credentialId}" + }, + "response": [] + } + ] + } + ] + } + ] + } + ], + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ssiToken}}", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "https://localhost:7001" + } + ] +} \ No newline at end of file diff --git a/docs/technical-documentation/dev-process/Dev-flow_deploy-dev-env.md b/docs/technical-documentation/dev-process/Dev-flow_deploy-dev-env.md index 8d2dc34c..4bae9934 100644 --- a/docs/technical-documentation/dev-process/Dev-flow_deploy-dev-env.md +++ b/docs/technical-documentation/dev-process/Dev-flow_deploy-dev-env.md @@ -17,9 +17,7 @@ flowchart LR Note\* Every pull request (PR) requires at least one approving review by a committer -Note\*\* Unit tests and Sonarcloud runs at pull request, Trivy and KICS scans at merge as well as daily and Veracode scan runs weekly - -Note\*\* Trivy and KICS scans are scheduled to daily +Note\*\* Unit tests and code analysis checks run at pull request ## NOTICE diff --git a/docs/technical-documentation/dev-process/Enumeration Handling.md b/docs/technical-documentation/dev-process/Enumeration Handling.md new file mode 100644 index 00000000..cb017940 --- /dev/null +++ b/docs/technical-documentation/dev-process/Enumeration Handling.md @@ -0,0 +1,47 @@ +# Enumeration + +Enum or enumeration are used for data type consisting of named values like elements, status workflow, types, etc., that represent integral constants. Enums are non-transactional (so called static data) which can only get changed in a new application version. Changes in the operation mode of an application are not allowed since this will result into possible system breaks. + +List of used enums in the ssi credential issuer application + +- company_ssi_detail_status +- document_status +- document_types +- expiry_check_types +- media_types +- process_step_statuses +- process_step_types +- process_steps +- process_types +- verified_credential_external_types +- verified_credential_type_kinds +- verified_credential_types + +## Add Enums + +New enums can get added easily be enhancing the enumeration table (via the seeding data). With the next deployment; the new enum is getting auto deployed to the respective env. +Since enums have an enhanced impact on the system functionality; it is mandatorily needed to test (FE wise) the impacted screens / flows before releasing new enums. It is likely that the enum has an enhanced impact on the user journey / flow and break the system if not well tested. + +## Change Enums + +Change of enums (labels) is possible but need to be done carefully and only if necessarily needed. +In the case a change is getting executed; the system configuration / appsettings / env. variables need to get checked to ensure that those don't refer to the enum which is getting changed/ updated. + +## Delete Enums + +Deletion of enums have following impacts + +- Seeding data update needed (likely data need to get deleted / changed) +- Data inside the database in the different running environments need to get updated +- User flow process impacted +- Business logic impacted + +**It is not recommended to delete enums.** + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/docs/technical-documentation/release-process/Release Process.md b/docs/technical-documentation/release-process/Release Process.md new file mode 100644 index 00000000..82f49aee --- /dev/null +++ b/docs/technical-documentation/release-process/Release Process.md @@ -0,0 +1,104 @@ +# Release Process + +The release process for a new version can roughly be divided into the following steps: + +- [Release Process](#release-process) + - [Preparations on the release branch](#preparations-on-the-release-branch) + - [1. Aggregate migrations](#1-aggregate-migrations) + - [2. Version bump](#2-version-bump) + - [3. Update README (on chart level)](#3-update-readme-on-chart-level) + - [Update CHANGELOG.md](#update-changelogmd) + - [Merge release branch](#merge-release-branch) + - [RC: provide successive rc branch and change base of open PRs](#rc-provide-successive-rc-branch-and-change-base-of-open-prs) + +The process builds on the development flow which, usually, takes place within forks and leads to merged pull requests in the repositories of the eclipse-tractusx organization. + +For assigning and incrementing **version** numbers [Semantic Versioning](https://semver.org) is followed. + +## Preparations on the release branch + +Checking out from the dev branch a release branch (release/{to be released version} e.g. release/v1.2.0, or respectively release/v1.2.0-rc.1 for a release candidate). +On the release branch the following steps are executed: + +### 1. Aggregate migrations + +Migrations should be **aggregated in the case of releasing a new version**, in order to not release the entire history of migrations which accumulate during the development process. + +Once a version has been released, migrations **mustn't be aggregated** in order to ensure upgradeability this also applies to **release candidates > rc.1 and hotfixes**. +Be aware that migrations coming release branches for release candidates or from hotfix branches, will **need to be incorporated into dev and main**. + +### 2. Version bump + +The version needs to be updated in the `src` directory within the 'Directory.Build.props' file. + +Also, bump the chart and app version in the [Chart.yaml](../../../charts/ssi-credential-issuer/Chart.yaml) and the version of the images in the [values.yaml](../../../charts/ssi-credential-issuer/values.yaml). + +_Consortia relevant: Update the version of the targetRevision tag in the [argocd-app-templates](../../../consortia/argocd-app-templates/), used for consortia-environments._ + +Example for commit message: + +_build: bump version for vx.x.x_ + +### 3. Update README (on chart level) + +Use [helm-docs](https://github.com/norwoodj/helm-docs) (gotemplate driven) for updating the README file. + +```bash +helm-docs --chart-search-root [charts-dir] --sort-values-order file +``` + +Example for commit message: + +_build: update readme for vx.x.x_ + +## Update CHANGELOG.md + +The changelog file tracks all notable changes since the last released version. +Once a new version is ready to be released, the changelog can get updated via an automatically created pull request using the [release-please workflow](../../../.github/workflows/release-please.yml) which can be triggered manually or by pushing a _changelog/v*.*.*_ branch. + +Please see: + +- [How release please works](https://github.com/google-github-actions/release-please-action/tree/v4.0.2?tab=readme-ov-file#how-release-please-works) +- [How do I change the version number?](https://github.com/googleapis/release-please/tree/v16.7.0?tab=readme-ov-file#how-do-i-change-the-version-number) +- [How can I fix release notes?](https://github.com/googleapis/release-please/tree/v16.7.0?tab=readme-ov-file#how-can-i-fix-release-notes) + +## Merge release branch + +The release branch must be merged into main. +Afterwards, main into dev. +Those merges need to happen via PRs. + +Example for PR titles: + +_build(1.2.0): merge release into main_ + +_build(1.2.0): merge main to dev_ + +> Be aware that the merge into main triggers the workflow with the [helm-chart releaser action](../../../.github/workflows/chart-release.yaml). +> +> The workflow creates a 'ssi-credential-issuer-x.x.x' tag and release. The release contains the new chart. +> +> This workflow also pushes the version tag that triggers the [release workflow](../../../.github/workflows/release.yml) which creates the versioned docker image/s. + + +_Consortia relevant: The 'ssi-credential-issuer-x.x.x' tag is used to install (with the convenience of the argocd-app-templates) or upgrade the version via AgroCD on the consortia K8s clusters._ + +## RC: provide successive rc branch and change base of open PRs + +During a release candidate phase, checkout the successive 'rc' branch and push it to the server, so that it can be used for further bugfixes. + +Example: + +```bash +git checkout tags/v0.1.0-rc.2 -b release/v0.1.0-rc.3 +``` + +Also make sure to change the base of all open pull requests still pointing to the previous 'rc' branch to the newly pushed 'rc' branch. + +## NOTICE + +This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation +- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/scripts/add_notice_footer.sh b/scripts/add_notice_footer.sh old mode 100644 new mode 100755 diff --git a/scripts/check-dependencies.md b/scripts/check-dependencies.md deleted file mode 100644 index ef25c0e9..00000000 --- a/scripts/check-dependencies.md +++ /dev/null @@ -1,17 +0,0 @@ -# Check dependencies - -Dependencies are checked by the [Eclipse Dash License Tool](https://github.com/eclipse/dash-licenses) with a GitHub workflow (dependencies.yaml). - -This workflow uses the executable jar in the download directory. - -In order to update the executable jar run the following command from the root directory: - - curl -L --output ./scripts/download/org.eclipse.dash.licenses-1.1.1.jar 'https://repo.eclipse.org/service/local/artifact/maven/redirect?r=dash-licenses&g=org.eclipse.dash&a=org.eclipse.dash.licenses&v=LATEST' - -## NOTICE - -This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). - -- SPDX-License-Identifier: Apache-2.0 -- SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation -- Source URL: https://github.com/eclipse-tractusx/ssi-credential-issuer diff --git a/scripts/download/org.eclipse.dash.licenses-1.1.1.jar b/scripts/download/org.eclipse.dash.licenses-1.1.1.jar deleted file mode 100644 index 2a031d64..00000000 Binary files a/scripts/download/org.eclipse.dash.licenses-1.1.1.jar and /dev/null differ diff --git a/src/Directory.Build.props b/src/Directory.Build.props index fc27ea0c..aa4a8588 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -20,6 +20,6 @@ 1.0.0 - rc.4 + rc.5 diff --git a/src/database/SsiCredentialIssuer.DbAccess/Models/CompanySsiApprovalType.cs b/src/database/SsiCredentialIssuer.DbAccess/Models/CompanySsiApprovalType.cs new file mode 100644 index 00000000..ec988c96 --- /dev/null +++ b/src/database/SsiCredentialIssuer.DbAccess/Models/CompanySsiApprovalType.cs @@ -0,0 +1,38 @@ +/******************************************************************************** + * 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.Entities; + +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Models; + +/// +/// Possible filter options for the pagination +/// +public enum CompanySsiDetailApprovalType +{ + /// + /// The credential request will be automatically approved + /// + Automatic = 1, + + /// + /// The operator needs to approve the credential request + /// + Manual = 2, +} diff --git a/src/database/SsiCredentialIssuer.DbAccess/Models/OwnedVerifiedCredentialData.cs b/src/database/SsiCredentialIssuer.DbAccess/Models/OwnedVerifiedCredentialData.cs index 62d6b1ef..6cf03d00 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Models/OwnedVerifiedCredentialData.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Models/OwnedVerifiedCredentialData.cs @@ -22,6 +22,7 @@ namespace Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Models; public record OwnedVerifiedCredentialData( + Guid CredentialDetailId, VerifiedCredentialTypeId CredentialType, CompanySsiDetailStatusId Status, DateTimeOffset? ExpiryDate, diff --git a/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs b/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs index 128f46c9..d3c5b297 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs @@ -169,17 +169,19 @@ public Task CheckSsiDetailsExistsForCompany(string bpnl, VerifiedCredentia .SingleOrDefaultAsync(); /// - public IQueryable GetAllCredentialDetails(CompanySsiDetailStatusId? companySsiDetailStatusId, VerifiedCredentialTypeId? credentialTypeId) => + public IQueryable GetAllCredentialDetails(CompanySsiDetailStatusId? companySsiDetailStatusId, VerifiedCredentialTypeId? credentialTypeId, CompanySsiDetailApprovalType? approvalType) => _context.CompanySsiDetails.AsNoTracking() .Where(c => (!companySsiDetailStatusId.HasValue || c.CompanySsiDetailStatusId == companySsiDetailStatusId.Value) && - (!credentialTypeId.HasValue || c.VerifiedCredentialTypeId == credentialTypeId)); + (!credentialTypeId.HasValue || c.VerifiedCredentialTypeId == credentialTypeId) && + (!approvalType.HasValue || (approvalType.Value == CompanySsiDetailApprovalType.Automatic && c.VerifiedCredentialType!.VerifiedCredentialTypeAssignedKind!.VerifiedCredentialTypeKindId == VerifiedCredentialTypeKindId.FRAMEWORK) || (approvalType.Value == CompanySsiDetailApprovalType.Manual && c.VerifiedCredentialType!.VerifiedCredentialTypeAssignedKind!.VerifiedCredentialTypeKindId != VerifiedCredentialTypeKindId.FRAMEWORK))); /// public IAsyncEnumerable GetOwnCredentialDetails(string bpnl) => _context.CompanySsiDetails.AsNoTracking() .Where(c => c.Bpnl == bpnl) .Select(c => new OwnedVerifiedCredentialData( + c.Id, c.VerifiedCredentialTypeId, c.CompanySsiDetailStatusId, c.ExpiryDate, diff --git a/src/database/SsiCredentialIssuer.DbAccess/Repositories/CredentialRepository.cs b/src/database/SsiCredentialIssuer.DbAccess/Repositories/CredentialRepository.cs index 7fca89d2..29b3dd16 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Repositories/CredentialRepository.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Repositories/CredentialRepository.cs @@ -26,22 +26,15 @@ namespace Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; -public class CredentialRepository : ICredentialRepository +public class CredentialRepository(IssuerDbContext dbContext) : ICredentialRepository { - private readonly IssuerDbContext _dbContext; - - public CredentialRepository(IssuerDbContext dbContext) - { - _dbContext = dbContext; - } - public Task GetWalletCredentialId(Guid credentialId) => - _dbContext.CompanySsiDetails.Where(x => x.Id == credentialId) + dbContext.CompanySsiDetails.Where(x => x.Id == credentialId) .Select(x => x.ExternalCredentialId) .SingleOrDefaultAsync(); public Task<(HolderWalletData HolderWalletData, string? Credential, EncryptionTransformationData EncryptionInformation, string? CallbackUrl)> GetCredentialData(Guid credentialId) => - _dbContext.CompanySsiDetails + dbContext.CompanySsiDetails .Where(x => x.Id == credentialId) .Select(x => new ValueTuple( new HolderWalletData(x.CompanySsiProcessData!.HolderWalletUrl, x.CompanySsiProcessData.ClientId), @@ -51,19 +44,19 @@ public CredentialRepository(IssuerDbContext dbContext) .SingleOrDefaultAsync(); public Task<(bool Exists, Guid CredentialId)> GetDataForProcessId(Guid processId) => - _dbContext.CompanySsiDetails + dbContext.CompanySsiDetails .Where(c => c.ProcessId == processId) .Select(c => new ValueTuple(true, c.Id)) .SingleOrDefaultAsync(); public Task<(VerifiedCredentialTypeKindId CredentialTypeKindId, JsonDocument Schema)> GetCredentialStorageInformationById(Guid credentialId) => - _dbContext.CompanySsiDetails + dbContext.CompanySsiDetails .Where(c => c.Id == credentialId) .Select(c => new ValueTuple(c.CompanySsiProcessData!.CredentialTypeKindId, c.CompanySsiProcessData.Schema)) .SingleOrDefaultAsync(); public Task<(Guid? ExternalCredentialId, VerifiedCredentialTypeKindId KindId, bool HasEncryptionInformation, string? CallbackUrl)> GetExternalCredentialAndKindId(Guid credentialId) => - _dbContext.CompanySsiDetails + dbContext.CompanySsiDetails .Where(c => c.Id == credentialId) .Select(c => new ValueTuple( c.ExternalCredentialId, @@ -73,13 +66,13 @@ public CredentialRepository(IssuerDbContext dbContext) .SingleOrDefaultAsync(); public Task<(string Bpn, string? CallbackUrl)> GetCallbackUrl(Guid credentialId) => - _dbContext.CompanySsiProcessData + dbContext.CompanySsiProcessData .Where(x => x.CompanySsiDetailId == credentialId) .Select(x => new ValueTuple(x.CompanySsiDetail!.Bpnl, x.CallbackUrl)) .SingleOrDefaultAsync(); public Task<(bool Exists, bool IsSameBpnl, Guid? ExternalCredentialId, CompanySsiDetailStatusId StatusId, IEnumerable<(Guid DocumentId, DocumentStatusId DocumentStatusId)> Documents)> GetRevocationDataById(Guid credentialId, string bpnl) => - _dbContext.CompanySsiDetails + dbContext.CompanySsiDetails .Where(x => x.Id == credentialId) .Select(x => new ValueTuple>( @@ -94,22 +87,34 @@ public void AttachAndModifyCredential(Guid credentialId, Action GetCredentialNotificationData(Guid credentialId) => - _dbContext.CompanySsiDetails + dbContext.CompanySsiDetails .Where(x => x.Id == credentialId) .Select(x => new ValueTuple(x.VerifiedCredentialTypeId, x.CreatorUserId)) .SingleOrDefaultAsync(); public Task<(bool Exists, bool IsSameCompany, IEnumerable<(DocumentStatusId StatusId, byte[] Content)> Documents)> GetSignedCredentialForCredentialId(Guid credentialId, string bpnl) => - _dbContext.CompanySsiDetails + dbContext.CompanySsiDetails .Where(x => x.Id == credentialId) .Select(x => new ValueTuple>>( true, x.Bpnl == bpnl, x.Documents.Where(d => d.DocumentTypeId == DocumentTypeId.VERIFIED_CREDENTIAL).Select(d => new ValueTuple(d.DocumentStatusId, d.DocumentContent)))) .SingleOrDefaultAsync(); + + public Task<(bool Exists, bool IsSameCompany, string FileName, DocumentStatusId StatusId, byte[] Content, MediaTypeId MediaTypeId)> GetDocumentById(Guid documentId, string bpnl) => + dbContext.Documents + .Where(x => x.Id == documentId) + .Select(x => new ValueTuple( + true, + x.CompanySsiDetails.Any(c => c.Bpnl == bpnl), + x.DocumentName, + x.DocumentStatusId, + x.DocumentContent, + x.MediaTypeId)) + .SingleOrDefaultAsync(); } diff --git a/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs b/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs index 8d6ee8a4..6d418935 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs @@ -85,8 +85,9 @@ public interface ICompanySsiDetailsRepository /// /// The status of the details /// OPTIONAL: The type of the credential that should be returned + /// OPTIONAL: The approval type of the credential /// Returns data to create the pagination - IQueryable GetAllCredentialDetails(CompanySsiDetailStatusId? companySsiDetailStatusId, VerifiedCredentialTypeId? credentialTypeId); + IQueryable GetAllCredentialDetails(CompanySsiDetailStatusId? companySsiDetailStatusId, VerifiedCredentialTypeId? credentialTypeId, CompanySsiDetailApprovalType? approvalType); /// /// Gets all credentials for a specific bpn diff --git a/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICredentialRepository.cs b/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICredentialRepository.cs index b1c0236e..4ea53609 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICredentialRepository.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICredentialRepository.cs @@ -36,4 +36,5 @@ public interface ICredentialRepository void AttachAndModifyCredential(Guid credentialId, Action? initialize, Action modify); Task<(VerifiedCredentialTypeId TypeId, string RequesterId)> 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/Enums/VerifiedCredentialExternalTypeId.cs b/src/database/SsiCredentialIssuer.Entities/Enums/VerifiedCredentialExternalTypeId.cs index a0e5fb13..e7ce16a1 100644 --- a/src/database/SsiCredentialIssuer.Entities/Enums/VerifiedCredentialExternalTypeId.cs +++ b/src/database/SsiCredentialIssuer.Entities/Enums/VerifiedCredentialExternalTypeId.cs @@ -47,7 +47,7 @@ public enum VerifiedCredentialExternalTypeId [EnumMember(Value = "DemandCapacityCredential")] DEMAND_AND_CAPACITY_MANAGEMENT = 8, - [EnumMember(Value = "DemandCapacityCredential")] + [EnumMember(Value = "PurisCredential")] DEMAND_AND_CAPACITY_MANAGEMENT_PURIS = 9, [EnumMember(Value = "BusinessPartnerCredential")] diff --git a/src/database/SsiCredentialIssuer.Entities/SsiCredentialIssuer.Entities.csproj b/src/database/SsiCredentialIssuer.Entities/SsiCredentialIssuer.Entities.csproj index 56ad10c5..335d58ae 100644 --- a/src/database/SsiCredentialIssuer.Entities/SsiCredentialIssuer.Entities.csproj +++ b/src/database/SsiCredentialIssuer.Entities/SsiCredentialIssuer.Entities.csproj @@ -32,7 +32,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/src/database/SsiCredentialIssuer.Migrations/Seeder/BatchInsertSeeder.cs b/src/database/SsiCredentialIssuer.Migrations/Seeder/BatchInsertSeeder.cs index dc841bbb..5204f3b5 100644 --- a/src/database/SsiCredentialIssuer.Migrations/Seeder/BatchInsertSeeder.cs +++ b/src/database/SsiCredentialIssuer.Migrations/Seeder/BatchInsertSeeder.cs @@ -63,6 +63,7 @@ public async Task ExecuteAsync(CancellationToken cancellationToken) _logger.LogInformation("Start BaseEntityBatch Seeder"); await SeedBaseEntity(cancellationToken); await SeedTable("company_ssi_process_datas", x => x.CompanySsiDetailId, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + await SeedTable("company_ssi_detail_assigned_documents", x => new { x.DocumentId, x.CompanySsiDetailId }, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); await SeedTable("verified_credential_type_assigned_kinds", x => new { x.VerifiedCredentialTypeId, x.VerifiedCredentialTypeKindId }, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); await SeedTable("verified_credential_type_assigned_use_cases", x => new { x.VerifiedCredentialTypeId, x.UseCaseId }, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); await SeedTable("verified_credential_type_assigned_external_types", x => new { x.VerifiedCredentialTypeId, x.VerifiedCredentialExternalTypeId }, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); diff --git a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/CredentialBusinessLogic.cs b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/CredentialBusinessLogic.cs index cc43ebae..7e991bdc 100644 --- a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/CredentialBusinessLogic.cs +++ b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/CredentialBusinessLogic.cs @@ -19,7 +19,9 @@ using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Extensions; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; +using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; using Org.Eclipse.TractusX.SsiCredentialIssuer.Service.ErrorHandling; using Org.Eclipse.TractusX.SsiCredentialIssuer.Service.Identity; using System.Text.Json; @@ -59,4 +61,25 @@ public async Task GetCredentialDocument(Guid credentialId) using var stream = new MemoryStream(credentialContent); return await JsonDocument.ParseAsync(stream).ConfigureAwait(ConfigureAwaitOptions.None); } + + public async Task<(string FileName, byte[] Content, string MediaType)> GetCredentialDocumentById(Guid documentId) + { + var (exists, isSameCompany, fileName, documentStatusId, content, mediaTypeId) = await _repositories.GetInstance().GetDocumentById(documentId, _identityData.Bpnl).ConfigureAwait(ConfigureAwaitOptions.None); + if (!exists) + { + throw NotFoundException.Create(CredentialErrors.DOCUMENT_NOT_FOUND, new[] { new ErrorParameter("documentId", documentId.ToString()) }); + } + + if (!isSameCompany) + { + throw ForbiddenException.Create(CredentialErrors.DOCUMENT_OTHER_COMPANY); + } + + if (documentStatusId == DocumentStatusId.INACTIVE) + { + throw ConflictException.Create(CredentialErrors.DOCUMENT_INACTIVE, new[] { new ErrorParameter("documentId", documentId.ToString()) }); + } + + return (fileName, content, mediaTypeId.MapToMediaType()); + } } diff --git a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/ICredentialBusinessLogic.cs b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/ICredentialBusinessLogic.cs index 5d937718..d4d368bd 100644 --- a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/ICredentialBusinessLogic.cs +++ b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/ICredentialBusinessLogic.cs @@ -24,4 +24,5 @@ namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Service.BusinessLogic; public interface ICredentialBusinessLogic { Task GetCredentialDocument(Guid credentialId); + Task<(string FileName, byte[] Content, string MediaType)> GetCredentialDocumentById(Guid documentId); } diff --git a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IIssuerBusinessLogic.cs b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IIssuerBusinessLogic.cs index 18420bd0..c430d257 100644 --- a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IIssuerBusinessLogic.cs +++ b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IIssuerBusinessLogic.cs @@ -30,7 +30,7 @@ public interface IIssuerBusinessLogic IAsyncEnumerable GetSsiCertificatesAsync(); - Task> GetCredentials(int page, int size, CompanySsiDetailStatusId? companySsiDetailStatusId, VerifiedCredentialTypeId? credentialTypeId, CompanySsiDetailSorting? sorting); + Task> GetCredentials(int page, int size, CompanySsiDetailStatusId? companySsiDetailStatusId, VerifiedCredentialTypeId? credentialTypeId, CompanySsiDetailApprovalType? approvalType, CompanySsiDetailSorting? sorting); IAsyncEnumerable GetCredentialsForBpn(); Task ApproveCredential(Guid credentialId, CancellationToken cancellationToken); diff --git a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs index 5475e60b..6ebf58c3 100644 --- a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs +++ b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs @@ -96,11 +96,11 @@ public IAsyncEnumerable GetSsiCertificatesAsync() .GetSsiCertificates(_identity.Bpnl, _dateTimeProvider.OffsetNow); /// - public Task> GetCredentials(int page, int size, CompanySsiDetailStatusId? companySsiDetailStatusId, VerifiedCredentialTypeId? credentialTypeId, CompanySsiDetailSorting? sorting) + public Task> GetCredentials(int page, int size, CompanySsiDetailStatusId? companySsiDetailStatusId, VerifiedCredentialTypeId? credentialTypeId, CompanySsiDetailApprovalType? approvalType, CompanySsiDetailSorting? sorting) { var query = _repositories .GetInstance() - .GetAllCredentialDetails(companySsiDetailStatusId, credentialTypeId); + .GetAllCredentialDetails(companySsiDetailStatusId, credentialTypeId, approvalType); var sortedQuery = sorting switch { CompanySsiDetailSorting.BpnlAsc or null => query.OrderBy(c => c.Bpnl), @@ -395,7 +395,7 @@ public async Task CreateFrameworkCredential(CreateFrameworkCredentialReque } var companyCredentialDetailsRepository = _repositories.GetInstance(); - var result = await companyCredentialDetailsRepository.CheckCredentialTypeIdExistsForExternalTypeDetailVersionId(requestData.UseCaseFrameworkVersionId, requestData.UseCaseFrameworkId, _identity.Bpnl).ConfigureAwait(ConfigureAwaitOptions.None); + var result = await companyCredentialDetailsRepository.CheckCredentialTypeIdExistsForExternalTypeDetailVersionId(requestData.UseCaseFrameworkVersionId, requestData.UseCaseFrameworkId, requestData.HolderBpn).ConfigureAwait(ConfigureAwaitOptions.None); if (!result.Exists) { throw ControllerArgumentException.Create(IssuerErrors.EXTERNAL_TYPE_DETAIL_NOT_FOUND, new ErrorParameter[] { new("verifiedCredentialExternalTypeDetailId", requestData.UseCaseFrameworkId.ToString()) }); diff --git a/src/issuer/SsiCredentialIssuer.Service/Controllers/CredentialController.cs b/src/issuer/SsiCredentialIssuer.Service/Controllers/CredentialController.cs index 2474365d..c1e34af0 100644 --- a/src/issuer/SsiCredentialIssuer.Service/Controllers/CredentialController.cs +++ b/src/issuer/SsiCredentialIssuer.Service/Controllers/CredentialController.cs @@ -45,6 +45,23 @@ public static RouteGroupBuilder MapCredentialApi(this RouteGroupBuilder group) .Produces(StatusCodes.Status200OK, typeof(JsonDocument), Constants.JsonContentType) .Produces(StatusCodes.Status409Conflict, typeof(ErrorResponse), Constants.JsonContentType); + issuer.MapGet("/documents/{documentId}", async ([FromRoute] Guid documentId, [FromServices] ICredentialBusinessLogic logic) => + { + var (fileName, content, mediaType) = await logic.GetCredentialDocumentById(documentId); + return Results.File(content, contentType: mediaType, fileDownloadName: fileName); + }) + .WithSwaggerDescription("The endpoint enables users to download the credential (full json) of their own company.", + "Example: GET: api/credential/documents/{documentId}") + .RequireAuthorization(r => + { + r.RequireRole("view_credential_requests"); + r.AddRequirements(new MandatoryIdentityClaimRequirement(PolicyTypeId.ValidIdentity)); + r.AddRequirements(new MandatoryIdentityClaimRequirement(PolicyTypeId.ValidBpn)); + }) + .WithDefaultResponses() + .Produces(StatusCodes.Status200OK, typeof(FileContentResult), Constants.JsonContentType) + .Produces(StatusCodes.Status409Conflict, typeof(ErrorResponse), Constants.JsonContentType); + return issuer; } } diff --git a/src/issuer/SsiCredentialIssuer.Service/Controllers/IssuerController.cs b/src/issuer/SsiCredentialIssuer.Service/Controllers/IssuerController.cs index 2e65a66a..49caa9fa 100644 --- a/src/issuer/SsiCredentialIssuer.Service/Controllers/IssuerController.cs +++ b/src/issuer/SsiCredentialIssuer.Service/Controllers/IssuerController.cs @@ -80,8 +80,9 @@ public static RouteGroupBuilder MapIssuerApi(this RouteGroupBuilder group) [FromQuery] int? size, [FromQuery] CompanySsiDetailStatusId? companySsiDetailStatusId, [FromQuery] VerifiedCredentialTypeId? credentialTypeId, + [FromQuery] CompanySsiDetailApprovalType? approvalType, [FromQuery] CompanySsiDetailSorting? sorting) => logic.GetCredentials(page ?? 0, size ?? 15, - companySsiDetailStatusId, credentialTypeId, sorting)) + companySsiDetailStatusId, credentialTypeId, approvalType, sorting)) .WithSwaggerDescription("Gets all outstanding, existing and inactive credentials", "Example: GET: /api/issuer", "The page to get", diff --git a/src/issuer/SsiCredentialIssuer.Service/Controllers/RevocationController.cs b/src/issuer/SsiCredentialIssuer.Service/Controllers/RevocationController.cs index 4bb091a4..7b01440e 100644 --- a/src/issuer/SsiCredentialIssuer.Service/Controllers/RevocationController.cs +++ b/src/issuer/SsiCredentialIssuer.Service/Controllers/RevocationController.cs @@ -41,17 +41,16 @@ public static RouteGroupBuilder MapRevocationApi(this RouteGroupBuilder group) "Id of the credential that should be revoked") .RequireAuthorization(r => { - // r.RequireRole("revoke_credentials_issuer"); + r.RequireRole("revoke_credentials_issuer"); r.AddRequirements(new MandatoryIdentityClaimRequirement(PolicyTypeId.ValidBpn)); r.AddRequirements(new MandatoryIdentityClaimRequirement(PolicyTypeId.ValidIdentity)); }) .WithDefaultResponses() .Produces(StatusCodes.Status200OK, typeof(Guid)); revocation.MapPost("credentials/{credentialId}", ([FromRoute] Guid credentialId, CancellationToken cancellationToken, [FromServices] IRevocationBusinessLogic logic) => logic.RevokeCredential(credentialId, false, cancellationToken)) - .WithSwaggerDescription("Revokes an credential of an holder", + .WithSwaggerDescription("Credential Revocation by holder", "POST: api/revocation/credentials/{credentialId}", "Id of the credential that should be revoked", - "The information for the holder wallet", "CancellationToken") .RequireAuthorization(r => { diff --git a/src/issuer/SsiCredentialIssuer.Service/ErrorHandling/CredentialErrorMessageContainer.cs b/src/issuer/SsiCredentialIssuer.Service/ErrorHandling/CredentialErrorMessageContainer.cs index ce4dcfe9..7f603ceb 100644 --- a/src/issuer/SsiCredentialIssuer.Service/ErrorHandling/CredentialErrorMessageContainer.cs +++ b/src/issuer/SsiCredentialIssuer.Service/ErrorHandling/CredentialErrorMessageContainer.cs @@ -59,7 +59,10 @@ public class CredentialErrorMessageContainer : IErrorMessageContainer { CredentialErrors.SCHEMA_NOT_FRAMEWORK, "The schema must be a framework credential" }, { CredentialErrors.CREDENTIAL_NOT_FOUND, "Credential {credentialId} does not exist" }, { CredentialErrors.COMPANY_NOT_ALLOWED, "Not allowed to display the credential" }, - { CredentialErrors.SIGNED_CREDENTIAL_NOT_FOUND, "There must be exactly one signed credential" } + { CredentialErrors.SIGNED_CREDENTIAL_NOT_FOUND, "There must be exactly one signed credential" }, + { CredentialErrors.DOCUMENT_NOT_FOUND, "Document {documentId} does not exist" }, + { CredentialErrors.DOCUMENT_INACTIVE, "Document {documentId} is inactive" }, + { CredentialErrors.DOCUMENT_OTHER_COMPANY, "Not allowed to access document of another company" } }.ToImmutableDictionary(x => (int)x.Key, x => x.Value); public Type Type { get => typeof(CredentialErrors); } @@ -100,5 +103,8 @@ public enum CredentialErrors SCHEMA_NOT_FRAMEWORK, CREDENTIAL_NOT_FOUND, COMPANY_NOT_ALLOWED, - SIGNED_CREDENTIAL_NOT_FOUND + SIGNED_CREDENTIAL_NOT_FOUND, + DOCUMENT_NOT_FOUND, + DOCUMENT_INACTIVE, + DOCUMENT_OTHER_COMPANY } diff --git a/src/issuer/SsiCredentialIssuer.Service/Identity/MandatoryIdentityClaimHandler.cs b/src/issuer/SsiCredentialIssuer.Service/Identity/MandatoryIdentityClaimHandler.cs index 2bc6efdc..cdf9569b 100644 --- a/src/issuer/SsiCredentialIssuer.Service/Identity/MandatoryIdentityClaimHandler.cs +++ b/src/issuer/SsiCredentialIssuer.Service/Identity/MandatoryIdentityClaimHandler.cs @@ -92,11 +92,12 @@ private void InitializeClaims(ClaimsPrincipal principal) } _identityDataBuilder.AddIdentityId(preferredUserName ?? clientId!); - bool isCompanyUser; - if (isCompanyUser = Guid.TryParse(preferredUserName, out var companyUserId)) + var isCompanyUser = Guid.TryParse(preferredUserName, out var companyUserId); + if (isCompanyUser) { _identityDataBuilder.AddCompanyUserId(companyUserId); } + _identityDataBuilder.AddIsServiceAccount(!isCompanyUser); _identityDataBuilder.Status = IClaimsIdentityDataBuilderStatus.Complete; } diff --git a/tests/database/SsiCredentialIssuer.DbAccess.Tests/CompanySsiDetailsRepositoryTests.cs b/tests/database/SsiCredentialIssuer.DbAccess.Tests/CompanySsiDetailsRepositoryTests.cs index 4e07b019..5b57223f 100644 --- a/tests/database/SsiCredentialIssuer.DbAccess.Tests/CompanySsiDetailsRepositoryTests.cs +++ b/tests/database/SsiCredentialIssuer.DbAccess.Tests/CompanySsiDetailsRepositoryTests.cs @@ -113,7 +113,7 @@ public async Task GetAllCredentialDetails_WithValidData_ReturnsExpected() var sut = await CreateSut(); // Act - var result = await sut.GetAllCredentialDetails(null, null).ToListAsync(); + var result = await sut.GetAllCredentialDetails(null, null, null).ToListAsync(); // Assert result.Should().NotBeNull(); @@ -138,7 +138,7 @@ public async Task GetAllCredentialDetails_WithWithStatusId_ReturnsExpected() var sut = await CreateSut(); // Act - var result = await sut.GetAllCredentialDetails(CompanySsiDetailStatusId.PENDING, null).ToListAsync(); + var result = await sut.GetAllCredentialDetails(CompanySsiDetailStatusId.PENDING, null, null).ToListAsync(); // Assert result.Should().NotBeNull().And.HaveCount(4); @@ -159,7 +159,7 @@ public async Task GetAllCredentialDetails_WithWithCredentialType_ReturnsExpected var sut = await CreateSut(); // Act - var result = await sut.GetAllCredentialDetails(null, VerifiedCredentialTypeId.PCF_FRAMEWORK).ToListAsync(); + var result = await sut.GetAllCredentialDetails(null, VerifiedCredentialTypeId.PCF_FRAMEWORK, null).ToListAsync(); // Assert result.Should().NotBeNull().And.ContainSingle().Which.Bpnl.Should().Be(ValidBpnl); diff --git a/tests/database/SsiCredentialIssuer.DbAccess.Tests/CredentialRepositoryTests.cs b/tests/database/SsiCredentialIssuer.DbAccess.Tests/CredentialRepositoryTests.cs index 6d80e5a4..5e377452 100644 --- a/tests/database/SsiCredentialIssuer.DbAccess.Tests/CredentialRepositoryTests.cs +++ b/tests/database/SsiCredentialIssuer.DbAccess.Tests/CredentialRepositoryTests.cs @@ -138,6 +138,71 @@ public async Task GetExternalCredentialAndKindId_ReturnsExpectedDocument() #endregion + #region GetCallbackUrl + + [Fact] + public async Task GetCallbackUrl_ReturnsExpectedDocument() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetCallbackUrl(new Guid("9f5b9934-4014-4099-91e9-7b1aee696b03")); + + // Assert + result.Bpn.Should().Be("BPNL00000003AYRE"); + result.CallbackUrl.Should().Be("https://example.org/callback"); + } + + #endregion + + #region GetRevocationDataById + + [Fact] + public async Task GetRevocationDataById_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetRevocationDataById(new Guid("9f5b9934-4014-4099-91e9-7b1aee696b03"), "BPNL00000003AYRE"); + + // Assert + result.Exists.Should().BeTrue(); + result.IsSameBpnl.Should().BeTrue(); + result.StatusId.Should().Be(CompanySsiDetailStatusId.PENDING); + result.ExternalCredentialId.Should().Be(new Guid("bd474c60-e7ce-450f-bdf4-73604546fc5e")); + } + + [Fact] + public async Task GetRevocationDataById_WithoutExisting_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetRevocationDataById(Guid.NewGuid(), "BPNL00000003AYRE"); + + // Assert + result.Exists.Should().BeFalse(); + } + + [Fact] + public async Task GetRevocationDataById_WithNotMatchingBpnl_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetRevocationDataById(new Guid("9f5b9934-4014-4099-91e9-7b1aee696b03"), "BPNL000000WRONG"); + + // Assert + result.Exists.Should().BeTrue(); + result.IsSameBpnl.Should().BeFalse(); + } + + #endregion + #region AttachAndModifyCredential [Fact] @@ -161,6 +226,54 @@ public async Task AttachAndModifyCredential_ReturnsExpectedResult() #endregion + #region GetDocumentById + + [Fact] + public async Task GetDocumentById_ReturnsExpectedDocument() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetDocumentById(new Guid("e020787d-1e04-4c0b-9c06-bd1cd44724b1"), "BPNL00000003AYRE"); + + // Assert + result.Exists.Should().BeTrue(); + result.IsSameCompany.Should().BeTrue(); + result.MediaTypeId.Should().Be(MediaTypeId.PNG); + } + + [Fact] + public async Task GetDocumentById_WithWrongBpn_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetDocumentById(new Guid("e020787d-1e04-4c0b-9c06-bd1cd44724b1"), "BPNL0000000WRONG"); + + // Assert + result.Exists.Should().BeTrue(); + result.IsSameCompany.Should().BeFalse(); + result.MediaTypeId.Should().Be(MediaTypeId.PNG); + } + + [Fact] + public async Task GetDocumentById_WithNotExisting_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetDocumentById(Guid.NewGuid(), "BPNL00000003AYRE"); + + // Assert + result.Exists.Should().BeFalse(); + result.IsSameCompany.Should().BeFalse(); + } + + #endregion + #region Setup private async Task CreateSut() diff --git a/tests/database/SsiCredentialIssuer.DbAccess.Tests/Seeder/Data/company_ssi_detail_assigned_documents.test.json b/tests/database/SsiCredentialIssuer.DbAccess.Tests/Seeder/Data/company_ssi_detail_assigned_documents.test.json new file mode 100644 index 00000000..37abc42f --- /dev/null +++ b/tests/database/SsiCredentialIssuer.DbAccess.Tests/Seeder/Data/company_ssi_detail_assigned_documents.test.json @@ -0,0 +1,6 @@ +[ + { + "document_id": "e020787d-1e04-4c0b-9c06-bd1cd44724b1", + "company_ssi_detail_id": "9f5b9934-4014-4099-91e9-7b1aee696b03" + } +] diff --git a/tests/database/SsiCredentialIssuer.DbAccess.Tests/Seeder/Data/company_ssi_process_datas.test.json b/tests/database/SsiCredentialIssuer.DbAccess.Tests/Seeder/Data/company_ssi_process_datas.test.json index 6921b95b..9f32b87e 100644 --- a/tests/database/SsiCredentialIssuer.DbAccess.Tests/Seeder/Data/company_ssi_process_datas.test.json +++ b/tests/database/SsiCredentialIssuer.DbAccess.Tests/Seeder/Data/company_ssi_process_datas.test.json @@ -7,6 +7,7 @@ "credential_type_kind_id": 1, "encryption_mode": 1, "holder_wallet_url": "https://example.org/wallet", - "client_id": "c123" + "client_id": "c123", + "callback_url": "https://example.org/callback" } ] \ No newline at end of file diff --git a/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/CredentialBusinessLogicTests.cs b/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/CredentialBusinessLogicTests.cs index f140e1a9..e79bcb54 100644 --- a/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/CredentialBusinessLogicTests.cs +++ b/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/CredentialBusinessLogicTests.cs @@ -19,6 +19,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Extensions; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Enums; using Org.Eclipse.TractusX.SsiCredentialIssuer.Service.BusinessLogic; @@ -26,6 +27,7 @@ using Org.Eclipse.TractusX.SsiCredentialIssuer.Service.Identity; using System.Text; using System.Text.Json; +using System.Text.Unicode; namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Service.Tests.BusinessLogic; @@ -33,6 +35,7 @@ public class CredentialBusinessLogicTests { private static readonly string Bpnl = "BPNL00000001TEST"; private readonly Guid CredentialId = Guid.NewGuid(); + private readonly Guid DocumentId = Guid.NewGuid(); private readonly IFixture _fixture; @@ -64,6 +67,8 @@ public CredentialBusinessLogicTests() _sut = new CredentialBusinessLogic(_issuerRepositories, _identityService); } + #region GetCredentialDocument + [Fact] public async Task GetCredentialDocument_WithNotExisting_ThrowsNotFoundException() { @@ -124,4 +129,72 @@ public async Task GetCredentialDocument_WithValid_ReturnsExpected() // Assert doc.RootElement.GetRawText().Should().Be("{\"test\":\"test\"}"); } + + #endregion + + #region GetCredentialDocumentById + + [Fact] + public async Task GetCredentialDocumentById_WithNotExisting_ThrowsNotFoundException() + { + // Arrange + A.CallTo(() => _credentialRepository.GetDocumentById(DocumentId, Bpnl)) + .Returns(default((bool, bool, string, DocumentStatusId, byte[], MediaTypeId))); + async Task Act() => await _sut.GetCredentialDocumentById(DocumentId); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be(CredentialErrors.DOCUMENT_NOT_FOUND.ToString()); + } + + [Fact] + public async Task GetCredentialDocumentById_WithDifferentCompany_ThrowsForbiddenException() + { + // Arrange + A.CallTo(() => _credentialRepository.GetDocumentById(DocumentId, Bpnl)) + .Returns((true, false, string.Empty, DocumentStatusId.ACTIVE, null!, MediaTypeId.JSON)); + async Task Act() => await _sut.GetCredentialDocumentById(DocumentId); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be(CredentialErrors.DOCUMENT_OTHER_COMPANY.ToString()); + } + + [Fact] + public async Task GetCredentialDocumentById_WithoutInactiveDocument_ThrowsConflictException() + { + // Arrange + A.CallTo(() => _credentialRepository.GetDocumentById(DocumentId, Bpnl)) + .Returns((true, true, string.Empty, DocumentStatusId.INACTIVE, null!, MediaTypeId.JSON)); + async Task Act() => await _sut.GetCredentialDocumentById(DocumentId); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be(CredentialErrors.DOCUMENT_INACTIVE.ToString()); + } + + [Fact] + public async Task GetCredentialDocumentById_WithValid_ReturnsExpected() + { + // Arrange + var json = JsonDocument.Parse("{\"test\":\"test\"}"); + var schema = JsonSerializer.Serialize(json, JsonSerializerOptions.Default); + A.CallTo(() => _credentialRepository.GetDocumentById(DocumentId, Bpnl)) + .Returns((true, true, "test.json", DocumentStatusId.ACTIVE, Encoding.UTF8.GetBytes(schema), MediaTypeId.JSON)); + + // Act + var doc = await _sut.GetCredentialDocumentById(DocumentId); + + // Assert + doc.MediaType.Should().Be(MediaTypeId.JSON.MapToMediaType()); + doc.FileName.Should().Be("test.json"); + } + + #endregion } diff --git a/tests/issuer/SsiCredentialIssuer.Service.Tests/Controllers/IssuerControllerTests.cs b/tests/issuer/SsiCredentialIssuer.Service.Tests/Controllers/IssuerControllerTests.cs index 6ddd8436..ffdcc5b1 100644 --- a/tests/issuer/SsiCredentialIssuer.Service.Tests/Controllers/IssuerControllerTests.cs +++ b/tests/issuer/SsiCredentialIssuer.Service.Tests/Controllers/IssuerControllerTests.cs @@ -26,7 +26,7 @@ namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Service.Tests.Controllers; -public class IssuerControllerTests : IClassFixture +public class IssuerControllerTests(IntegrationTestFactory factory) : IClassFixture { private static readonly JsonSerializerOptions JsonOptions = new() { @@ -36,12 +36,7 @@ public class IssuerControllerTests : IClassFixture }; private const string BaseUrl = "/api/issuer"; - private readonly HttpClient _client; - - public IssuerControllerTests(IntegrationTestFactory factory) - { - _client = factory.CreateClient(); - } + private readonly HttpClient _client = factory.CreateClient(); #region GetCertificateTypes diff --git a/tests/issuer/SsiCredentialIssuer.Service.Tests/appsettings.IntegrationTests.json b/tests/issuer/SsiCredentialIssuer.Service.Tests/appsettings.IntegrationTests.json index 845532d3..78b30caa 100644 --- a/tests/issuer/SsiCredentialIssuer.Service.Tests/appsettings.IntegrationTests.json +++ b/tests/issuer/SsiCredentialIssuer.Service.Tests/appsettings.IntegrationTests.json @@ -5,7 +5,7 @@ "AllowedOrigins": [] }, "ConnectionStrings": { - "PolicyHubDb": "Server=placeholder;Database=placeholder;Port=5432;User Id=placeholder;Password=;Ssl Mode=Disable;" + "IssuerDb": "Server=placeholder;Database=placeholder;Port=5432;User Id=placeholder;Password=;Ssl Mode=Disable;" }, "JwtBearerOptions": { "RequireHttpsMetadata": true,