diff --git a/.dockerignore b/.dockerignore index 9e26bea92..fd7a10b20 100644 --- a/.dockerignore +++ b/.dockerignore @@ -18,4 +18,4 @@ # ********************************************************************************/ **/.git -build/test-results +miw/build/test-results diff --git a/.github/workflows/app-test-coverage-pr.yml b/.github/workflows/app-test-coverage-pr.yml index f0e56fb9f..193358ebe 100644 --- a/.github/workflows/app-test-coverage-pr.yml +++ b/.github/workflows/app-test-coverage-pr.yml @@ -35,11 +35,14 @@ jobs: fetch-depth: 0 - name: Set up Java 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' + - name: Validate Gradle wrapper + uses: gradle/wrapper-validation-action@v2 + - name: Test app run: ./gradlew test @@ -48,18 +51,18 @@ jobs: run: ./gradlew jacocoTestReport - name: Upload test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: test-results - path: ${{ github.workspace }}/build/ + path: ${{ github.workspace }}/miw/build/ - name: Publish code coverage report as PR comment uses: madrapps/jacoco-report@v1.6.1 if: always() with: paths: | - ${{ github.workspace }}/build/reports/xml/jacoco.xml + ${{ github.workspace }}/miw/build/reports/xml/jacoco.xml token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: 80 min-coverage-changed-files: 80 diff --git a/.github/workflows/app-test-coverage-repo.yml b/.github/workflows/app-test-coverage-repo.yml index c52a486f0..b9ea4a2d0 100644 --- a/.github/workflows/app-test-coverage-repo.yml +++ b/.github/workflows/app-test-coverage-repo.yml @@ -36,7 +36,7 @@ jobs: steps: - name: Download test results - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: test-results path: ${{ github.workspace }}/ diff --git a/.github/workflows/chart-verification.yml b/.github/workflows/chart-verification.yml index a75cec444..47bafb3ad 100644 --- a/.github/workflows/chart-verification.yml +++ b/.github/workflows/chart-verification.yml @@ -51,7 +51,7 @@ jobs: fetch-depth: 0 - name: Set up Helm - uses: azure/setup-helm@v3 + uses: azure/setup-helm@v4.1.0 with: version: v3.12.3 @@ -86,8 +86,8 @@ jobs: uses: addnab/docker-run-action@v3 with: image: jnorwood/helm-docs:v1.11.3 - options: -v ${{ github.workspace }}/charts:/helm-docs - run: helm-docs -i managed-identity-wallet/.helmdocsignore + options: -v ${{ github.workspace }}/charts:/helm-docs -v ${{ github.workspace }}/.helmdocsignore:/helm-docs/.helmdocsignore + run: helm-docs - name: Verify that no changes are required run: | @@ -108,14 +108,14 @@ jobs: fetch-depth: 0 - name: Set up Helm - uses: azure/setup-helm@v3 + uses: azure/setup-helm@v4.1.0 with: version: v3.12.3 - name: Set up Taskfile uses: arduino/setup-task@v1 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' @@ -151,7 +151,7 @@ jobs: version: v0.20.0 - name: Build image - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . push: true diff --git a/.github/workflows/dast-scan.yaml b/.github/workflows/dast-scan.yaml index d94c66d8b..243641724 100644 --- a/.github/workflows/dast-scan.yaml +++ b/.github/workflows/dast-scan.yaml @@ -38,14 +38,14 @@ jobs: fetch-depth: 0 - name: Set up Helm - uses: azure/setup-helm@v3 + uses: azure/setup-helm@v4.1.0 with: version: v3.12.3 - name: Set up Taskfile uses: arduino/setup-task@v1 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' @@ -72,7 +72,7 @@ jobs: version: v0.20.0 - name: Build image - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . push: true @@ -120,15 +120,10 @@ jobs: echo "... done." - - name: Add Summary - if: success() || failure() - run: | - echo "Publishing Job summary... " - cat report_md.md >> $GITHUB_STEP_SUMMARY - - name: Upload HTML report if: success() || failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ZAP scan report path: ./report_html.html + retention-days: 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e25c95c0..1751418f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,13 +48,14 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Validate Gradle wrapper + uses: gradle/wrapper-validation-action@v2 + - name: Setup Helm - uses: azure/setup-helm@v3 - with: - token: ${{ secrets.GITHUB_TOKEN }} + uses: azure/setup-helm@v4.1.0 - name: Setup JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' @@ -94,15 +95,15 @@ jobs: run: ./gradlew build - name: Upload build artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: build - path: ./build + path: ./miw/build if-no-files-found: error retention-days: 1 - name: Upload Helm chart artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: charts path: ./charts @@ -114,6 +115,15 @@ jobs: echo "::notice::${{ env.next_release }}" echo "::notice::${{ env.will_create_new_release }}" + - name: Upload jar to GitHub release + if: github.event_name != 'pull_request' && steps.semantic-release.outputs.will_create_new_release == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_VERSION: ${{ steps.semantic-release.outputs.next_release }} + run: | + echo "::notice::Uploading jar to GitHub release" + gh release upload "v$RELEASE_VERSION" ./miw/build/libs/miw-latest.jar + docker: name: Docker Release needs: semantic_release @@ -122,13 +132,13 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Download build artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: build - path: ./build + path: ./miw/build - name: Download Helm chart artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: charts path: ./charts @@ -136,7 +146,7 @@ jobs: # Create SemVer or ref tags dependent of trigger event - name: Docker meta id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: | ${{ env.IMAGE_NAMESPACE }}/${{ env.IMAGE_NAME }} @@ -152,14 +162,14 @@ jobs: - name: DockerHub login if: github.event_name != 'pull_request' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: # Use existing DockerHub credentials present as secrets username: ${{ secrets.DOCKER_HUB_USER }} password: ${{ secrets.DOCKER_HUB_TOKEN }} - name: Push image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . push: ${{ github.event_name != 'pull_request' }} @@ -189,15 +199,13 @@ jobs: uses: actions/checkout@v4 - name: Download Helm chart artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: charts path: ./charts - name: Install Helm - uses: azure/setup-helm@v3 - with: - token: ${{ secrets.GITHUB_TOKEN }} + uses: azure/setup-helm@v4.1.0 - name: Add Helm dependency repositories run: | diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index c6430dc0c..5194e52ef 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -35,6 +35,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Validate Gradle wrapper + uses: gradle/wrapper-validation-action@v2 + - uses: madhead/read-java-properties@latest id: version with: @@ -48,20 +51,20 @@ jobs: - name: Set outputs id: vars run: | - echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" + echo "{sha_short}=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV - name: Show version run: echo ${APP_VERSION}.${SHORT_SHA} - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' - name: Build with Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@2.4.2 with: arguments: build @@ -71,6 +74,6 @@ jobs: with: appname: 'project-managed-identity-wallet' createprofile: false - filepath: 'build/libs/miw-latest.jar' # add filepath for upload + filepath: 'miw/build/libs/miw-latest.jar' # add filepath for upload vid: '${{ secrets.ORG_VERACODE_API_ID }}' # reference to API ID, which is set as github org. secret vkey: '${{ secrets.ORG_VERACODE_API_KEY }}' #reference to API Key in github, which is set as github or. secret diff --git a/charts/managed-identity-wallet/.helmdocsignore b/.helmdocsignore similarity index 100% rename from charts/managed-identity-wallet/.helmdocsignore rename to .helmdocsignore diff --git a/.releaserc b/.releaserc index e2a2ce0f0..d1e82e0b8 100644 --- a/.releaserc +++ b/.releaserc @@ -41,7 +41,7 @@ [ "@semantic-release/exec", { - "prepareCmd": "cd charts/managed-identity-wallet && helm-docs -i .helmdocsignore ." + "prepareCmd": "cd charts/managed-identity-wallet && helm-docs ." } ], "@semantic-release/release-notes-generator", @@ -56,7 +56,7 @@ { "assets": [ "CHANGELOG.md", - "DEPENDENCIES", + "miw/DEPENDENCIES", "gradle.properties", "./charts/managed-identity-wallet/Chart.yaml", "./charts/managed-identity-wallet/README.md" diff --git a/CHANGELOG.md b/CHANGELOG.md index c0ea6c80d..4cd2c0d6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,253 @@ +# [0.5.0-develop.20](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.19...v0.5.0-develop.20) (2024-06-13) + + +### Bug Fixes + +* failing test cases ([bf71a1d](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/bf71a1dc9d89334bfbbe8c189b331d4841ee37d2)) +* formatting issue with api json file ([b10fa38](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/b10fa38eeae221b48adc6ebdace8ffdf4781bb09)) +* test cases of get VC api ([1430881](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/14308815e8e8a5420d02bb73ffca2a5386e8eee6)) +* VP test cases ([847f123](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/847f123f0e3dc4679fd0a22ff287d774c894e8f8)) +* VP validation tests ([09d337f](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/09d337f85f70e1c6514dc111757264330dc2a3ac)) + +# [0.5.0-develop.19](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.18...v0.5.0-develop.19) (2024-06-12) + + +### Bug Fixes + +* .helmdocsignore is not at project root ([a6ceba4](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/a6ceba41ac9a456f6016ad16afbf423b17126c4a)) +* new build location for upload action ([b6a7656](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/b6a76561af20ee675debe9564bf6e5e203fdb705)) +* update helm-docs test action ([99a1e91](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/99a1e91f05010b1dbd5d0b5c734ef703d8b1ef7f)) + + +### Features + +* move src/ to a dedicated sub-project ([45ddd7b](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/45ddd7b4a63120801eb638bd64e72821c309a336)) +* save dash.jar in the root project for easy re-use ([98747c8](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/98747c8357e769259c9ef44b80fe5c469a0d82d0)) + +# [0.5.0-develop.18](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.17...v0.5.0-develop.18) (2024-06-03) + + +### Bug Fixes + +* chart file ([8b16023](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/8b16023dd1f73a5e8f3f83117ff8b461cbbff4f5)) +* chart file ([099fabb](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/099fabbbfd0f14dfdd93307895440d4c0dd48abd)) +* direct access to WalletKeyService while signing VC removed ([e264c0f](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/e264c0f08de30fa5f3064f9a8d67c121419d75ad)) +* ficense header updated ([b2959e6](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/b2959e69e26346ff94a8dcbb47d60d41dde1c72f)) +* javadoc, imports and throws ([af87ecb](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/af87ecb3a79fa49577af0aa0da31d22366e48791)) +* sonar issues ([a7fb417](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/a7fb417e694c6199618e1e51e62c53b33c173089)) +* test cases ([22e3bb8](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/22e3bb831e8cc83fd947a6aef6b687f438cc7e5e)) + +# [0.5.0-develop.17](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.16...v0.5.0-develop.17) (2024-05-29) + + + +### Bug Fixes + +* api doc folder structure ([ebd691a](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/ebd691a8a5c05f26a6aa10b778d4c8be6189a4af)) + +# [0.5.0-develop.16](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.15...v0.5.0-develop.16) (2024-05-28) + + +### Bug Fixes + +* adjust api docs for 'api/token' endpoint ([7190ed1](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/7190ed17880dcd6aeba46a79211c5b6dba1eae08)) +* fix openapi_v001.json ([068462b](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/068462b992136aedeeaace966910bbcefc10aad3)) +* fix openapi_v001.json ([c30134e](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/c30134e10c4d2aa8ec91afc8ba2d1c1de9eca87b)) +* fix openapi_v001.json ([1910e88](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/1910e886dd5aace37e55d00858559c51d5ba7659)) + +# [0.5.0-develop.15](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.14...v0.5.0-develop.15) (2024-05-28) + + +### Bug Fixes + +* add asJwt as query param and fix exceptions ([8cbb756](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/8cbb756c48f07a90fcdd9e49244e04dae597d03a)) +* add asJwt as query param and fix exceptions ([ef961a5](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/ef961a54a24b30b4db18203532c4db8a3916c480)) +* add asJwt to controllers and services ([050358e](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/050358ef1aad0094edadcd678c18ad6a738e4010)) +* add asJwt to controllers and services ([b604f3d](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/b604f3d6f6734ab89df43c1b21bb7221b5b09eb0)) +* add copyright to getCredentialsCommand ([2154b7f](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/2154b7fd6c61324e1895a26522eb3632686c6b28)) +* add exceptions and fix naming ([dba48e2](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/dba48e2c9cda6e86500d12be7511ec4d1a48b5ac)) +* add exceptions and fix naming ([531d3f7](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/531d3f79e046ad9ff04387299bf70c40b0396560)) +* add keyId to createPresentation ([069a7d3](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/069a7d3531f6220239d2a0b42870608a91e55f8d)) +* add test and validation ([b3db3e0](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/b3db3e0ce58e028dd42cad9d2072d794dd735413)) +* add test and validation ([842e437](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/842e4375ac68567fddcfa648d75d1e98eaa0e0d4)) +* add wiremock ([4aec527](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/4aec527b0994be7af5acf6544f74776aa0d9bff7)) +* api docs ([8e7b796](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/8e7b7961faeabd274598adfe346fba62f53f98c2)) +* api docs ([760ee94](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/760ee94dbd79fa019a051c9c915fa0a7a8b0968c)) +* change context url ([fa9ee4b](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/fa9ee4bfd9f0aa43c5275fee177603765b67c38b)) +* copyright updated ([9ade90e](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/9ade90e21e2d705a5bedb1bb04a0c8f07169c7c9)) +* copyright updated ([956b6af](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/956b6afb56935e75940cd59d30c8b639c1ec2aa4)) +* current tests ([bb0b30c](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/bb0b30c672982cfc5aab5abb5389f4e40d1a6bca)) +* did resolver ([d0522f4](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/d0522f4dc7160a8617abad26ee47b6d60aeb7644)) +* exception names ([30a60d5](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/30a60d5b0b965b1d75586640d0c606ccab82795a)) +* exception names ([61832ed](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/61832ed39414d07a282f1d4c504cbeb262cb0e81)) +* exceptions ([fc6c077](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/fc6c07759d3f5adc4cd9c8be8684915a0a19f038)) +* exceptions ([1b260fa](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/1b260fa4732580d51416f667047fdf036090c07a)) +* formatting ([e00620d](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/e00620d3851853eb846d1578564ec72897b847b3)) +* formatting ([697ff85](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/697ff85f6cd682d5beac7a75288dce43525b5e50)) +* get vc as jwt with tests ([4216e0d](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/4216e0d48f409366bf7fe49111c3a3083e983130)) +* part of tests ([50ba388](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/50ba3883ca2e3f54b34ac0cfb7119e2f58f030f2)) +* part of tests ([7f01263](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/7f012635c338cb517231a87c8b91af563a035964)) +* remove new line ([31819c8](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/31819c861bcc40f9fa0aa32179ebf1b0dd12a2c1)) +* remove new line ([0c5f111](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/0c5f11197919b99c2935af074421fb84261c9f93)) +* remove try catch ([874e850](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/874e85064fd298b0ff6eaaca90f9a28706e894b0)) +* remove try catch ([b840c77](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/b840c771a8f8a94dd83a4d03894d5c5b9d08b058)) +* revert IssueDismantlerCredentialReqeust.java ([bac5200](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/bac5200b5c60577dbeb60c2f359df50a279ec3b9)) +* revert IssueDismantlerCredentialReqeust.java ([14f6195](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/14f6195f980059dade91338e34b638cbdb656142)) +* test for jwt ([0a69f1c](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/0a69f1c6eccfd71ffb16893a4b6c1a91903888db)) +* test for jwt ([8b5180a](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/8b5180a1956e89dc64228b93ebe1d69ed718ab88)) +* update code with the new ssi lib main ([9497d3e](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/9497d3e27c218800c3bbb06118fe992651a6baf2)) +* update code with the new ssi lib main ([3da1eff](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/3da1effc24769751d908762d8edc7d1fa2947ef1)) +* update ssi agent lib version ([02b1f5a](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/02b1f5a9e4a1c027d54778272def5c167d1b40e6)) + +# [0.5.0-develop.14](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.13...v0.5.0-develop.14) (2024-05-02) + + +### Bug Fixes + +* STS Token API Response ([78b7dc3](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/78b7dc33d6ddf9fbe5d0aeccf6e540fc3110e1d5)) +* Test Cases ([8eda499](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/8eda499476604ee14001811df3c01297bd3e7f7a)) + +# [0.5.0-develop.13](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.12...v0.5.0-develop.13) (2024-04-18) + + +### Bug Fixes + +* fix object name ([59ccdb2](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/59ccdb2e1b15633d7de442691d851a6a7d134025)) + + +### Features + +* added body type to /token endpoint ([6a67c92](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/6a67c9266170d77d5161ea38f6e9a8fc76a213ba)) + +# [0.5.0-develop.12](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.11...v0.5.0-develop.12) (2024-03-25) + + +### Bug Fixes + +* fix did creation in did document ([c21f7af](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/c21f7af470a6db484a63e49df222a1b7132a0a1e)) + + +### Features + +* adapt request and wallet service ([c911d33](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/c911d332579f669f347dcec3399b430e685baaf0)) + +# [0.5.0-develop.11](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.10...v0.5.0-develop.11) (2024-03-21) + + +### Bug Fixes + +* add "y" parameter and remove unused imports ([72b592c](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/72b592c0e09d1baef25ffd607a449ce8a9ca6589)) +* add WalletKey for ES256K algorithm to DidDocument ([9eb3294](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/9eb3294a9016befe7bcc469552a9266829bcba4b)) +* fix ES256K signature ([b33f8a6](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/b33f8a621e7c6429e1c89285b0eefa942e9e8828)) +* fix kid parameter ([97437b4](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/97437b4ef8c1ca07fd16fdf3321e61b1fcab2db0)) + + +### Features + +* add extra transaction ([b694d19](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/b694d19b591df8b460ba45351e7936ef24b84331)) +* support new algorithm (WIP) ([9dd6f27](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/9dd6f27f33311fc4e4467a412a4ee77eff617e18)) + +# [0.5.0-develop.10](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.9...v0.5.0-develop.10) (2024-03-08) + + +### Features + +* upload miw jar to gh release ([a7d6aa6](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/a7d6aa66b3468e4975711c72f1cfdbf3055dcd20)) + +# [0.5.0-develop.9](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.8...v0.5.0-develop.9) (2024-03-08) + + +### Features + +* add logging of auth failures ([ec60117](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/ec60117e68e7e139afb1f1f24179237bae03762c)) + +# [0.5.0-develop.8](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.7...v0.5.0-develop.8) (2024-03-08) + + +### Bug Fixes + +* possible caching vulnerability in action ([eb2e38f](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/eb2e38f6c6650491fcc81b70d1cf6f6fc5a0886f)) + +# [0.5.0-develop.7](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.6...v0.5.0-develop.7) (2024-03-07) + + +### Bug Fixes + +* fix bug with token re-wrapping ([e047550](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/e04755016aecdf6d6ade0eebae065f36ce80d429)) + + +### Features + +* add logic regarding jti info store and check ([6809578](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/6809578d208bc0380fa8a34bf119227e12ea1ecd)) + +# [0.5.0-develop.6](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.5...v0.5.0-develop.6) (2024-03-01) + + +### Features + +* add api docs, option asJwt ([9dc628b](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/9dc628b3ff4812759e9762f984f0406073191e40)) +* add ignoring version ([1785080](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/1785080a7f3585ecf6fcb89cd94d91701c4906b9)) +* add service method, controller, config for scope matching ([4feebd4](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/4feebd40dabe45e94f73cafcec410c2e51016758)) +* improve filter, add exception handling ([579a5ec](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/579a5ec547d486f04fca4336545cd9f5bb0dc216)) + +# [0.5.0-develop.5](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.4...v0.5.0-develop.5) (2024-02-27) + + +### Bug Fixes + +* fix integration test ([d8084ee](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/d8084eee8a4b562ce1242af1689791963b277783)) + + +### Features + +* add JWT verification and extend tests ([d83ce91](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/d83ce91b2e04b131ef3a83983bbd79331be6cac9)) +* add JWT verification and extend tests ([5ae223d](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/5ae223d61f23e71bf426d36ea3255f508abd254b)) +* adding draft for integration test, refactoring ([ce75056](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/ce75056a475ce39970de998376a91dcdc95e4065)) +* create initial class ([061faa7](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/061faa72591550d3a7a93cd2e492aaf8ace82ec2)) +* create initial classes with validation ([fc5db81](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/fc5db81092991da9686aa7f7855d30ae4f4e2e83)) +* Merge PR [#255](https://github.com/eclipse-tractusx/managed-identity-wallet/issues/255) ([a0d9bdf](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/a0d9bdfd1246c684156795523d64210fd828925f)) + +# [0.5.0-develop.4](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.3...v0.5.0-develop.4) (2024-02-23) + + +### Bug Fixes + +* Add missing gradle validation PR [#257](https://github.com/eclipse-tractusx/managed-identity-wallet/issues/257) ([f9acf55](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/f9acf55fd7998895f463225eb6a8ed8a621d734b)) + +# [0.5.0-develop.3](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.2...v0.5.0-develop.3) (2024-02-23) + + +### Bug Fixes + +* DAST scan PR [#256](https://github.com/eclipse-tractusx/managed-identity-wallet/issues/256) ([1c1f1fb](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/1c1f1fb3eacf0e34887ec78acdc4f51464c3f4de)) + +# [0.5.0-develop.2](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.1...v0.5.0-develop.2) (2024-02-22) + + +### Bug Fixes + +* add missing JSON properties ([5330f64](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/5330f64dd51669d4bfd337a656c6151512ad915a)) + + +### Features + +* add domain objects needed by sts infrastructure ([effb480](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/effb4801b8788c9221f6a64b4a71c990d05f3a64)) +* add dto objects for sts infrastructure ([103e7f2](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/103e7f2100b2613a5ab82038795c9d8f3bf06d02)) +* add exceptions for sts infrastructure ([b4bb9b7](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/b4bb9b78be41c721e968e1a350bd22920467b8fe)) +* add missing relationships to entities ([56a48eb](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/56a48eb6b4eb8a6a451512d772bf4298fce8f4f4)) +* add sts properties ([018d9c0](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/018d9c03430ecb15a73e753837a7a838eb7cca81)) +* add token endpoint happy-path test ([d6c30bf](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/d6c30bff5cec4f31498c512ecf06ed8237741d30)) +* extend 'miw.security' properties ([e982919](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/e9829195245780605a97e1cee5d22439c2f44c64)) +* implement token endpoint ([4227f3e](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/4227f3e2d147f71de84e4db6c0540e1e60849078)) +* use a spring validator for SecureTokenRequest ([accc0d2](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/accc0d20404852035876cb7cb4ce5ce7b5da6e64)) + +# [0.5.0-develop.1](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.4.0...v0.5.0-develop.1) (2024-02-09) + + +### Features + +* Merge PR [#252](https://github.com/eclipse-tractusx/managed-identity-wallet/issues/252) ([8cb132b](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/8cb132b035a60e7137e536a3a451ce314253ff7b)) + # [0.4.0](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.3.0...v0.4.0) (2024-02-09) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1915f50a3..9246e1b5e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ The companies involved want to increase the automotive industry's competitiveness, improve efficiency through industry-specific cooperation and accelerate company processes through standardization and access to information and data. A special focus is also on SMEs, whose active participation is of -central importance for the network’s success. That is why Catena-X has been +central importance for the network's success. That is why Catena-X has been conceived from the outset as an open network with solutions ready for SMEs, where these companies will be able to participate quickly and with little IT infrastructure investment. Tractus-X is meant to be the PoC project of the @@ -16,6 +16,19 @@ Catena-X alliance focusing on parts traceability. * https://projects.eclipse.org/projects/automotive.tractusx +## Project licenses + +The Tractus-X project uses the following licenses: + +* Apache-2.0 for code +* CC-BY-4.0 for non-code + +## Terms of Use + +This repository is subject to the Terms of Use of the Eclipse Foundation + +* https://www.eclipse.org/legal/termsofuse.php + ## Developer resources Information regarding source code management, builds, coding standards, and @@ -23,10 +36,87 @@ more. * https://projects.eclipse.org/projects/automotive.tractusx/developer +Getting started: + +* https://eclipse-tractusx.github.io/docs/developer + The project maintains the source code repositories in the following GitHub organization: * https://github.com/eclipse-tractusx/ +### How to submit pull requests + +It is paramount to ensure that the git history of the project remains clean and +consistent. This means that the usage of concise and expressive commits **MUST** +be used. Other helpful tips are to always rebase your branch before submitting +the pull request. + +First make sure you are working on your fork of the project, for example: + +```shell +$ git remote show origin +* remote origin +Fetch URL: git@github.com:borisrizov-zf/managed-identity-wallet.git +Push URL: git@github.com:borisrizov-zf/managed-identity-wallet.git +``` + +Make sure you setup a remote which points at the Tractus-X repository: + +```shell +git remote add upstream git@github.com:eclipse-tractusx/managed-identity-wallet.git +``` + +Whenever you want to start working, pull all changes from your remotes: + +```shell +git fetch --all +``` + +Then rebase your develop branch: + +```shell +git checkout develop +git rebase upstream/develop +``` + +At this point your branches are synced and you can create a new branch: + +```shell +git checkout -b feature/add-some-feature +``` + +### For Eclipse Committers and Maintainers + +The project uses the tool `semantic-release` to automatically create releases +and manage CHANGELOG.md entries. These files **SHOULD** never be manually edited +nor present in any PR. If you see this file in a PR, it means the incoming branch +is not at the tip of the project history - it will most likely mangle your project +when merged. + +You'll find all important steps in the files `.github/release.yaml` and `.releaserc`. + +The development work is always done on branch `develop`, all pull requests are made +against `develop`. When it is time to create an official release a PR from `develop` +to `main` must be created. **IMPORTANT**: after merging, you **MUST** wait for the +pipeline to complete, as it will create two new commits on `main`. After that you +**MUST** create a PR, merging main back into develop, to obtain these two new commits, +and to kick-off the new tag on `develop`. Failing to do so will result in a huge +headache, spaghetti code, faulty commits and other "life-improving" moments. **DO NOT +MESS THIS STEP UP**. + +It is possible to test how a release will work on your own fork, **BUT** you'll have +to do some extra work to make it happen. `semantic-release` uses git notes to track +the tags. You'll have to sync them manually (as most git configs do not include the settings +to do so automatically): + +```shell +git fetch upstream refs/notes/*:refs/notes/* +git push origin --tags +git push origin refs/notes/*:refs/notes/* +``` + +At this point your repository will behave exactly like upstream when doing a release. + ## Eclipse Development Process This Eclipse Foundation open project is governed by the Eclipse Foundation diff --git a/Dockerfile b/Dockerfile index 1dccce7af..59c6d84da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,7 +27,7 @@ RUN apk add curl USER miw -COPY LICENSE NOTICE.md DEPENDENCIES SECURITY.md /build/libs/miw-latest.jar /app/ +COPY LICENSE NOTICE.md miw/DEPENDENCIES SECURITY.md miw/build/libs/miw-latest.jar /app/ WORKDIR /app diff --git a/LICENSE_non-code b/LICENSE_non-code new file mode 100644 index 000000000..8e4d8772c --- /dev/null +++ b/LICENSE_non-code @@ -0,0 +1,395 @@ +Creative Commons Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/NOTICE.md b/NOTICE.md index b589e03db..4b4baed6f 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -4,6 +4,8 @@ This content is produced and maintained by the Eclipse Tractus-X project. * Project home: https://projects.eclipse.org/projects/automotive.tractusx +See the AUTHORS file(s) distributed with this work for additional information regarding authorship. + ## Trademarks Eclipse Tractus-X is a trademark of the Eclipse Foundation. @@ -16,12 +18,19 @@ source code repository logs. ## Declared Project Licenses -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. +The Tractus-X project uses the following licenses: + +- Apache-2.0 for code +- CC-BY-4.0 for non-code +Apache-2.0: +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. SPDX-License-Identifier: Apache-2.0 +CC-BY-4.0: +The materials in this repository are made available under the terms of the Creative Commons Attribution 4.0 International License, which is available at https://spdx.org/licenses/CC-BY-4.0.html. +SPDX-License-Identifier: CC-BY-4.0 + ## Source Code The project maintains the following source code repositories diff --git a/README.md b/README.md index 396475d29..a4c2920d3 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,41 @@ The Managed Identity Wallets (MIW) service implements the Self-Sovereign-Identit # Usage -See [INSTALL.md](INSTALL.md) +See [INSTALL.md](INSTALL.md) + +# Committer Documentation + +*(This section is also intentionally included in the CONTRIBUTING.md file)* + +The project uses the tool `semantic-release` to automatically create releases +and manage CHANGELOG.md entries. These files **SHOULD** never be manually edited +nor present in any PR. If you see this file in a PR, it means the incoming branch +is not at the tip of the project history - it will most likely mangle your project +when merged. + +You'll find all important steps in the files `.github/release.yaml` and `.releaserc`. + +The development work is always done on branch `develop`, all pull requests are made +against `develop`. When it is time to create an official release a PR from `develop` +to `main` must be created. **IMPORTANT**: after merging, you **MUST** wait for the +pipeline to complete, as it will create two new commits on `main`. After that you +**MUST** create a PR, merging main back into develop, to obtain these two new commits, +and to kick-off the new tag on `develop`. Failing to do so will result in a huge +headache, spaghetti code, faulty commits and other "life-improving" moments. **DO NOT +MESS THIS STEP UP**. + +It is possible to test how a release will work on your own fork, **BUT** you'll have +to do some extra work to make it happen. `semantic-release` uses git notes to track +the tags. You'll have to sync them manually (as most git configs do not include the settings +to do so automatically): + +```shell +git fetch upstream refs/notes/*:refs/notes/* +git push origin --tags +git push origin refs/notes/*:refs/notes/* +``` + +At this point your repository will behave exactly like upstream when doing a release. # Developer Documentation @@ -113,9 +147,6 @@ Overview by Endpoint | **Verfiable Credential - Validation** | Create | POST | /api/credentials/validation | **view_wallets** OR
**view_wallet** | | | **Verfiable Credential - Issuer** | Read | GET | /api/credentials/issuer | **view_wallets** | | | **Verfiable Credential - Issuer** | Create | POST | /api/credentials/issuer | **update_wallets** | | -| **Verfiable Credential - Issuer** | Create | POST | /api/credentials/issuer/membership | **update_wallets** | | -| **Verfiable Credential - Issuer** | Create | POST | /api/credentials/issuer/framework | **update_wallets** | | -| **Verfiable Credential - Issuer** | Create | POST | /api/credentials/issuer/distmantler | **update_wallets** | | | **DIDDocument** | Read | GET | /{bpn}/did.json | N/A | | | **DIDDocument** | Read | GET | /api/didDocuments/{identifier} | N/A | | @@ -139,6 +170,8 @@ Credentials* recreate the secret. ## Development Setup +NOTE: The MIW requires access to the internet in order to validate the JSON-LD schema of DID documents. + ### Prerequisites To simplify the dev environment, [Taskfile](https://taskfile.dev) is used as a task executor. You have to install it @@ -155,13 +188,13 @@ directory, but without ".dist" at the end. Description of the env files: -- **env.local**: Set up everything to get ready for flow "local". You need to fill in the passwords. +- **env.local**: Set up everything to get ready for flow "local". You need to fill in the passwords. - **env.docker**: Set up everything to get ready for flow "docker". You need to fill in the passwords. > **IMPORTANT**: ssi-lib is resolving DID documents over the network. There are two endpoints that rely on this resolution: > - Verifiable Credentials - Validation > - Verifiable Presentations - Validation -> +> > The following parameters are set in env.local or env.docker file per default: > ENFORCE_HTTPS_IN_DID_RESOLUTION=false > MIW_HOST_NAME=localhost @@ -208,10 +241,11 @@ When you just run `task` without parameters, you will see all tasks available. ### pgAdmin -This local environment contains [pgAdmin](https://www.pgadmin.org/), which is also started (default: http://localhost:8888). +This local environment contains [pgAdmin](https://www.pgadmin.org/), which is also started ( +default: http://localhost:8888). The default login is: -``` +``` user: pg@admin.com (you can change it in the env.* files) password: the one you set for "POSTGRES_PASSWORD" in the env.* files ``` @@ -231,7 +265,7 @@ For example, You can save DB backups there, so you can access them on your local See OpenAPI documentation, which is automatically created from the source and available on each deployment at the `/docs/api-docs/docs` endpoint (e.g. locally at http://localhost:8087/docs/api-docs/docs). An export of the JSON -document can be also found in [docs/openapi_v001.json](docs/openapi_v001.json). +document can be also found in [docs/openapi_v001.json](docs/api/openapi_v001.json). # Test Coverage @@ -306,10 +340,10 @@ This process ensures that any issues with the database schema are resolved by re | KEYCLOAK_REALM | Realm name of keycloak | miw_test | | KEYCLOAK_CLIENT_ID | Keycloak private client id | | | AUTH_SERVER_URL | Keycloak server url | | -| SUPPORTED_FRAMEWORK_VC_TYPES | Supported framework VC, provide values ie type1=value1,type2=value2 | cx-behavior-twin=Behavior Twin,cx-pcf=PCF,cx-quality=Quality,cx-resiliency=Resiliency,cx-sustainability=Sustainability,cx-traceability=ID_3.0_Trace | | ENFORCE_HTTPS_IN_DID_RESOLUTION | Enforce https during web did resolution | true | -| CONTRACT_TEMPLATES_URL | Contract templates URL used in summary VC | https://public.catena-x.org/contracts/ | | APP_LOG_LEVEL | Log level of application | INFO | +| AUTHORITY_SIGNING_SERVICE_TYPE | Base wallet signing type, Currency only LOCAL is supported | Local | +| LOCAL_SIGNING_KEY_STORAGE_TYPE | Key storage type, currently only DB is supported | DB | | | | | # Technical Debts and Known issue diff --git a/charts/managed-identity-wallet/Chart.yaml b/charts/managed-identity-wallet/Chart.yaml index 78e11d889..495ea6d8b 100644 --- a/charts/managed-identity-wallet/Chart.yaml +++ b/charts/managed-identity-wallet/Chart.yaml @@ -25,8 +25,8 @@ description: | type: application -version: 0.4.0 -appVersion: 0.4.0 +version: 0.5.0-develop.20 +appVersion: 0.5.0-develop.20 home: https://github.com/eclipse-tractusx/managed-identity-wallet keywords: diff --git a/charts/managed-identity-wallet/README.md b/charts/managed-identity-wallet/README.md index da4f35823..d25fa1907 100644 --- a/charts/managed-identity-wallet/README.md +++ b/charts/managed-identity-wallet/README.md @@ -2,7 +2,7 @@ # managed-identity-wallet -![Version: 0.4.0](https://img.shields.io/badge/Version-0.4.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.0](https://img.shields.io/badge/AppVersion-0.4.0-informational?style=flat-square) +![Version: 0.5.0-develop.20](https://img.shields.io/badge/Version-0.5.0--develop.20-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.0-develop.20](https://img.shields.io/badge/AppVersion-0.5.0--develop.20-informational?style=flat-square) Managed Identity Wallet is supposed to supply a secure data source and data sink for Digital Identity Documents (DID), in order to enable Self-Sovereign Identity founding on those DIDs. And at the same it shall support an uninterrupted tracking and tracing and documenting the usage of those DIDs, e.g. within logistical supply chains. diff --git a/dev-assets/env-files/env.docker.dist b/dev-assets/env-files/env.docker.dist index d6c8204c8..e0ac25992 100644 --- a/dev-assets/env-files/env.docker.dist +++ b/dev-assets/env-files/env.docker.dist @@ -26,10 +26,11 @@ ENCRYPTION_KEY= AUTHORITY_WALLET_BPN=BPNL000000000000 AUTHORITY_WALLET_DID=did:web:localhost:BPNL000000000000 AUTHORITY_WALLET_NAME=Catena-X +AUTHORITY_SIGNING_SERVICE_TYPE=LOCAL +LOCAL_SIGNING_KEY_STORAGE_TYPE=DB KEYCLOAK_REALM=miw_test VC_SCHEMA_LINK="https://www.w3.org/2018/credentials/v1, https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json" VC_EXPIRY_DATE=01-01-2025 -SUPPORTED_FRAMEWORK_VC_TYPES="PcfCredential, SustainabilityCredential, QualityCredential, TraceabilityCredential, BehaviorTwinCredential, ResiliencyCredential" MIW_HOST_NAME=localhost ENFORCE_HTTPS_IN_DID_RESOLUTION=false diff --git a/dev-assets/env-files/env.local.dist b/dev-assets/env-files/env.local.dist index 68ffa0547..d7065338a 100644 --- a/dev-assets/env-files/env.local.dist +++ b/dev-assets/env-files/env.local.dist @@ -26,10 +26,11 @@ ENCRYPTION_KEY= AUTHORITY_WALLET_BPN=BPNL000000000000 AUTHORITY_WALLET_DID=did:web:localhost:BPNL000000000000 AUTHORITY_WALLET_NAME=Catena-X +AUTHORITY_SIGNING_SERVICE_TYPE=LOCAL +LOCAL_SIGNING_KEY_STORAGE_TYPE=DB KEYCLOAK_REALM=miw_test VC_SCHEMA_LINK="https://www.w3.org/2018/credentials/v1, https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json" VC_EXPIRY_DATE=01-01-2025 -SUPPORTED_FRAMEWORK_VC_TYPES="PcfCredential, SustainabilityCredential, QualityCredential, TraceabilityCredential, BehaviorTwinCredential, ResiliencyCredential" MIW_HOST_NAME=localhost ENFORCE_HTTPS_IN_DID_RESOLUTION=false diff --git a/docs/api/openapi_v001.json b/docs/api/openapi_v001.json new file mode 100644 index 000000000..d29890256 --- /dev/null +++ b/docs/api/openapi_v001.json @@ -0,0 +1,3121 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Managed Identity Wallets API", + "description": "Managed Identity Wallets API", + "termsOfService": "https://www.eclipse.org/legal/termsofuse.php", + "contact": { + "name": "Eclipse Tractus-X", + "url": "https://projects.eclipse.org/projects/automotive.tractusx", + "email": "tractusx-dev@eclipse.org" + }, + "license": { + "name": "Apache 2.0", + "url": "https://github.com/eclipse-tractusx/managed-identity-wallets/blob/develop/LICENSE" + }, + "version": "0.0.1" + }, + "security": [ + {"Authenticate using access_token": []}, + {"sts_token": []} + ], + "servers": [], + "paths": { + "/api/wallets": { + "get": { + "tags": [ + "Wallets" + ], + "summary": "List of wallets", + "description": "Permission: **view_wallets** \n\n Retrieve list of registered wallets", + "operationId": "getWallets", + "parameters": [ + { + "name": "pageNumber", + "in": "query", + "description": "Page number, Page number start with zero", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "size", + "in": "query", + "description": "Number of records per page", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 2147483647 + } + }, + { + "name": "sortColumn", + "in": "query", + "description": "Sort column name", + "required": false, + "schema": { + "type": "string", + "default": "createdAt" + }, + "examples": { + "Creation date": { + "description": "Creation date", + "value": "createdAt" + }, + "Wallet BPN": { + "description": "Wallet BPN", + "value": "bpn" + }, + "Wallet did": { + "description": "Wallet did", + "value": "did" + }, + "Wallet name": { + "description": "Wallet name", + "value": "name" + } + } + }, + { + "name": "sortTpe", + "in": "query", + "description": "Sort order", + "required": false, + "schema": { + "type": "string", + "default": "desc" + }, + "examples": { + "Ascending order": { + "description": "Ascending order", + "value": "asc" + }, + "Descending order": { + "description": "Descending order", + "value": "desc" + } + } + } + ], + "responses": { + "200": { + "description": "Wallet list", + "content": { + "application/json": { + "examples": { + "Wallet list": { + "description": "Wallet list", + "value": { + "content": [ + { + "name": "companyA", + "did": "did:web:localhost:BPNL000000000001", + "bpn": "BPNL000000000001", + "algorithm": "ED25519", + "didDocument": { + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3c.github.io/vc-jws-2020/contexts/v1" + ], + "id": "did:web:localhost:BPNL000000000001", + "verificationMethod": [ + { + "controller": "did:web:localhost:BPNL000000000001", + "id": "did:web:localhost:BPNL000000000001#", + "publicKeyJwk": { + "crv": "Ed25519", + "kty": "OKP", + "x": "mhph0ZSVk7cDVmazbaaC3jBDpphW4eNygAK9gHPlMow" + }, + "type": "JsonWebKey2020" + } + ] + } + } + ], + "pageable": { + "sort": { + "empty": false, + "sorted": true, + "unsorted": false + }, + "offset": 0, + "pageNumber": 0, + "pageSize": 1, + "paged": true, + "unpaged": false + }, + "totalElements": 3, + "totalPages": 3, + "last": false, + "size": 1, + "number": 0, + "sort": { + "empty": false, + "sorted": true, + "unsorted": false + }, + "first": true, + "numberOfElements": 1, + "empty": false + } + } + } + } + } + }, + "400": { + "description": "The input does not comply to the syntax requirements", + "content": { + "application/json": { + "examples": { + "Response in case of invalid data provided": { + "description": "Response in case of invalid data provided", + "value": { + "type": "about:blank", + "title": "title", + "status": 400, + "detail": "details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689760833962, + "errors": {} + } + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden access" + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + }, + "post": { + "tags": [ + "Wallets" + ], + "summary": "Create Wallet", + "description": "Permission: **add_wallets** (The BPN of the base wallet must equal BPN of caller)\n\n Create a wallet and store it", + "operationId": "createWallet", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateWalletRequest" + }, + "examples": { + "Create wallet with BPN": { + "description": "Create wallet with BPN", + "value": { + "businessPartnerNumber": "BPNL000000000001", + "companyName": "companyA", + "didUrl": "portal.com:BPNL000000000001" + } + } + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "examples": { + "Success response": { + "description": "Success response", + "value": { + "name": "companyA", + "did": "did:web:localhost:BPNL000000000001", + "bpn": "BPNL000000000501", + "algorithm": "ED25519", + "didDocument": { + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3c.github.io/vc-jws-2020/contexts/v1" + ], + "id": "did:web:localhost:BPNL000000000001", + "verificationMethod": [ + { + "controller": "did:web:localhost:BPNL000000000001", + "id": "did:web:localhost:BPNL000000000001#key-1", + "publicKeyJwk": { + "crv": "Ed25519", + "kty": "OKP", + "x": "0Ap6FsX5UuRBIoOzxWtcFA2ymnqXw0U08Ino_mIuYM4" + }, + "type": "JsonWebKey2020" + }, + { + "controller": "did:web:localhost:BPNL000000000001", + "id": "did:web:localhost:BPNL000000000001#key-2", + "publicKeyJwk": { + "crv": "secp256k1", + "kty": "EC", + "x": "f9PkTOpsbcgKe_-s6bNCve3-aB1VZAFsCub8C5bhDn0", + "y": "xH1d7jCFavolGVZtaWcZZGP2nLuEsamDCotD56llxUk" + }, + "type": "JsonWebKey2020" + } + ] + } + } + } + } + } + } + }, + "400": { + "description": "The input does not comply to the syntax requirements", + "content": { + "application/json": { + "examples": { + "Response in case of invalid data provided": { + "description": "Response in case of invalid data provided", + "value": { + "type": "about:blank", + "title": "Invalid data provided", + "status": 400, + "detail": "details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689760833962, + "errors": { + "filed": "filed error message" + } + } + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden access" + }, + "409": { + "description": "The request could not be completed due to a conflict.", + "content": { + "application/json": { + "examples": { + "Wallet already exist": { + "description": "Wallet already exist", + "value": { + "type": "about:blank", + "title": "Wallet is already exists for bpn BPNL000000000001", + "status": 409, + "detail": "Wallet is already exists for bpn BPNL000000000001", + "instance": "/api/wallets", + "properties": { + "timestamp": 1689762639948 + } + } + } + } + } + } + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + } + }, + "/api/wallets/{identifier}/credentials": { + "post": { + "tags": [ + "Wallets" + ], + "summary": "Store Verifiable Credential", + "description": "Permission: **update_wallets** OR **update_wallet** (The BPN of wallet to extract credentials from must equal BPN of caller) \n\n Store a verifiable credential in the wallet of the given identifier", + "operationId": "storeCredential", + "parameters": [ + { + "name": "identifier", + "in": "path", + "description": "Did or BPN", + "required": true, + "schema": { + "type": "string" + }, + "examples": { + "bpn": { + "description": "bpn", + "value": "BPNL000000000000" + }, + "did": { + "description": "did", + "value": "did:web:localhost:BPNL000000000000" + } + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "example": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#" + ], + "id": "did:web:localhost.in#123456789", + "type": [ + "VerifiableCredential", + "LegalParticipant" + ], + "issuer": "did:web:localhost.in", + "issuanceDate": "2023-05-04T07:36:03.633Z", + "credentialSubject": { + "id": "https://localhost/.well-known/participant.json", + "type": "gx:LegalParticipant", + "gx:legalName": "Sample Company", + "gx:legalRegistrationNumber": { + "gx:taxID": "113123123" + }, + "gx:headquarterAddress": { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "gx:legalAddress": { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "gx-terms-and-conditions:gaiaxTermsAndConditions": "70c1d713215f95191a11d38fe2341faed27d19e083917bc8732ca4fea4976700" + }, + "proof": { + "type": "JsonWebSignature2020", + "created": "2023-05-04T07:36:04.079Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:web:localhost", + "jws": "eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..iHki8WC3nPfcSRkC_AV4tXh0ikfT7BLPTGc_0ecI8zontTmJLqwcpPfAt0PFsoo3SkZgc6j636z55jj5tagBc-OKoiDu7diWryNAnL9ASsmWJyrPhOKVARs6x6PxVaTFBuyCfAHZeipxmkcYfNB_jooIXO2HuRcL2odhsQHELkGc5IDD-aBMWyNpfVAaYQ-cCzvDflZQlsowziUKfMkBfwpwgMdXFIgKWYdDIRvzA-U-XiC11-6QV7tPeKsMguEU0F5bh8cCEm2rooqXtENcsM_7cqFdQoOyblJyM-agoz2LUTj9QIdn9_gnNkGN-2U7_qBJWmHkK1Hm_mHqcNeeQw" + } + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Success Response", + "content": { + "application/json": { + "examples": { + "Success Response": { + "description": "Success Response", + "value": { + "message": "Credential with id did:web:localhost#123456789 has been successfully stored" + } + } + } + } + } + }, + "400": { + "description": "The input does not comply to the syntax requirements", + "content": { + "application/json": { + "examples": { + "Response in case of invalid data provided": { + "description": "Response in case of invalid data provided", + "value": { + "type": "about:blank", + "title": "title", + "status": 400, + "detail": "details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689760833962, + "errors": {} + } + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden access" + }, + "404": { + "description": "Wallet not found with provided identifier", + "content": { + "application/json": { + "examples": { + "Wallet not found with provided identifier": { + "description": "Wallet not found with provided identifier", + "value": { + "type": "about:blank", + "title": "Wallet not found for identifier did:web:localhost:BPNL000000044001", + "status": 404, + "detail": "Wallet not found for identifier did:web:localhost:BPNL000000044001", + "instance": "/api/wallets/did%3Aweb%3Alocalhost%3ABPNL0000000/credentials", + "properties": { + "timestamp": 1689765541959 + } + } + } + } + } + } + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + } + }, + "/api/token": { + "post": { + "tags": [ + "STS" + ], + "summary": "Create and Sign Access Tokens", + "description": "The endpoint for creating and signing access tokens which are to be used during a verifiable presentation flow.", + "operationId": "token_1", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SecureTokenRequest" + }, + "examples": { + "Request Secure Token using Scopes": { + "description": "Request Secure Token using Scopes", + "value": { + "audience": "BPNL000000000009", + "client_id": "your_client_id", + "client_secret": "your_client_secret", + "grant_type": "client_credentials", + "bearer_access_scope": "org.eclipse.tractusx.vc.type:ValidCredentialType:read" + } + }, + "Request Secure Token using Access Token": { + "description": "Request Secure Token using Access Token", + "value": { + "audience": "BPNL000000000009", + "client_id": "your_client_id", + "client_secret": "your_client_secret", + "grant_type": "client_credentials", + "access_token": "a_jwt_token" + } + } + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "examples": { + "Success response": { + "description": "Success response", + "value": { + "token": "a_jwt_token", + "expiresAt": 1706888709315 + } + } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "examples": { + "Unknown BPN": { + "description": "Unknown BPN", + "value": { + "error": "UnknownBusinessPartnerNumber", + "errorDescription": "The provided BPN 'BPNL000000000001' is unknown" + } + }, + "Wrong Grant Type": { + "description": "Wrong Grant Type", + "value": { + "error": "UnsupportedGrantTypeException", + "errorDescription": "The provided 'grant_type' is not valid. Use 'client_credentials'." + } + }, + "Invalid idp Token Response": { + "description": "Invalid idp Token Response", + "value": { + "error": "InvalidIdpTokenResponse", + "errorDescription": "The idp response cannot be null. Possible causes for this are: the 'clientId' is invalid, or the 'client' is not enabled." + } + }, + "Invalid Secure Token Request": { + "description": "Invalid Secure Token Request", + "value": { + "error": "InvalidSecureTokenRequest", + "errorDescription": "The provided data could not be used to create and sign a token." + } + } + } + } + } + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + } + } + }, + "/api/presentations": { + "post": { + "tags": [ + "Verifiable Presentations - Generation" + ], + "summary": "Create Verifiable Presentation", + "description": "Permission: **update_wallets** OR **update_wallet** (The BPN of the issuer of the Verifiable Presentation must equal to BPN of caller) \n\n Create a verifiable presentation from a list of verifiable credentials, signed by the holder", + "operationId": "createPresentation", + "parameters": [ + { + "name": "audience", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "asJwt", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "example": { + "verifiableCredentials": [ + { + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z", + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Verifiable Presentation", + "content": { + "application/json": { + "examples": { + "VP as Json-LD": { + "description": "VP as Json-LD", + "value": { + "vp": { + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "id": "did:web:localhost:BPNL000000000000#b2e69e47-95f3-48ff-af30-eaaab36431d5", + "type": [ + "VerifiablePresentation" + ], + "verifiableCredential": [ + { + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z", + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + ] + } + } + }, + "VP as JWT": { + "description": "VP as JWT", + "value": { + "vp": "eyJraWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJzdWIiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwiYXVkIjoic21hcnQiLCJpc3MiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwidnAiOnsiaWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIzM4ZTU2ZTg1LTNkODQtNGEyNS1iZjg1LWFiMjRlYzY4MmMwOSIsInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vY2F0ZW5heC1uZy5naXRodWIuaW8vcHJvZHVjdC1jb3JlLXNjaGVtYXMvYnVzaW5lc3NQYXJ0bmVyRGF0YS5qc29uIiwiaHR0cHM6Ly93M2lkLm9yZy9zZWN1cml0eS9zdWl0ZXMvandzLTIwMjAvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIkJwbkNyZWRlbnRpYWwiXSwiaWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwI2Y3M2UzNjMxLWJhODctNGEwMy1iZWEzLWIyODcwMDA1Njg3OSIsImlzc3VlciI6ImRpZDp3ZWI6bG9jYWxob3N0OkJQTkwwMDAwMDAwMDAwMDAiLCJpc3N1YW5jZURhdGUiOiIyMDIzLTA3LTE5VDA5OjExOjM0WiIsImV4cGlyYXRpb25EYXRlIjoiMjAyNC0xMi0zMVQxODozMDowMFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDp3ZWI6bG9jYWxob3N0OkJQTkwwMDAwMDAwMDAwMDAiLCJicG4iOiJCUE5MMDAwMDAwMDAwMDAwIiwidHlwZSI6IkJwbkNyZWRlbnRpYWwifSwicHJvb2YiOnsicHJvb2ZQdXJwb3NlIjoicHJvb2ZQdXJwb3NlIiwidHlwZSI6Ikpzb25XZWJTaWduYXR1cmUyMDIwIiwidmVyaWZpY2F0aW9uTWV0aG9kIjoiZGlkOndlYjpsb2NhbGhvc3Q6QlBOTDAwMDAwMDAwMDAwMCMiLCJjcmVhdGVkIjoiMjAyMy0wNy0xOVQwOToxMTozOVoiLCJqd3MiOiJleUpoYkdjaU9pSkZaRVJUUVNKOS4uZmRuMnFVODVhdU9sdGRIRExkSEk3c0pWVjFaUGRmdHBpWGRfbmRYTjBkRmdTRFdpSXJTY2REMDN3dHZLTHFfSC1zaFFXZmgyUlllTW1ybEV6QWhmRHcifX19LCJleHAiOjE2ODk4MzQ4MDUsImp0aSI6ImIwODYzOWZiLWQ5MWEtNGUwZS1iNmY4LTYzYjdhMzQ1ZTRhZiJ9.80x0AB-OauefdeZfx1cwhitdVKRvCRFeFzYwU73DL7y4w34vu6BdfHWLBGjkwELxkQEoFfiTPOqtuyqhtsyDBg" + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden access" + }, + "404": { + "description": "Wallet not found with provided identifier", + "content": { + "application/json": { + "examples": { + "Wallet not found with provided identifier": { + "description": "Wallet not found with provided identifier", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 404, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + } + }, + "/api/presentations/validation": { + "post": { + "tags": [ + "Verifiable Presentations - Validation" + ], + "summary": "Validate Verifiable Presentation", + "description": "Permission: **view_wallets** OR **view_wallet** \n\n Validate Verifiable Presentation with all included credentials", + "operationId": "validatePresentation", + "parameters": [ + { + "name": "audience", + "in": "query", + "description": "Audience to validate in VP (Only supported in case of JWT formatted VP)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "asJwt", + "in": "query", + "description": "Pass true in case of VP is in JWT format", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "withCredentialExpiryDate", + "in": "query", + "description": "Check expiry of VC(Only supported in case of JWT formatted VP)", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "examples": { + "VP as JWT": { + "description": "VP as JWT", + "value": { + "vp": "eyJraWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJzdWIiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwiYXVkIjoic21hcnQiLCJpc3MiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwidnAiOnsiaWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIzZmZWRmYWRmLWYxNTItNGNjZS05MWQ1LWNjMjhhNzhlMTExMyIsInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vY2F0ZW5heC1uZy5naXRodWIuaW8vcHJvZHVjdC1jb3JlLXNjaGVtYXMvYnVzaW5lc3NQYXJ0bmVyRGF0YS5qc29uIiwiaHR0cHM6Ly93M2lkLm9yZy9zZWN1cml0eS9zdWl0ZXMvandzLTIwMjAvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIkJwbkNyZWRlbnRpYWwiXSwiaWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwI2Y3M2UzNjMxLWJhODctNGEwMy1iZWEzLWIyODcwMDA1Njg3OSIsImlzc3VlciI6ImRpZDp3ZWI6bG9jYWxob3N0OkJQTkwwMDAwMDAwMDAwMDAiLCJpc3N1YW5jZURhdGUiOiIyMDIzLTA3LTE5VDA5OjExOjM0WiIsImV4cGlyYXRpb25EYXRlIjoiMjAyNC0xMi0zMVQxODozMDowMFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDp3ZWI6bG9jYWxob3N0OkJQTkwwMDAwMDAwMDAwMDAiLCJicG4iOiJCUE5MMDAwMDAwMDAwMDAwIiwidHlwZSI6IkJwbkNyZWRlbnRpYWwifSwicHJvb2YiOnsicHJvb2ZQdXJwb3NlIjoicHJvb2ZQdXJwb3NlIiwidHlwZSI6Ikpzb25XZWJTaWduYXR1cmUyMDIwIiwidmVyaWZpY2F0aW9uTWV0aG9kIjoiZGlkOndlYjpsb2NhbGhvc3Q6QlBOTDAwMDAwMDAwMDAwMCMiLCJjcmVhdGVkIjoiMjAyMy0wNy0xOVQwOToxMTozOVoiLCJqd3MiOiJleUpoYkdjaU9pSkZaRVJUUVNKOS4uZmRuMnFVODVhdU9sdGRIRExkSEk3c0pWVjFaUGRmdHBpWGRfbmRYTjBkRmdTRFdpSXJTY2REMDN3dHZLTHFfSC1zaFFXZmgyUlllTW1ybEV6QWhmRHcifX19LCJleHAiOjE2ODk3NTg1NDQsImp0aSI6IjdlNWE4MzQ4LTgwZjUtNGIzMS1iMDNlLTBiOTJmNzc4ZTVjZiJ9.c7FS-CLwm3vxfO9847M5sqcVxv3QbwwSmSsFWcGif7MOesjt1pdnARlQ4pvHzgsFj1UqBEvHwZQvyYyPCQg_Cw" + } + }, + "VP as json-ld": { + "description": "VP as json-ld", + "value": { + "vp": { + "id": "b9d97cef-758d-4a7c-843d-86f17632b08a", + "type": [ + "VerifiablePresentation" + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "verifiableCredential": [ + { + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z", + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + ] + } + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Verifiable presentation validate", + "content": { + "application/json": { + "examples": { + "VP as JWT": { + "description": "VP as JWT", + "value": { + "valid": true, + "validateJWTExpiryDate": true, + "validateAudience": true, + "vp": "eyJraWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJzdWIiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwiYXVkIjoic21hcnQiLCJpc3MiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwidnAiOnsiaWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIzM4ZTU2ZTg1LTNkODQtNGEyNS1iZjg1LWFiMjRlYzY4MmMwOSIsInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vY2F0ZW5heC1uZy5naXRodWIuaW8vcHJvZHVjdC1jb3JlLXNjaGVtYXMvYnVzaW5lc3NQYXJ0bmVyRGF0YS5qc29uIiwiaHR0cHM6Ly93M2lkLm9yZy9zZWN1cml0eS9zdWl0ZXMvandzLTIwMjAvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIkJwbkNyZWRlbnRpYWwiXSwiaWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwI2Y3M2UzNjMxLWJhODctNGEwMy1iZWEzLWIyODcwMDA1Njg3OSIsImlzc3VlciI6ImRpZDp3ZWI6bG9jYWxob3N0OkJQTkwwMDAwMDAwMDAwMDAiLCJpc3N1YW5jZURhdGUiOiIyMDIzLTA3LTE5VDA5OjExOjM0WiIsImV4cGlyYXRpb25EYXRlIjoiMjAyNC0xMi0zMVQxODozMDowMFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDp3ZWI6bG9jYWxob3N0OkJQTkwwMDAwMDAwMDAwMDAiLCJicG4iOiJCUE5MMDAwMDAwMDAwMDAwIiwidHlwZSI6IkJwbkNyZWRlbnRpYWwifSwicHJvb2YiOnsicHJvb2ZQdXJwb3NlIjoicHJvb2ZQdXJwb3NlIiwidHlwZSI6Ikpzb25XZWJTaWduYXR1cmUyMDIwIiwidmVyaWZpY2F0aW9uTWV0aG9kIjoiZGlkOndlYjpsb2NhbGhvc3Q6QlBOTDAwMDAwMDAwMDAwMCMiLCJjcmVhdGVkIjoiMjAyMy0wNy0xOVQwOToxMTozOVoiLCJqd3MiOiJleUpoYkdjaU9pSkZaRVJUUVNKOS4uZmRuMnFVODVhdU9sdGRIRExkSEk3c0pWVjFaUGRmdHBpWGRfbmRYTjBkRmdTRFdpSXJTY2REMDN3dHZLTHFfSC1zaFFXZmgyUlllTW1ybEV6QWhmRHcifX19LCJleHAiOjE2ODk4MzQ4MDUsImp0aSI6ImIwODYzOWZiLWQ5MWEtNGUwZS1iNmY4LTYzYjdhMzQ1ZTRhZiJ9.80x0AB-OauefdeZfx1cwhitdVKRvCRFeFzYwU73DL7y4w34vu6BdfHWLBGjkwELxkQEoFfiTPOqtuyqhtsyDBg", + "validateExpiryDate": true + } + } + } + } + } + }, + "400": { + "description": "The input does not comply to the syntax requirements", + "content": { + "application/json": { + "examples": { + "Validation of VP in form of JSON-LD is not supported": { + "description": "Validation of VP in form of JSON-LD is not supported", + "value": { + "type": "about:blank", + "title": "Validation of VP in form of JSON-LD is not supported", + "status": 400, + "detail": "Validation of VP in form of JSON-LD is not supported", + "instance": "/api/presentations/validation", + "properties": { + "timestamp": 1689835085703 + } + } + }, + "Response in case of invalid data provided": { + "description": "Response in case of invalid data provided", + "value": { + "type": "about:blank", + "title": "Invalid data provided", + "status": 400, + "detail": "details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689760833962, + "errors": { + "filed": "filed error message" + } + } + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden access" + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + } + }, + "/api/credentials": { + "get": { + "tags": [ + "Verifiable Credential - Holder" + ], + "summary": "Query Verifiable Credentials", + "description": "Permission: **view_wallets** OR **view_wallet** (The BPN of holderIdentifier must equal BPN of caller)\n\n Search verifiable credentials with filter criteria", + "operationId": "getCredentials", + "parameters": [ + { + "name": "credentialId", + "in": "query", + "description": "Credential Id", + "required": false, + "schema": { + "type": "string" + }, + "examples": { + "Credential Id": { + "description": "Credential Id", + "value": "did:web:localhost:BPNL000000000000#12528899-160a-48bd-ba15-f396c3959ae9" + } + } + }, + { + "name": "issuerIdentifier", + "in": "query", + "description": "Issuer identifier(did of BPN)", + "required": false, + "schema": { + "type": "string" + }, + "examples": { + "bpn": { + "description": "bpn", + "value": "BPNL000000000000" + }, + "did": { + "description": "did", + "value": "did:web:localhost:BPNL000000000000" + } + } + }, + { + "name": "type", + "in": "query", + "description": "Type of VC", + "required": false, + "schema": { + "type": "array", + "maxItems": 100, + "items": { + "type": "string" + } + }, + "examples": { + "SummaryCredential": { + "description": "SummaryCredential", + "value": "SummaryCredential" + }, + "BpnCredential": { + "description": "BpnCredential", + "value": "BpnCredential" + } + } + }, + { + "name": "sortColumn", + "in": "query", + "description": "Sort column name", + "required": false, + "schema": { + "type": "string", + "default": "createdAt" + }, + "examples": { + "creation date": { + "description": "creation date", + "value": "createdAt" + }, + "Self issued credential": { + "description": "Self issued credential", + "value": "selfIssued" + }, + "Stored credential": { + "description": "Stored credential", + "value": "stored" + }, + "Issuer did": { + "description": "Issuer did", + "value": "issuerDid" + }, + "Credential type": { + "description": "Credential type", + "value": "type" + }, + "Credential id": { + "description": "Credential id", + "value": "credentialId" + } + } + }, + { + "name": "sortTpe", + "in": "query", + "description": "Sort order", + "required": false, + "schema": { + "type": "string", + "default": "desc" + }, + "examples": { + "Ascending order": { + "description": "Ascending order", + "value": "asc" + }, + "Descending order": { + "description": "Descending order", + "value": "desc" + } + } + }, + { + "name": "pageNumber", + "in": "query", + "description": "Page number, Page number start with zero", + "required": false, + "schema": { + "maximum": 2147483647, + "minimum": 0, + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "size", + "in": "query", + "description": "Number of records per page", + "required": false, + "schema": { + "maximum": 2147483647, + "minimum": 0, + "type": "integer", + "format": "int32", + "default": 2147483647 + } + }, + { + "name": "asJwt", + "in": "query", + "description": "Specifies whether the VC (Verifiable Credential) should be created as a JWT (JSON Web Token). If set to true, the VC will be generated in JWT formatSetting this parameter to false will result in the VC being created as JSON-LD Defaults to false if not specified.", + "required": false, + "schema": { + "type": "boolean", + "default": false + }, + "examples": { + "Create VC as JWT": { + "description": "Create VC as JWT", + "value": true + }, + "Do not create VC as JWT": { + "description": "Do not create VC as JWT", + "value": false + } + } + } + ], + "responses": { + "200": { + "description": "Credential list", + "content": { + "application/json": { + "examples": { + "Credential list": { + "description": "Credential list", + "value": { + "content": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/SummaryVC.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#954d43de-ebed-481d-9e35-e3bbb311b8f5", + "type": [ + "VerifiableCredential", + "SummaryCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-14T11:05:48Z", + "expirationDate": "2023-09-30T18:30:00Z", + "credentialSubject": [ + { + "contractTemplate": "https://public.catena-x.org/contracts/", + "holderIdentifier": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "items": [ + "BpnCredential" + ], + "type": "SummaryCredential" + } + ], + "proof": { + "created": "2023-07-14T11:05:50Z", + "jws": "eyJhbGciOiJFZERTQSJ9..4xwFUCtP0xXVEo5_lXd90Vv-TWO2FijZut-HZ5cozAQseexj8EpTkK1erhFbf2Ua1kb8pi_H5At5HiPkTxSIAQ", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + ], + "pageable": { + "sort": { + "empty": false, + "sorted": true, + "unsorted": false + }, + "offset": 0, + "pageNumber": 0, + "pageSize": 2147483647, + "paged": true, + "unpaged": false + }, + "totalElements": 1, + "totalPages": 1, + "last": true, + "size": 2147483647, + "number": 0, + "sort": { + "empty": false, + "sorted": true, + "unsorted": false + }, + "first": true, + "numberOfElements": 1, + "empty": false + } + } + } + } + } + }, + "400": { + "description": "The input does not comply to the syntax requirements", + "content": { + "application/json": { + "examples": { + "Response in case of invalid data provided": { + "description": "Response in case of invalid data provided", + "value": { + "type": "about:blank", + "title": "title", + "status": 400, + "detail": "details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689760833962, + "errors": {} + } + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden access" + }, + "404": { + "description": "Wallet not found with caller BPN", + "content": { + "application/json": { + "examples": { + "Wallet not found with caller BPN": { + "description": "Wallet not found with caller BPN", + "value": { + "type": "about:blank", + "title": "Wallet not found for identifier did:web:localhost:BPNL0000000", + "status": 404, + "detail": "Wallet not found for identifier did:web:localhost:BPNL0000000", + "instance": "/api/wallets/did%3Aweb%3Alocalhost%3ABPNL0000000/credentials", + "properties": { + "timestamp": 1689765541959 + } + } + } + } + } + } + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + }, + "post": { + "tags": [ + "Verifiable Credential - Holder" + ], + "summary": "Issue Verifiable Credential", + "description": "Permission: **update_wallets** OR **update_wallet** (The BPN of the issuer of the Verifiable Credential must equal BPN of caller)\nIssue a verifiable credential with a given issuer DID", + "operationId": "issueCredential", + "parameters": [ + { + "name": "asJwt", + "in": "query", + "description": "Specifies whether the VC (Verifiable Credential) should be created as a JWT (JSON Web Token). If set to true, the VC will be generated in JWT formatSetting this parameter to false will result in the VC being created as JSON-LD Defaults to false if not specified.", + "required": false, + "schema": { + "type": "boolean", + "default": false + }, + "examples": { + "Create VC as JWT": { + "description": "Create VC as JWT", + "value": true + }, + "Do not create VC as JWT": { + "description": "Do not create VC as JWT", + "value": false + } + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "example": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "type": [ + "VerifiableCredential", + "BankDetails" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z", + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BankDetails", + "accountNumber": "123456789", + "bankName": "Dummy Bank" + } + ] + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Success Response", + "content": { + "application/json": { + "examples": { + "Success Response": { + "description": "Success Response", + "value": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#319a2641-9407-4c39-bf51-a4a109b59604", + "type": [ + "VerifiableCredential", + "BankDetails" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T13:41:52Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "bankName": "Dummy Bank", + "id": "did:web:localhost:BPNL000000000000", + "type": "BankDetails", + "accountNumber": "123456789" + } + ], + "proof": { + "proofPurpose": "proofPurpose", + "verificationMethod": "did:web:localhost:BPNL000000000000#", + "type": "JsonWebSignature2020", + "created": "2023-07-19T13:41:54Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdqaAsPhQ5xZhQiRvWliDVXX-R9NzCvFXGUAOyQ8yE1hmf_4cvxS7JFuEojjsi3V-n66iiRCUFEXsnv56XPgDA" + } + } + } + } + } + } + }, + "400": { + "description": "The input does not comply to the syntax requirements", + "content": { + "application/json": { + "examples": { + "Response in case of invalid data provided": { + "description": "Response in case of invalid data provided", + "value": { + "type": "about:blank", + "title": "Invalid data provided", + "status": 400, + "detail": "details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689760833962, + "errors": { + "filed": "filed error message" + } + } + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden access" + }, + "404": { + "description": "Wallet not found with caller BPN", + "content": { + "application/json": { + "examples": { + "Wallet not found with caller BPN": { + "description": "Wallet not found with caller BPN", + "value": { + "type": "about:blank", + "title": "Wallet not found for identifier did:web:localhost:BPNL0000000501", + "status": 404, + "detail": "Wallet not found for identifier did:web:localhost:BPNL0000000501", + "instance": "/api/wallets/did%3Aweb%3Alocalhost%3ABPNL0000000501", + "properties": { + "timestamp": 1689764377224 + } + } + } + } + } + } + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + } + }, + "/api/credentials/validation": { + "post": { + "tags": [ + "Verifiable Credential - Validation" + ], + "summary": "Validate Verifiable Credentials", + "description": "Permission: **view_wallets** OR **view_wallet** \n\n Validate Verifiable Credentials", + "operationId": "credentialsValidation", + "parameters": [ + { + "name": "withCredentialExpiryDate", + "in": "query", + "description": "Check expiry of VC", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialVerificationRequest" + }, + "examples": { + "Validate credential in JSON-LD format": { + "description": "Validate credential in JSON-LD format", + "value": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T09:11:34Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + }, + "Validate credential in JWT format": { + "description": "Validate credential in JWT format", + "value": { + "jwt": "eyJraWQiOiJkaWQ6d2ViOmFmODgtMjAzLTEyOS0yMTMtMTA3Lm5ncm9rLWZyZWUuYXBwOkJQTkwwMDAwMDAwMDAwMDAjOGYyZWU5ZDItYTM2Yy00MTM4LWJlMWYtYjZmZWZiNmY4MDI0IiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJpc3MiOiJkaWQ6d2ViOmFmODgtMjAzLTEyOS0yMTMtMTA3Lm5ncm9rLWZyZWUuYXBwOkJQTkwwMDAwMDAwMDAwMDAiLCJzdWIiOiJkaWQ6d2ViOmFmODgtMjAzLTEyOS0yMTMtMTA3Lm5ncm9rLWZyZWUuYXBwOkJQTkwwMDAwMDAwMDAwMTEiLCJleHAiOjE3MzU2Njk4MDAsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly9jb2Zpbml0eS14LmdpdGh1Yi5pby9zY2hlbWEtcmVnaXN0cnkvdjEuMS9Vc2VDYXNlVkMuanNvbiIsImh0dHBzOi8vdzNpZC5vcmcvc2VjdXJpdHkvc3VpdGVzL2p3cy0yMDIwL3YxIl0sImlkIjoiZGlkOndlYjphZjg4LTIwMy0xMjktMjEzLTEwNy5uZ3Jvay1mcmVlLmFwcDpCUE5MMDAwMDAwMDAwMDAwI2Q4Y2ZjZDBiLWY0NGQtNDVkMC05OGEzLTA4ZDZkNmU5Y2E5NSIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJVc2VDYXNlRnJhbWV3b3JrQ29uZGl0aW9uIl0sImlzc3VlciI6ImRpZDp3ZWI6YWY4OC0yMDMtMTI5LTIxMy0xMDcubmdyb2stZnJlZS5hcHA6QlBOTDAwMDAwMDAwMDAwMCIsImNyZWRlbnRpYWxTdWJqZWN0IjpbeyJob2xkZXJJZGVudGlmaWVyIjoiQlBOTDAwMDAwMDAwMDAxMSIsImlkIjoiZGlkOndlYjphZjg4LTIwMy0xMjktMjEzLTEwNy5uZ3Jvay1mcmVlLmFwcDpCUE5MMDAwMDAwMDAwMDExIiwidHlwZSI6IkJlaGF2aW9yVHdpbkNyZWRlbnRpYWwiLCJjb250cmFjdFRlbXBsYXRlIjoiaHR0cHM6Ly9wdWJsaWMuY2F0ZW5hLXgub3JnL2NvbnRyYWN0cy90cmFjZWFiaWx0eS52MS5wZGYiLCJjb250cmFjdFZlcnNpb24iOiIxLjAuMCJ9XSwiY3JlZGVudGlhbFN0YXR1cyI6bnVsbCwiaXNzdWFuY2VEYXRlIjoiMjAyNC0wMi0wOFQxNDowMjo1M1oiLCJleHBpcmF0aW9uRGF0ZSI6IjIwMjQtMTItMzFUMTg6MzA6MDBaIn0sImp0aSI6IjliYWFhMjIzLTAxMjctNDEyZS05NjZhLTA3ZTJmZGU4NGNlNCJ9.X3rkj8Gv4OD5nEaeFG5pSA-dogbcYA91YEPmHiKT4FhAiIr7QAdSEULGXHYOn8-eK0jSDHNdAxNYIK1UwYRsCA" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Validate Verifiable Credentials", + "content": { + "application/json": { + "examples": { + "Verifiable Credentials without check expiry": { + "description": "Verifiable Credentials without check expiry", + "value": { + "valid": true, + "vc": { + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + }, + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z" + } + } + }, + "Verifiable Credentials with check expiry": { + "description": "Verifiable Credentials with check expiry", + "value": { + "valid": true, + "validateExpiryDate": true, + "vc": { + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + }, + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z" + } + } + }, + "Verifiable expired credentials with check expiry ": { + "description": "Verifiable expired credentials with check expiry ", + "value": { + "valid": false, + "validateExpiryDate": false, + "vc": { + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + }, + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2022-12-31T18:30:00Z" + } + } + }, + "Revocable Verifiable credentials with check expiry ": { + "description": "Revocable Verifiable credentials with check expiry ", + "value": { + "credentialStatus": "active", + "valid": true, + "validateExpiryDate": true, + "vc": { + "credentialSubject": [ + { + "holderIdentifier": "BPNL000000000001", + "allowedVehicleBrands": [ + "Audi", + "Abarth", + "Alfa Romeo", + "Chrysler" + ], + "id": "did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000001", + "activityType": "vehicleDismantle", + "type": "DismantlerCredential" + } + ], + "issuanceDate": "2024-01-05T05:42:53Z", + "id": "did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000000#8507aa50-b2a4-4532-8e45-f50e7654b23b", + "proof": { + "proofPurpose": "assertionMethod", + "verificationMethod": "did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000000#a39d8ccf-2a66-488d-bfec-916768082e91", + "type": "JsonWebSignature2020", + "created": "2024-01-05T05:42:53Z", + "jws": "eyJhbGciOiJFZERTQSJ9..15NdxA8L_Iw7Igxevm7YGMAQA-Kt6PMOpix6p0jaYHCtfQnTy3q61SDvsnsltGT6fzM90JOubOuig2WFy-GPDg" + }, + "type": [ + "VerifiableCredential", + "DismantlerCredential" + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://cofinity-x.github.io/schema-registry/v1.1/DismantlerVC.json", + "https://w3id.org/security/suites/jws-2020/v1", + "https://w3id.org/vc/status-list/2021/v1" + ], + "issuer": "did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000000", + "credentialStatus": { + "id": "did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000000#0", + "statusPurpose": "revocation", + "statusListIndex": "0", + "statusListCredential": "https://ae96-203-129-213-107.ngrok-free.app/api/v1/revocations/credentials?issuerId=did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000000", + "type": "StatusList2021Entry" + }, + "expirationDate": "2024-12-31T18:30:00Z" + } + } + }, + "Verifiable Credentials with invalid signature": { + "description": "Verifiable Credentials with invalid signature", + "value": { + "valid": false, + "vc": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z", + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhf", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden access" + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + } + }, + "/api/credentials/issuer": { + "get": { + "tags": [ + "Verifiable Credential - Issuer" + ], + "summary": "Query Verifiable Credentials", + "description": "Permission: **view_wallets** (The BPN of holderIdentifier must equal BPN of caller)\n\n Search verifiable credentials with filter criteria", + "operationId": "getCredentials_1", + "parameters": [ + { + "name": "credentialId", + "in": "query", + "description": "Credential Id", + "required": false, + "schema": { + "type": "string" + }, + "examples": { + "Credential Id": { + "description": "Credential Id", + "value": "did:web:localhost:BPNL000000000000#12528899-160a-48bd-ba15-f396c3959ae9" + } + } + }, + { + "name": "holderIdentifier", + "in": "query", + "description": "Holder identifier(did of BPN)", + "required": false, + "schema": { + "type": "string" + }, + "examples": { + "bpn": { + "description": "bpn", + "value": "BPNL000000000001" + }, + "did": { + "description": "did", + "value": "did:web:localhost:BPNL000000000001" + } + } + }, + { + "name": "type", + "in": "query", + "description": "Type of VC", + "required": false, + "schema": { + "type": "array", + "maxItems": 100, + "items": { + "type": "string" + } + }, + "examples": { + "SummaryCredential": { + "description": "SummaryCredential", + "value": "SummaryCredential" + }, + "BpnCredential": { + "description": "BpnCredential", + "value": "BpnCredential" + } + } + }, + { + "name": "pageNumber", + "in": "query", + "description": "Page number, Page number start with zero", + "required": false, + "schema": { + "maximum": 2147483647, + "minimum": 0, + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "size", + "in": "query", + "description": "Number of records per page", + "required": false, + "schema": { + "maximum": 2147483647, + "minimum": 0, + "type": "integer", + "format": "int32", + "default": 2147483647 + } + }, + { + "name": "sortColumn", + "in": "query", + "description": "Sort column name", + "required": false, + "schema": { + "type": "string", + "default": "createdAt" + }, + "examples": { + "creation date": { + "description": "creation date", + "value": "createdAt" + }, + "Holder did": { + "description": "Holder did", + "value": "holderDid" + }, + "Credential type": { + "description": "Credential type", + "value": "type" + }, + "Credential id": { + "description": "Credential id", + "value": "credentialId" + } + } + }, + { + "name": "sortTpe", + "in": "query", + "description": "Sort order", + "required": false, + "schema": { + "type": "string", + "default": "desc" + }, + "examples": { + "Ascending order": { + "description": "Ascending order", + "value": "asc" + }, + "Descending order": { + "description": "Descending order", + "value": "desc" + } + } + }, + { + "name": "asJwt", + "in": "query", + "description": "Specifies whether the VC (Verifiable Credential) should be created as a JWT (JSON Web Token). If set to true, the VC will be generated in JWT formatSetting this parameter to false will result in the VC being created as JSON-LD Defaults to false if not specified.", + "required": false, + "schema": { + "type": "boolean", + "default": false + }, + "examples": { + "Create VC as JWT": { + "description": "Create VC as JWT", + "value": true + }, + "Do not create VC as JWT": { + "description": "Do not create VC as JWT", + "value": false + } + } + } + ], + "responses": { + "200": { + "description": "Issuer credential list", + "content": { + "application/json": { + "examples": { + "Issuer credential list": { + "description": "Issuer credential list", + "value": { + "content": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#ae364f71-f054-4d91-b579-f001bcb3e59e", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T09:27:42Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:27:44Z", + "jws": "eyJhbGciOiJFZERTQSJ9..evDHQfW4EzJUt2HnS_WlmO8FFtywTGnwyywtCE7WP41my4Iscpqr4tbuVOqnZg85b4U8L3_ut8_pEONIhbExCQ", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + }, + { + "type": [ + "VerifiableCredential", + "SummaryCredential" + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/SummaryVC.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T09:11:39Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "contractTemplate": "https://public.catena-x.org/contracts/", + "holderIdentifier": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "items": [ + "BpnCredential" + ], + "type": "SummaryCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:41Z", + "jws": "eyJhbGciOiJFZERTQSJ9..YvoFhDip3TQAfZUIu0yc843oA4uGTg049dMFt_GoaMmPjiNB_B1EFOL-gDpwjIxTYNlGOO_CLp9qStbzlDTNBg", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + }, + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T09:11:34Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + ], + "pageable": { + "sort": { + "empty": false, + "unsorted": false, + "sorted": true + }, + "offset": 0, + "pageNumber": 0, + "pageSize": 2147483647, + "paged": true, + "unpaged": false + }, + "last": true, + "totalPages": 1, + "totalElements": 3, + "first": true, + "size": 2147483647, + "number": 0, + "sort": { + "empty": false, + "unsorted": false, + "sorted": true + }, + "numberOfElements": 3, + "empty": false + } + } + } + } + } + }, + "400": { + "description": "The input does not comply to the syntax requirements", + "content": { + "application/json": { + "examples": { + "Response in case of invalid data provided": { + "description": "Response in case of invalid data provided", + "value": { + "type": "about:blank", + "title": "title", + "status": 400, + "detail": "details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689760833962, + "errors": {} + } + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden access" + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + }, + "post": { + "tags": [ + "Verifiable Credential - Issuer" + ], + "summary": "Issue Verifiable Credential", + "description": "Permission: **update_wallets** (The BPN of the base wallet must equal BPN of caller)\nIssue a verifiable credential with a given issuer DID", + "operationId": "issueCredentialUsingBaseWallet", + "parameters": [ + { + "name": "holderDid", + "in": "query", + "description": "Holder DID", + "required": true, + "schema": { + "type": "string" + }, + "examples": { + "did": { + "description": "did", + "value": "did:web:localhost:BPNL000000000000" + } + } + }, + { + "name": "asJwt", + "in": "query", + "description": "Specifies whether the VC (Verifiable Credential) should be created as a JWT (JSON Web Token). If set to true, the VC will be generated in JWT formatSetting this parameter to false will result in the VC being created as JSON-LD Defaults to false if not specified.", + "required": false, + "schema": { + "type": "boolean", + "default": false + }, + "examples": { + "Create VC as JWT": { + "description": "Create VC as JWT", + "value": true + }, + "Do not create VC as JWT": { + "description": "Do not create VC as JWT", + "value": false + } + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "example": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T09:11:34Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ] + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Issuer credential", + "content": { + "application/json": { + "examples": { + "Issuer credential": { + "description": "Issuer credential", + "value": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#ff084e7a-1b46-4a2f-a78d-3d701a0bd6e4", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T12:18:30Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#", + "created": "2023-07-19T12:18:34Z", + "jws": "eyJhbGciOiJFZERTQSJ9..0Ua1vcTQAYwQY3PPuHr4RQxqW6iIngrHQQx1oPgk2uzqUpcbfY2YUxXAnbNA333-lSuvNhiV_1NLfBnCEcI2DQ" + } + } + } + } + } + } + }, + "400": { + "description": "The input does not comply to the syntax requirements", + "content": { + "application/json": { + "examples": { + "Response in case of invalid data provided": { + "description": "Response in case of invalid data provided", + "value": { + "type": "about:blank", + "title": "Invalid data provided", + "status": 400, + "detail": "details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689760833962, + "errors": { + "filed": "filed error message" + } + } + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden access" + }, + "404": { + "description": "Wallet not found with provided identifier", + "content": { + "application/json": { + "examples": { + "Wallet not found with provided identifier": { + "description": "Wallet not found with provided identifier", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 404, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + } + }, + "/{bpn}/did.json": { + "get": { + "tags": [ + "DIDDocument" + ], + "summary": "Resolve DID Document", + "description": "Resolve the DID document for a given BPN", + "operationId": "getDidResolve", + "parameters": [ + { + "name": "bpn", + "in": "path", + "description": "BPN", + "required": true, + "schema": { + "type": "string" + }, + "examples": { + "bpn": { + "description": "bpn", + "value": "BPNL000000000000" + } + } + } + ], + "responses": { + "200": { + "description": "DID document", + "content": { + "application/json": { + "examples": { + " DID document": { + "description": " DID document", + "value": { + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3c.github.io/vc-jws-2020/contexts/v1" + ], + "id": "did:web:localhost:BPNL000000000000", + "verificationMethod": [ + { + "controller": "did:web:localhost:BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "publicKeyJwk": { + "crv": "Ed25519", + "kty": "OKP", + "x": "wAOQvr92L1m7RwrpeOrgWByVYvWmhRr4fJbiMwHEIdY" + }, + "type": "JsonWebKey2020" + } + ] + } + } + } + } + } + }, + "404": { + "description": "Wallet not found with provided bpn", + "content": { + "application/json": { + "examples": { + "Wallet not found with provided bpn": { + "description": "Wallet not found with provided bpn", + "value": { + "type": "about:blank", + "title": "Wallet not found for identifier BPNL00000000000", + "status": 404, + "detail": "Wallet not found for identifier BPNL00000000000", + "instance": "/BPNL00000000000/did.json", + "properties": { + "timestamp": 1689767698010 + } + } + } + } + } + } + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + } + }, + "/api/wallets/{identifier}": { + "get": { + "tags": [ + "Wallets" + ], + "summary": "Retrieve wallet by BPN", + "description": "Permission: **view_wallets** OR **view_wallet** (The BPN of Wallet to retrieve must equal the BPN of caller or Base wallet, authority wallet can see all wallets) \n\n Retrieve single wallet by identifier, with or without its credentials", + "operationId": "getWalletByIdentifier", + "parameters": [ + { + "name": "identifier", + "in": "path", + "description": "Did or BPN", + "required": true, + "schema": { + "type": "string" + }, + "examples": { + "bpn": { + "description": "bpn", + "value": "BPNL000000000000" + }, + "did": { + "description": "did", + "value": "did:web:localhost:BPNL000000000000" + } + } + }, + { + "name": "withCredentials", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "Wallet Details", + "content": { + "application/json": { + "examples": { + "Wallet details without with credentials false": { + "description": "Wallet details without with credentials false", + "value": { + "name": "companyA", + "did": "did:web:localhost:BPNL000000000001", + "bpn": "BPNL000000000001", + "algorithm": "ED25519", + "didDocument": { + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3c.github.io/vc-jws-2020/contexts/v1" + ], + "id": "did:web:localhost:BPNL000000000001", + "verificationMethod": [ + { + "controller": "did:web:localhost:BPNL000000000001", + "id": "did:web:localhost:BPNL000000000001#", + "publicKeyJwk": { + "crv": "Ed25519", + "kty": "OKP", + "x": "mhph0ZSVk7cDVmazbaaC3jBDpphW4eNygAK9gHPlMow" + }, + "type": "JsonWebKey2020" + } + ] + } + } + }, + "Wallet details without with credentials true": { + "description": "Wallet details without with credentials true", + "value": { + "name": "companyA", + "did": "did:web:localhost:BPNL000000000001", + "bpn": "BPNL000000000001", + "algorithm": "ED25519", + "didDocument": { + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3c.github.io/vc-jws-2020/contexts/v1" + ], + "id": "did:web:localhost:BPNL000000000001", + "verificationMethod": [ + { + "controller": "did:web:localhost:BPNL000000000001", + "id": "did:web:localhost:BPNL000000000001#", + "publicKeyJwk": { + "crv": "Ed25519", + "kty": "OKP", + "x": "mhph0ZSVk7cDVmazbaaC3jBDpphW4eNygAK9gHPlMow" + }, + "type": "JsonWebKey2020" + } + ] + }, + "verifiableCredentials": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#a1f8ae36-9919-4ed8-8546-535280acc5bf", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T09:14:45Z", + "expirationDate": "2023-09-30T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000001", + "id": "did:web:localhost:BPNL000000000001", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:14:47Z", + "jws": "eyJhbGciOiJFZERTQSJ9..O69dLGMDVgZQJ7chFx3aUbkJFvibH8WWunw634rIDC77_pdiUHvQpQ0hq15_7OgFMy3dp-9H-pNgxTZ-i4UXCw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + ] + } + } + } + } + } + }, + "400": { + "description": "The input does not comply to the syntax requirements", + "content": { + "application/json": { + "examples": { + "Response in case of invalid data provided": { + "description": "Response in case of invalid data provided", + "value": { + "type": "about:blank", + "title": "title", + "status": 400, + "detail": "details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689760833962, + "errors": {} + } + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden access" + }, + "404": { + "description": "Wallet not found with provided identifier", + "content": { + "application/json": { + "examples": { + "Wallet not found with provided identifier": { + "description": "Wallet not found with provided identifier", + "value": { + "type": "about:blank", + "title": "Wallet not found for identifier did:web:localhost:BPNL0000000501", + "status": 404, + "detail": "Wallet not found for identifier did:web:localhost:BPNL0000000501", + "instance": "/api/wallets/did%3Aweb%3Alocalhost%3ABPNL0000000501", + "properties": { + "timestamp": 1689764377224 + } + } + } + } + } + } + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + } + }, + "/api/presentations/iatp": { + "get": { + "tags": [ + "Verifiable Presentations - Generation" + ], + "summary": "Create Verifiable Presentation", + "description": "Create a verifiable presentation for the verifiable credential types listed in STS token", + "operationId": "createPresentation_1", + "parameters": [ + { + "name": "asJwt", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "Verifiable Presentation", + "content": { + "application/json": { + "examples": { + "VP as Json-LD": { + "description": "VP as Json-LD", + "value": { + "vp": { + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "id": "did:web:localhost:BPNL000000000000#b2e69e47-95f3-48ff-af30-eaaab36431d5", + "type": [ + "VerifiablePresentation" + ], + "verifiableCredential": [ + { + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z", + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + ] + } + } + }, + "VP as JWT": { + "description": "VP as JWT", + "value": { + "vp": "eyJraWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJzdWIiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwiYXVkIjoic21hcnQiLCJpc3MiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwidnAiOnsiaWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIzM4ZTU2ZTg1LTNkODQtNGEyNS1iZjg1LWFiMjRlYzY4MmMwOSIsInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vY2F0ZW5heC1uZy5naXRodWIuaW8vcHJvZHVjdC1jb3JlLXNjaGVtYXMvYnVzaW5lc3NQYXJ0bmVyRGF0YS5qc29uIiwiaHR0cHM6Ly93M2lkLm9yZy9zZWN1cml0eS9zdWl0ZXMvandzLTIwMjAvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIkJwbkNyZWRlbnRpYWwiXSwiaWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwI2Y3M2UzNjMxLWJhODctNGEwMy1iZWEzLWIyODcwMDA1Njg3OSIsImlzc3VlciI6ImRpZDp3ZWI6bG9jYWxob3N0OkJQTkwwMDAwMDAwMDAwMDAiLCJpc3N1YW5jZURhdGUiOiIyMDIzLTA3LTE5VDA5OjExOjM0WiIsImV4cGlyYXRpb25EYXRlIjoiMjAyNC0xMi0zMVQxODozMDowMFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDp3ZWI6bG9jYWxob3N0OkJQTkwwMDAwMDAwMDAwMDAiLCJicG4iOiJCUE5MMDAwMDAwMDAwMDAwIiwidHlwZSI6IkJwbkNyZWRlbnRpYWwifSwicHJvb2YiOnsicHJvb2ZQdXJwb3NlIjoicHJvb2ZQdXJwb3NlIiwidHlwZSI6Ikpzb25XZWJTaWduYXR1cmUyMDIwIiwidmVyaWZpY2F0aW9uTWV0aG9kIjoiZGlkOndlYjpsb2NhbGhvc3Q6QlBOTDAwMDAwMDAwMDAwMCMiLCJjcmVhdGVkIjoiMjAyMy0wNy0xOVQwOToxMTozOVoiLCJqd3MiOiJleUpoYkdjaU9pSkZaRVJUUVNKOS4uZmRuMnFVODVhdU9sdGRIRExkSEk3c0pWVjFaUGRmdHBpWGRfbmRYTjBkRmdTRFdpSXJTY2REMDN3dHZLTHFfSC1zaFFXZmgyUlllTW1ybEV6QWhmRHcifX19LCJleHAiOjE2ODk4MzQ4MDUsImp0aSI6ImIwODYzOWZiLWQ5MWEtNGUwZS1iNmY4LTYzYjdhMzQ1ZTRhZiJ9.80x0AB-OauefdeZfx1cwhitdVKRvCRFeFzYwU73DL7y4w34vu6BdfHWLBGjkwELxkQEoFfiTPOqtuyqhtsyDBg" + } + } + } + } + } + }, + "401": { + "description": "The request could not be completed due to a failed authorization." + }, + "403": { + "description": "The request could not be completed due to a forbidden scope value" + }, + "404": { + "description": "One or more of the requested verifiable credential types were not found", + "content": { + "application/json": { + "examples": { + "One or more of the requested verifiable credential types were not found": { + "description": "One or more of the requested verifiable credential types were not found", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 404, + "detail": "Verifiable credential types that were not found", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "sts_token": [] + } + ] + } + }, + "/api/didDocuments/{identifier}": { + "get": { + "tags": [ + "DIDDocument" + ], + "summary": "Resolve DID Document", + "description": "Resolve the DID document for a given DID or BPN", + "operationId": "getDidDocument", + "parameters": [ + { + "name": "identifier", + "in": "path", + "description": "Did or BPN", + "required": true, + "schema": { + "type": "string" + }, + "examples": { + "bpn": { + "description": "bpn", + "value": "BPNL000000000000" + }, + "did": { + "description": "did", + "value": "did:web:localhost:BPNL000000000000" + } + } + } + ], + "responses": { + "200": { + "description": "DID document", + "content": { + "application/json": { + "examples": { + " DID document": { + "description": " DID document", + "value": { + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3c.github.io/vc-jws-2020/contexts/v1" + ], + "id": "did:web:localhost:BPNL000000000000", + "verificationMethod": [ + { + "controller": "did:web:localhost:BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "publicKeyJwk": { + "crv": "Ed25519", + "kty": "OKP", + "x": "wAOQvr92L1m7RwrpeOrgWByVYvWmhRr4fJbiMwHEIdY" + }, + "type": "JsonWebKey2020" + } + ] + } + } + } + } + } + }, + "404": { + "description": "Wallet not found with provided bpn", + "content": { + "application/json": { + "examples": { + "Wallet not found with provided bpn": { + "description": "Wallet not found with provided bpn", + "value": { + "type": "about:blank", + "title": "Wallet not found for identifier BPNL00000000000", + "status": 404, + "detail": "Wallet not found for identifier BPNL00000000000", + "instance": "/BPNL00000000000/did.json", + "properties": { + "timestamp": 1689767698010 + } + } + } + } + } + } + }, + "500": { + "description": "Any other internal server error", + "content": { + "application/json": { + "examples": { + "Internal server error": { + "description": "Internal server error", + "value": { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + } + } + } + } + } + }, + "security": [ + { + "Authenticate using access_token": [] + } + ] + } + } + }, + "components": { + "schemas": { + "CreateWalletRequest": { + "required": [ + "businessPartnerNumber", + "companyName", + "didUrl" + ], + "type": "object", + "properties": { + "businessPartnerNumber": { + "pattern": "^(BPN)(L|S|A)[0-9A-Z]{12}", + "type": "string" + }, + "companyName": { + "maxLength": 255, + "minLength": 1, + "type": "string" + }, + "didUrl": { + "maxLength": 2000, + "minLength": 1, + "type": "string" + } + } + }, + "SecureTokenRequest": { + "type": "object", + "properties": { + "audience": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "client_secret": { + "type": "string" + }, + "grant_type": { + "type": "string" + }, + "access_token": { + "type": "string" + }, + "bearer_access_alias": { + "type": "string" + }, + "bearer_access_scope": { + "type": "string" + } + } + }, + "CredentialVerificationRequest": { + "type": "object", + "properties": { + "jwt": { + "type": "string", + "writeOnly": true + }, + "vc": { + "type": "object", + "additionalProperties": { + "type": "object" + }, + "writeOnly": true + }, + "empty": { + "type": "boolean" + } + }, + "additionalProperties": { + "type": "object" + } + } + }, + "securitySchemes": { + "Authenticate using access_token": { + "type": "apiKey", + "description": "**Bearer (apiKey)**\nJWT Authorization header using the Bearer scheme.\nEnter **Bearer** [space] and then your token in the text input below:\nExample: Bearer 12345abcdef\n", + "name": "Authorization", + "in": "header" + }, + "sts_token": { + "type": "apiKey", + "description": "**STS token**\nJWT Authorization header.\nEnter your token in the text input below:\nExample: 12345abcdef\n", + "name": "Authorization", + "in": "header" + } + } + } +} \ No newline at end of file diff --git a/docs/arc42/main.md b/docs/arc42/main.md index a56720345..ba5f7fe2f 100644 --- a/docs/arc42/main.md +++ b/docs/arc42/main.md @@ -165,167 +165,18 @@ end box group "Create Wallet" box "Create Wallet" user -> MIW: "/api/wallet" with BPN and Name - group "Wallet Creation" + group "Wallet Creation" MIW -> MIW: Create Database entry MIW -> MIW: Create Private and Public Key MIW -> MIW: Store Private Key AES encrypted in DB MIW -> MIW: Create DID:web Document MIW -> MIW: Store DID-Document end group - group "BPN Credential" - MIW -> MIW: Create BPN Credential - MIW -> MIW: Sign JSON-LD BPN Credential with issuer private key (Private Key of Issuer Wallet) - MIW -> MIW: Store BPN Credential - end group - group "Summary Credential" - MIW -> MIW: Access User Wallet - MIW -> MIW: Check if Summary Credential is already Created - MIW -> MIW: Check BPN Credential is not already in Summary Credential - MIW -> MIW: Create Summary Credential with BPN - MIW -> MIW: Store Summary Credential in Issuer Wallet - MIW -> MIW: Store Summary Credential in Holder Wallet - end group MIW --> user: Return Wallet end box end group ``` -### Issue Membership Credential - -```plantuml -title Issue Membership Credential - -actor User as User - -participant PortalIDP as keycloak -participant ManagedIdentityWallet as MIW - -box "Get Accesstoken" - User -> keycloak: Get AccessToken - keycloak --> User: AccessToken -end box - -group "Issue Membership" - User -> MIW: "/api/credentials/issuer/membership" with BPN - group "Create Membership Credential" - MIW -> MIW: Create Use Case Credential - MIW -> MIW: Sign JSON-LD Use Case Credential with issuer private key (Private Key of Issuer Wallet) - MIW -> MIW: Store Credential in Issuer Wallet - MIW -> MIW: Store Credential in Holder Wallet - end group - group "Summary Credential" - MIW -> MIW: Access User Wallet - MIW -> MIW: Check if Summary Credential is already Created - MIW -> MIW: Check Membership Credential is not already in Summary Credential - MIW -> MIW: Delete Summary Credential in User Wallet - MIW -> MIW: Create Summary Credential with specific Use Case - MIW -> MIW: Store Summary Credential in Issuer Wallet - MIW -> MIW: Store Summary Credential in Holder Wallet - end group - MIW --> User: Return signed Membership Credential -end group -``` - -### Issue Usecase Credential - -```plantuml -title Issue UseCaseFrameworkCredential - -actor User as User - -participant PortalIDP as keycloak -participant ManagedIdentityWallet as MIW - -box "Get Accesstoken" - User -> keycloak: Get AccessToken - keycloak --> User: AccessToken -end box - -group "Issue UseCaseCredential" - User -> MIW: "/api/credentials/issuer/framework" with (BPN, Type, ContractVersion, ContractTemplate) - group "Use Case Credential" - MIW -> MIW: Create Use Case Credential - MIW -> MIW: Sign JSON-LD Use Case Credential with issuer private key (Private Key of Issuer Wallet) - MIW -> MIW: Store Credential in Issuer Wallet - end group - group "Summary Credential" - MIW -> MIW: Access User Wallet - MIW -> MIW: Check if Summary Credential is already Created - MIW -> MIW: Check Use Case Credential is not already in Summary Credential - MIW -> MIW: If not delete Summary Credential in User Wallet - MIW -> MIW: Create Summary Credential with specific Use Case - MIW -> MIW: Store Summary Credential in Issuer Wallet - MIW -> MIW: Store Summary Credential in Holder Wallet - end group - MIW --> User: Return signed Use Case Credential -end group -``` - -### Issue Dismantler Credential - -```plantuml -title Issue Dismantler Credential - -actor User as User - -participant PortalIDP as keycloak -participant ManagedIdentityWallet as MIW - -box "Get Accesstoken" - User -> keycloak: Get AccessToken - keycloak --> User: AccessToken -end box - -group "Issue Dismantler Credential" - User -> MIW: "/api/credentials/issuer/dismantler" with bpn, activityType, allowedVehicleBrands - group "Create Dismantler Credential" - MIW -> MIW: Create Dismantler Credential - MIW -> MIW: Sign JSON-LD Dismantler Credential with issuer private key (Private Key of Issuer Wallet) - MIW -> MIW: Store Credential in Issuer Wallet - MIW -> MIW: Store Credential in Holder Wallet - end group - group "Summary Credential" - MIW -> MIW: Access User Wallet - MIW -> MIW: Check if Summary Credential is already Created - MIW -> MIW: Check Dismantler Credential is not already in Summary Credential - MIW -> MIW: Delete Summary Credential in User Wallet - MIW -> MIW: Create Summary Credential with Dismantler added - MIW -> MIW: Store Summary Credential in Issuer Wallet - MIW -> MIW: Store Summary Credential in Holder Wallet - end group - MIW --> User: Return signed Dismantler Credential -end group -``` - -### Fetch Summary Verifiable Presentation - -```plantuml -title Fetch SummaryVP - -actor User as User - -participant PortalIDP as keycloak -participant ManagedIdentityWallet as MIW - -box "Get Accesstoken" - User -> keycloak: Get AccessToken - keycloak --> User: AccessToken -end box - -group "Get Summary VP" - group "Get Summary Credential" - User -> MIW: "/api/credentials?type=['SummaryCredential']" - MIW -> MIW: Lookup Credential in Wallet with Type - MIW --> User: Return Credential(s) with Type - end group - group "Create Summary Presentation" - User -> MIW: "/api/presentations?withAudience=['Audience1','Audience2']+asJwt=true" - MIW -> MIW: Issue VP with Audience as JWT - MIW --> User: Return signed Presentation - end group -end group -``` - ### Validate Verifiable Presentation ```plantuml @@ -343,7 +194,7 @@ end box group "Verify/Validate Verifiable Presentation" User -> MIW: "/api/presentations/validation?withDateValidation=true" with VP - group "Presentation Validation" + group "Presentation Validation" MIW -> MIW: Validate Presentation JsonLD MIW -> MIW: Verify Presentation Signature end group @@ -420,354 +271,6 @@ examples: - `[uuid]` is a UUIDv4 type of UUID, e.g. `f01d7219-d1aa-48c6-beaa-9e433e80ac79` - `[IRI]` is a URL-type of ID, but with extended characters, e.g. `"https://example.com/credentials/123" -#### BPN Credential - -
-{
-    "@context": [
-        "https://www.w3.org/2018/credentials/v1",
-        "https://w3id.org/security/suites/jws-2020/v1",
-        "https://raw.githubusercontent.com/catenax-ng/product-core-schemas/main/businessPartnerData"
-    ],
-    "id": "[uuid]",
-    "type": [
-        "VerifiableCredential",
-        "BpnCredential"
-    ],
-    "issuer": "[did]",
-    "issuanceDate": "[iso8601-timestamp]",
-    "credentialSubject": {
-        "id": "[did]"
-        "type": "BpnCredential",
-        "bpn": "[bpn]"
-    }
-}
-
- -#### Behavior Twin Use Case Credential - -
-{
-    "@context": [
-        "https://www.w3.org/2018/credentials/v1",
-        "https://w3id.org/security/suites/jws-2020/v1",
-        "https://raw.githubusercontent.com/catenax-ng/product-core-schemas/main/UseCaseVC"
-    ],
-    "id": "[IRI]",
-    "issuer": "[did]",
-    "type": [
-        "VerifiableCredential",
-        "UseCaseFrameworkCondition"
-    ],
-    "issuanceDate": "[iso8601-timestamp]",
-    "expirationDate": "[iso8601-timestamp]",
-    "credentialSubject": {
-        "id": "[did]",
-        "holderIdentifier": "[bpn]",
-        "usecase-agreement": {
-            "value": "Behavior Twin",
-            "type": "cx-behavior-twin",
-            "contract-template": "https://public.catena-x.org/contracts/behavior_twin.v1.pdf",
-            "contract-version": "1.0.0"
-        }
-    },
-    "proof": {
-        "type": "JsonWebSignature2020",
-        "created": "[iso8601-timestamp]",
-        "jws": "[jws]",
-        "proofPurpose": "assertionMethod",
-        "verificationMethod": "[did#key-id]"
-    }
-}
-
- -#### Membership Credential - -Attestation of membership, currently used for Catena-X membership - -
-{
-    "@context": [
-        "https://www.w3.org/2018/credentials/v1",
-        "https://w3id.org/security/suites/jws-2020/v1",
-        "https://raw.githubusercontent.com/catenax-ng/product-core-schemas/main/businessPartnerData"
-    ],
-    "id": "[uuid]",
-    "type": [
-        "VerifiableCredential",
-        "MembershipCredential"
-    ],
-    "issuanceDate": "[iso8601-timestamp]",
-    "expirationDate": "[iso8601-timestamp]",
-    "issuer": "[did]",
-    "credentialSubject": {
-        "id": "[did]"
-        "type": "MembershipCredential",
-        "holderIdentifier": "[bpn]",
-        "memberOf": "Catena-X",
-        "status": "Active",
-        "startTime": "[iso8601-timestamp]",
-    }
-}
-
- -#### Dismantler Credential - -
-{
-    "@context": [
-        "https://www.w3.org/2018/credentials/v1",
-        "https://w3id.org/security/suites/jws-2020/v1",
-        "https://raw.githubusercontent.com/catenax-ng/product-core-schemas/main/businessPartnerData"
-    ],
-    "id": "[uuid]",
-    "issuer": "[did]",
-    "type": [
-        "VerifiableCredential",
-        "DismantlerCredential"
-    ],
-    "issuanceDate": "[iso8601-timestamp]",
-    "expirationDate": "[iso8601-timestamp]",
-    "credentialSubject": {
-        "id": "[did]",
-        "holderIdentifier": "[bpn]",
-        "allowedVehicleBrands": [
-            "[brand 1]",
-            "[brand 2]",
-            "[brand 3]"
-        ]
-    },
-    "proof": {
-        "type": "JsonWebSignature2020",
-        "created": "[iso8601-timestamp]",
-        "jws": "[jws]",
-        "proofPurpose": "assertionMethod",
-        "verificationMethod": "[did#key-id]"
-    }
-}
-
- -#### PCF Use Case Credential - -
-{
-    "@context": [
-        "https://www.w3.org/2018/credentials/v1",
-        "https://www.w3.org/2018/credentials/examples/v1",
-        "https://raw.githubusercontent.com/catenax-ng/product-core-schemas/main/UseCaseVC"
-    ],
-    "id": "[uuid]",
-    "issuer": "[did]",
-    "type": [
-        "VerifiableCredential",
-        "UseCaseFrameworkCondition"
-    ],
-    "issuanceDate": "[iso8601-timestamp]",
-    "expirationDate": "[iso8601-timestamp]", //Optional field
-    "credentialSubject": {
-        "id": "[did]",
-        "holderIdentifier": "[bpn]",
-        "usecaseAgreement": {
-            "value": "PCF",
-            "type": "cx-pcf",
-            "contract-template": "https://public.catena-x.org/contracts/pcf.v1.pdf",
-            "contract-version": "1.0.0"
-        }
-    },
-    "proof": {
-        "type": "JsonWebSignature2020",
-        "created": "[iso8601-timestamp]",
-        "jws": "[jws]",
-        "proofPurpose": "assertionMethod",
-        "verificationMethod": "[did#key-id]"
-    }
-}
-
- -#### Quality Use Case Credential - -
-{
-    "@context": [
-        "https://www.w3.org/2018/credentials/v1",
-        "https://w3id.org/security/suites/jws-2020/v1",
-        "https://raw.githubusercontent.com/catenax-ng/product-core-schemas/main/UseCaseVC"
-    ],
-    "id": "[uuid]",
-    "issuer": "[did]",
-    "type": [
-        "VerifiableCredential",
-        "UseCaseFrameworkCondition"
-    ],
-    "issuanceDate": "[iso8601-timestamp]",
-    "expirationDate": "[iso8601-timestamp]",
-    "credentialSubject": {
-        "id": "[did]",
-        "holderIdentifier": "[bpn]",
-        "usecase-agreement": {
-            "value": "Quality",
-            "type": "cx-quality",
-            "contract-template": "https://public.catena-x.org/contracts/quality.v1.pdf",
-            "contract-version": "1.0.0"
-        }
-    },
-    "proof": {
-        "type": "JsonWebSignature2020",
-        "created": "[iso8601-timestamp]",
-        "jws": "[jws]",
-        "proofPurpose": "assertionMethod",
-        "verificationMethod": "[did#key-id]"
-    }
-}
-
- -#### Resiliency Use Case Credential - -
-{
-    "@context": [
-        "https://www.w3.org/2018/credentials/v1",
-        "https://w3id.org/security/suites/jws-2020/v1",
-        "https://raw.githubusercontent.com/catenax-ng/product-core-schemas/main/UseCaseVC"
-    ],
-    "id": "[uuid]",
-    "issuer": "[did]",
-    "type": [
-        "VerifiableCredential",
-        "UseCaseFrameworkCondition"
-    ],
-    "issuanceDate": "[iso8601-timestamp]",
-    "expirationDate": "[iso8601-timestamp]",
-    "credentialSubject": {
-        "id": "[did]",
-        "holderIdentifier": "[bpn]",
-        "usecase-agreement": {
-            "value": "Resiliency",
-            "type": "cx-resiliency",
-            "contract-template": "https://public.catena-x.org/contracts/resiliency.v1.pdf",
-            "contract-version": "1.0.0"
-        }
-    },
-    "proof": {
-        "type": "JsonWebSignature2020",
-        "created": "[iso8601-timestamp]",
-        "jws": "[jws]",
-        "proofPurpose": "assertionMethod",
-        "verificationMethod": "[did#key-id]"
-    }
-}
-
- -#### Sustainability Use Case Credential - -
-{
-    "@context": [
-        "https://www.w3.org/2018/credentials/v1",
-        "https://w3id.org/security/suites/jws-2020/v1",
-        "https://raw.githubusercontent.com/catenax-ng/product-core-schemas/main/UseCaseVC"
-    ],
-    "id": "[uuid]",
-    "issuer": "[did]",
-    "type": [
-        "VerifiableCredential",
-        "UseCaseFrameworkCondition"
-    ],
-    "issuanceDate": "[iso8601-timestamp]",
-    "expirationDate": "[iso8601-timestamp]",
-    "credentialSubject": {
-        "id": "[did]",
-        "holderIdentifier": "[bpn]",
-        "usecase-agreement": {
-            "value": "Sustainability",
-            "type": "cx-sustainability",
-            "contract-template": "https://public.catena-x.org/contracts/sustainability.v1.pdf",
-            "contract-version": "1.0.0"
-        }
-    },
-    "proof": {
-        "type": "JsonWebSignature2020",
-        "created": "[iso8601-timestamp]",
-        "jws": "[jws]",
-        "proofPurpose": "assertionMethod",
-        "verificationMethod": "[did#key-id]"
-    }
-}
-
- -#### Trace Use Case Credential - -
-{
-    "@context": [
-        "https://www.w3.org/2018/credentials/v1",
-        "https://w3id.org/security/suites/jws-2020/v1",
-        "https://raw.githubusercontent.com/catenax-ng/product-core-schemas/main/UseCaseVC"
-    ],
-    "id": "[uuid]",
-    "issuer": "[did]",
-    "type": [
-        "VerifiableCredential",
-        "UseCaseFrameworkCondition"
-    ],
-    "issuanceDate": "[iso8601-timestamp]",
-    "expirationDate": "[iso8601-timestamp]", //Optional field
-    "credentialSubject": {
-        "id": "[did]",
-        "holderIdentifier": "[bpn]",
-        "usecaseAgreement": {
-            "value": "ID_3.0_Trace",
-            "type": "cx-traceability",
-            "contract-template": "https://public.catena-x.org/contracts/traceabilty.v1.pdf",
-            "contract-version": "1.0.0",
-        }
-    },
-    "proof": {
-        "type": "JsonWebSignature2020",
-        "created": "[iso8601-timestamp]",
-        "jws": "[jws]",
-        "proofPurpose": "assertionMethod",
-        "verificationMethod": "[did#key-id]"
-    }
-}
-
- -#### Summary Credential (scheduled for deprecation) - -The flow of creating a summary credential - -```plantuml -``` - -
-{
-    "@context": [
-        "https://www.w3.org/2018/credentials/v1",
-        "https://w3id.org/security/suites/jws-2020/v1",
-        "https://raw.githubusercontent.com/catenax-ng/product-core-schemas/main/UseCaseVC"
-    ],
-    "id": "[uuid]",
-    "issuer": "[did]",
-    "type": [
-        "VerifiableCredential",
-        "SummaryCredential"
-    ],
-    "issuanceDate": "[iso8601-timestamp]",
-    "expirationDate": "[iso8601-timestamp]", //Optional field
-    "credentialSubject": {
-        "id": "[did]",
-        "holderIdentifier": "[bpn]",
-    },
-    "proof": {
-        "type": "JsonWebSignature2020",
-        "created": "[iso8601-timestamp]",
-        "jws": "[jws]",
-        "proofPurpose": "assertionMethod",
-        "verificationMethod": "[did#key-id]"
-    }
-}
-
- # Deployment @@ -848,7 +351,7 @@ requirements where relevant and applicable: - Security & Compliance: Container Scan - Security & Compliance: Infrastructure as Code -# Technical Debts +# Technical Debts ## DID Technical Debts @@ -859,14 +362,7 @@ requirements where relevant and applicable: - No real tenant system - Private Keys are AES encrypted and stored in the MIW Postgres database - No revocation service available -- Summary Credential used as a token. -- Only 1 verifiable credential (VC) in a verifiable presentation (VP) possible -- Summary VC (S-VC) created with the private key of the auhtority - DID documents are stored in the MIW -- Summary VC always get deleted when new CX-Credential is added to the - MIW -- The creation of CX-Credential is located in the MIW, should be a dedicated - service outside of the wallet service - Only managed wallet available. No self-mangaged wallet - No Issuer Registry. Only one trusted issuer available - Download of VC to own wallet not possible @@ -874,11 +370,6 @@ requirements where relevant and applicable: - No key rotation - No update possibility for credentials, they need to be deleted and new ones generated -## Verifiable Credential - -- CX-Credentials are not consistent -- Only Summary Credential will be used because of the http header limition of 8KB - ## Verifiable Presentation Protocol (VVP) The *Verifiable Presentation Protocol (VPP)* is designed to address the problem of resolving Verifiable Presentations @@ -909,7 +400,7 @@ Declaring file: [VVP Flow Declaration](images/VVP-Flow.puml) ## SSI Library - + - No validation for JsonWebSignature2020 with RSA key - No Security valdition only Sercurity Assessment done, no attack vectors are tested diff --git a/docs/openapi_v001.json b/docs/openapi_v001.json deleted file mode 100644 index 857db03b7..000000000 --- a/docs/openapi_v001.json +++ /dev/null @@ -1,2536 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "Managed Identity Wallets API", - "version": "0.0.1", - "description": "Managed Identity Wallets API", - "termsOfService": "https://www.eclipse.org/legal/termsofuse.php", - "contact": { - "name": "Eclipse Tractus-X", - "url": "https://projects.eclipse.org/projects/automotive.tractusx", - "email": "tractusx-dev@eclipse.org" - }, - "license": { - "name": "Apache 2.0", - "url": "https://github.com/eclipse-tractusx/managed-identity-wallets/blob/develop/LICENSE" - } - }, - "servers": [], - "paths": { - "/api/wallets": { - "get": { - "tags": [ - "Wallets" - ], - "summary": "List of wallets", - "description": "Permission: **view_wallets**\n\nRetrieve list of registered wallets", - "parameters": [], - "responses": { - "200": { - "description": "List of wallets", - "content": { - "application/json": { - "schema": { - "items": { - "$ref": "#/components/schemas/WalletDto" - }, - "type": "array", - "maxItems": 100 - } - } - } - }, - "403": { - "description": "The request could not be completed due to a forbidden access.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "401": { - "description": "The request could not be completed due to a failed authorization.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - } - }, - "/api/wallet": { - "post": { - "tags": [ - "Wallets" - ], - "summary": "Create wallet", - "description": "Permission: **add_wallets**\n\nCreate a wallet and store it ", - "parameters": [], - "requestBody": { - "description": "wallet to create", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WalletCreateDto" - }, - "examples": { - "demo": { - "value": { - "bpn": "name", - "name": "bpn" - } - } - } - } - }, - "required": true - }, - "responses": { - "201": { - "description": "Wallet was successfully created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WalletDto" - }, - "examples": { - "demo": { - "value": { - "name": "name", - "bpn": "bpn", - "did": "did", - "createdAt": "2023-02-13T15:06:06.539270", - "vcs": [] - } - } - } - } - } - }, - "400": { - "description": "The input does not comply to the syntax requirements", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "409": { - "description": "The request could not be completed due to a conflict.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "403": { - "description": "The request could not be completed due to a forbidden access.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "401": { - "description": "The request could not be completed due to a failed authorization.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - } - }, - "/api/wallet/{identifier}": { - "get": { - "tags": [ - "Wallets" - ], - "summary": "Retrieve wallet by identifier", - "description": "Permission: **view_wallets** OR **view_wallet** (The BPN of Wallet to retrieve must equal the BPN of caller)\n\nRetrieve single wallet by identifier, with or without its credentials", - "parameters": [ - { - "name": "identifier", - "in": "path", - "schema": { - "type": "string" - }, - "required": true, - "deprecated": false, - "examples": { - "did": { - "value": "did:example:0123" - }, - "bpn": { - "value": "bpn123" - } - } - }, - { - "name": "withCredentials", - "in": "query", - "schema": { - "type": "boolean" - }, - "required": true, - "deprecated": false, - "examples": { - "withCredentials": { - "value": "false" - } - } - } - ], - "responses": { - "200": { - "description": "The wallet", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WalletDto" - }, - "examples": { - "demo": { - "value": { - "name": "name", - "bpn": "bpn", - "did": "did", - "createdAt": "2023-02-13T15:06:06.539342", - "vcs": [] - } - } - } - } - } - }, - "400": { - "description": "The input does not comply to the syntax requirements", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "404": { - "description": "The required entity does not exists", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "403": { - "description": "The request could not be completed due to a forbidden access.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "401": { - "description": "The request could not be completed due to a failed authorization.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - } - }, - "/api/wallet/{identifier}/credentials": { - "post": { - "tags": [ - "Wallets" - ], - "summary": "Store Verifiable Credential", - "description": "Permission: **update_wallets** OR **update_wallet** (The BPN of wallet to extract credentials from must equal BPN of caller)\n\nStore a verifiable credential in the wallet of the given identifier", - "parameters": [ - { - "name": "identifier", - "in": "path", - "schema": { - "type": "string" - }, - "required": true, - "deprecated": false, - "examples": { - "did": { - "value": "did:exp:123" - }, - "bpn": { - "value": "BPN123" - } - } - } - ], - "requestBody": { - "description": "The verifiable credential to be stored", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/IssuedVerifiableCredentialRequestDto" - }, - "examples": { - "demo": { - "value": { - "id": "http://example.edu/credentials/3732", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "University-Degree-Credential, VerifiableCredential" - ], - "issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "issuanceDate": "2019-06-16T18:56:59Z", - "expirationDate": "2019-06-17T18:56:59Z", - "credentialSubject": { - "college": "Test-University" - }, - "credentialStatus": { - "id": "http://example.edu/api/credentials/status/test#3", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "3", - "statusListCredential": "http://example.edu/api/credentials/status/test" - }, - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - } - } - } - }, - "required": true - }, - "responses": { - "201": { - "description": "Success message", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - }, - "examples": { - "demo": { - "value": { - "message": "Credential with id http://example.edu/credentials/3732has been successfully stored" - } - } - } - } - } - }, - "422": { - "description": "The input can not be processed due to semantic mismatches", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "404": { - "description": "The required entity does not exists", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "403": { - "description": "The request could not be completed due to a forbidden access.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "401": { - "description": "The request could not be completed due to a failed authorization.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - } - }, - "/api/didDocuments/{identifier}": { - "get": { - "tags": [ - "DIDDocument" - ], - "summary": "Resolve DID Document", - "description": "Resolve the DID document for a given DID or BPN", - "parameters": [ - { - "name": "identifier", - "in": "path", - "schema": { - "type": "string" - }, - "required": true, - "deprecated": false, - "examples": { - "did": { - "value": "did:exp:123" - }, - "bpn": { - "value": "BPN123" - } - } - } - ], - "responses": { - "200": { - "description": "The resolved DID Document", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DidDocumentDto" - }, - "examples": { - "demo": { - "value": { - "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "@context": [ - "https://www.w3.org/ns/did/v1" - ], - "controller": [ - "123", - "1231" - ], - "verificationMethod": [ - { - "id": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "type": "Ed25519VerificationKey2018", - "controller": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "publicKeyBase58": "FyfKP2HvTKqDZQzvyL38yXH7bExmwofxHf2NR5BrcGf1" - } - ], - "service": [ - { - "id": "did:example:123#edv", - "type": "ServiceEndpointProxyService", - "serviceEndpoint": "https://myservice.com/myendpoint" - } - ] - } - } - } - } - } - }, - "404": { - "description": "The required entity does not exists", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "400": { - "description": "The input does not comply to the syntax requirements", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - } - }, - "/api/credentials": { - "get": { - "tags": [ - "VerifiableCredentials" - ], - "summary": "Query Verifiable Credentials", - "description": "Permission: **view_wallets** OR **view_wallet** (The BPN of holderIdentifier must equal BPN of caller)\n\nSearch verifiable credentials with filter criteria", - "parameters": [ - { - "name": "holderIdentifier", - "in": "query", - "schema": { - "type": "string" - }, - "required": false, - "deprecated": false, - "examples": { - "holderIdentifierDid": { - "value": "did:example:4567" - }, - "holderIdentifierBPN": { - "value": "BPN4567" - } - } - }, - { - "name": "credentialId", - "in": "query", - "schema": { - "type": "string" - }, - "required": false, - "deprecated": false, - "examples": { - "id": { - "value": "http://example.edu/credentials/3732" - } - } - }, - { - "name": "issuerIdentifier", - "in": "query", - "schema": { - "type": "string" - }, - "required": false, - "deprecated": false, - "examples": { - "issuerIdentifierDid": { - "value": "did:example:0123" - }, - "issuerIdentifierBPN": { - "value": "BPN0123" - } - } - }, - { - "name": "type", - "in": "query", - "schema": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - }, - "required": false, - "deprecated": false, - "examples": { - "type": { - "value": "['University-Degree-Credential']" - } - } - } - ], - "responses": { - "200": { - "description": "The list of verifiable credentials matching the query, empty if no match found", - "content": { - "application/json": { - "schema": { - "items": { - "$ref": "#/components/schemas/VerifiableCredentialDto" - }, - "type": "array", - "maxItems": 100 - } - } - } - }, - "403": { - "description": "The request could not be completed due to a forbidden access.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "401": { - "description": "The request could not be completed due to a failed authorization.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - }, - "post": { - "tags": [ - "VerifiableCredentials" - ], - "summary": "Issue Verifiable Credential", - "description": "Permission: **update_wallets** OR **update_wallet** (The BPN of the issuer of the Verifiable Credential must equal BPN of caller)\n\nIssue a verifiable credential with a given issuer DID", - "parameters": [], - "requestBody": { - "description": "The verifiable credential input data", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifiableCredentialRequestDto" - }, - "examples": { - "demo": { - "value": { - "id": "http://example.edu/credentials/3732", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "University-Degree-Credential, VerifiableCredential" - ], - "issuerIdentifier": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "issuanceDate": "2019-06-16T18:56:59Z", - "expirationDate": "2019-06-17T18:56:59Z", - "credentialSubject": { - "college": "Test-University" - }, - "holderIdentifier": "did:example:492edf208", - "isRevocable": true - } - } - } - } - }, - "required": true - }, - "responses": { - "201": { - "description": "The created Verifiable Credential", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifiableCredentialDto" - }, - "examples": { - "demo": { - "value": { - "id": "http://example.edu/credentials/3732", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "University-Degree-Credential, VerifiableCredential" - ], - "issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "issuanceDate": "2019-06-16T18:56:59Z", - "expirationDate": "2019-06-17T18:56:59Z", - "credentialSubject": { - "college": "Test-University" - }, - "credentialStatus": { - "id": "https://example.com/credentials/status/3#94567", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "94567", - "statusListCredential": "https://example.com/credentials/status/3" - }, - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - } - } - } - } - }, - "422": { - "description": "The input can not be processed due to semantic mismatches", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "400": { - "description": "The input does not comply to the syntax requirements", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "403": { - "description": "The request could not be completed due to a forbidden access.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "401": { - "description": "The request could not be completed due to a failed authorization.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - } - }, - "/api/credentials/issuer": { - "post": { - "tags": [ - "VerifiableCredentials" - ], - "summary": "Issue a Verifiable Credential with base wallet issuer", - "description": "Permission: **update_wallets** OR **update_wallet** (The BPN of base wallet must equal BPN of caller)\n\nIssue a verifiable credential by base wallet", - "parameters": [], - "requestBody": { - "description": "The verifiable credential input", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifiableCredentialRequestWithoutIssuerDto" - }, - "examples": { - "demo": { - "value": { - "id": "http://example.edu/credentials/3732", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "University-Degree-Credential, VerifiableCredential" - ], - "issuanceDate": "2019-06-16T18:56:59Z", - "expirationDate": "2019-06-17T18:56:59Z", - "credentialSubject": { - "college": "Test-University" - }, - "holderIdentifier": "did:example:492edf208" - } - } - } - } - }, - "required": true - }, - "responses": { - "201": { - "description": "The created Verifiable Credential", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifiableCredentialDto" - }, - "examples": { - "demo": { - "value": { - "id": "http://example.edu/credentials/3732", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "University-Degree-Credential, VerifiableCredential" - ], - "issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "issuanceDate": "2019-06-16T18:56:59Z", - "expirationDate": "2019-06-17T18:56:59Z", - "credentialSubject": { - "college": "Test-University" - }, - "credentialStatus": { - "id": "https://example.com/credentials/status/3#94567", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "94567", - "statusListCredential": "https://example.com/credentials/status/3" - }, - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - } - } - } - } - }, - "422": { - "description": "The input can not be processed due to semantic mismatches", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "400": { - "description": "The input does not comply to the syntax requirements", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "403": { - "description": "The request could not be completed due to a forbidden access.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "401": { - "description": "The request could not be completed due to a failed authorization.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - } - }, - "/api/credentials/issuer/membership": { - "post": { - "tags": [ - "VerifiableCredentials" - ], - "summary": "Issue a Membership Verifiable Credential with base wallet issuer", - "description": "Permission: **update_wallets** OR **update_wallet** (The BPN of base wallet must equal BPN of caller)\n\nIssue a verifiable credential by base wallet", - "parameters": [], - "requestBody": { - "description": "The bpn of the holders wallet", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateMembershipVCDto" - }, - "examples": { - "demo": { - "value": { - "bpn": "bpn" - } - } - } - } - }, - "required": true - }, - "responses": { - "201": { - "description": "The created Verifiable Credential", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifiableCredentialDto" - }, - "examples": { - "demo": { - "value": { - "id": "http://example.edu/credentials/3732", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "MembershipCredential, VerifiableCredential" - ], - "issuer": "did:web:miw.de/bpn0000000000", - "issuanceDate": "2021-06-16T18:56:59Z", - "credentialSubject": { - "type": "MembershipCredential", - "memberOf": "Catena-X", - "status": "Active", - "startTime": "2021-06-16T18:56:59Z" - }, - "holderIdentifier": "bpn", - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:web:miw.de/bpn0000000000#key-0", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - } - } - } - } - }, - "422": { - "description": "The input can not be processed due to semantic mismatches", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "400": { - "description": "The input does not comply to the syntax requirements", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "403": { - "description": "The request could not be completed due to a forbidden access.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "401": { - "description": "The request could not be completed due to a failed authorization.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - } - }, - "/api/credentials/validation": { - "post": { - "tags": [ - "VerifiableCredentials" - ], - "summary": "Validate Verifiable Credential", - "description": "Permission: **view_wallets** OR **view_wallet**\n\nValidate Verifiable Credentials", - "parameters": [ - { - "name": "withDateValidation", - "in": "query", - "schema": { - "type": "boolean", - "default": false, - "nullable": true - }, - "required": false, - "deprecated": false, - "examples": { - "withDateValidation": { - "value": "false" - } - } - }, - { - "name": "withRevocationValidation", - "in": "query", - "schema": { - "type": "boolean", - "default": true - }, - "required": false, - "deprecated": false, - "examples": { - "withRevocationValidation": { - "value": "false" - } - } - } - ], - "requestBody": { - "description": "The verifiable credential to validate", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifiableCredentialDto" - }, - "examples": { - "demo": { - "value": { - "id": "http://example.edu/credentials/3732", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "University-Degree-Credential, VerifiableCredential" - ], - "issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "issuanceDate": "2019-06-16T18:56:59Z", - "expirationDate": "2019-06-17T18:56:59Z", - "credentialSubject": { - "college": "Test-University" - }, - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - } - } - } - } - }, - "responses": { - "200": { - "description": "The verification value", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifyResponse" - }, - "examples": { - "demo": { - "value": { - "valid": true, - "vp": { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": [ - "VerifiablePresentation" - ], - "holder": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "verifiableCredential": [ - { - "id": "http://example.edu/credentials/3732", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "University-Degree-Credential, VerifiableCredential" - ], - "issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "issuanceDate": "2019-06-16T18:56:59Z", - "expirationDate": "2019-06-17T18:56:59Z", - "credentialSubject": { - "college": "Test-University" - }, - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - ], - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - } - } - } - } - } - }, - "422": { - "description": "The input can not be processed due to semantic mismatches", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "400": { - "description": "The input does not comply to the syntax requirements", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "403": { - "description": "The request could not be completed due to a forbidden access.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "401": { - "description": "The request could not be completed due to a failed authorization.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - } - }, - "/api/presentations": { - "post": { - "tags": [ - "VerifiablePresentations" - ], - "summary": "Create Verifiable Presentation", - "description": "Permission: **update_wallets** OR **update_wallet** (The BPN of the issuer of the Verifiable Presentation must equal to BPN of caller)\n\nCreate a verifiable presentation from a list of verifiable credentials, signed by the holder", - "parameters": [ - { - "name": "asJwt", - "in": "query", - "schema": { - "type": "boolean", - "default": true - }, - "required": false, - "deprecated": false, - "examples": { - "asJwt": { - "value": "false" - } - } - }, - { - "name": "withCredentialsDateValidation", - "in": "query", - "schema": { - "type": "boolean", - "default": true - }, - "required": false, - "deprecated": false, - "examples": { - "withCredentialsDateValidation": { - "value": "false" - } - } - }, - { - "name": "withCredentialsValidation", - "in": "query", - "schema": { - "type": "boolean", - "default": true - }, - "required": false, - "deprecated": false, - "examples": { - "withCredentialsValidation": { - "value": "false" - } - } - }, - { - "name": "withRevocationValidation", - "in": "query", - "schema": { - "type": "boolean", - "default": true - }, - "required": false, - "deprecated": false, - "examples": { - "withRevocationValidation": { - "value": "false" - } - } - } - ], - "requestBody": { - "description": "The verifiable presentation input data", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifiablePresentationRequestDto" - }, - "examples": { - "demo": { - "value": { - "holderIdentifier": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "verifiableCredentials": [ - { - "id": "http://example.edu/credentials/333", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "University-Degree-Credential, VerifiableCredential" - ], - "issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "issuanceDate": "2019-06-16T18:56:59Z", - "expirationDate": "2019-06-17T18:56:59Z", - "credentialSubject": { - "college": "Test-University" - }, - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#keys-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - ] - } - } - } - } - }, - "required": true - }, - "responses": { - "201": { - "description": "The created verifiable presentation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifiablePresentationDto" - }, - "examples": { - "demo": { - "value": { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": [ - "VerifiablePresentation" - ], - "holder": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "verifiableCredential": [ - { - "id": "http://example.edu/credentials/3732", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "University-Degree-Credential, VerifiableCredential" - ], - "issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "issuanceDate": "2019-06-16T18:56:59Z", - "expirationDate": "2019-06-17T18:56:59Z", - "credentialSubject": { - "college": "Test-University" - }, - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - ], - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - } - } - } - } - }, - "422": { - "description": "The input can not be processed due to semantic mismatches", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "403": { - "description": "The request could not be completed due to a forbidden access.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "401": { - "description": "The request could not be completed due to a failed authorization.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - } - }, - "/api/presentations/validation": { - "post": { - "tags": [ - "VerifiablePresentations" - ], - "summary": "Validate Verifiable Presentation", - "description": "Permission: **view_wallets** OR **view_wallet**\n\nValidate Verifiable Presentation with all included credentials", - "parameters": [ - { - "name": "withDateValidation", - "in": "query", - "schema": { - "type": "boolean", - "default": false, - "nullable": true - }, - "required": false, - "deprecated": false, - "examples": { - "withDateValidation": { - "value": "false" - } - } - }, - { - "name": "withRevocationValidation", - "in": "query", - "schema": { - "type": "boolean", - "default": true - }, - "required": false, - "deprecated": false, - "examples": { - "withRevocationValidation": { - "value": "false" - } - } - } - ], - "requestBody": { - "description": "The verifiable presentation to validate", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifiablePresentationDto" - }, - "examples": { - "demo": { - "value": { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": [ - "VerifiablePresentation" - ], - "holder": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "verifiableCredential": [ - { - "id": "http://example.edu/credentials/3732", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "University-Degree-Credential, VerifiableCredential" - ], - "issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "issuanceDate": "2019-06-16T18:56:59Z", - "expirationDate": "2019-06-17T18:56:59Z", - "credentialSubject": { - "college": "Test-University" - }, - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - ], - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - } - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "The verification value", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifyResponse" - }, - "examples": { - "demo": { - "value": { - "valid": true, - "vp": { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": [ - "VerifiablePresentation" - ], - "holder": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "verifiableCredential": [ - { - "id": "http://example.edu/credentials/3732", - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "type": [ - "University-Degree-Credential, VerifiableCredential" - ], - "issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f", - "issuanceDate": "2019-06-16T18:56:59Z", - "expirationDate": "2019-06-17T18:56:59Z", - "credentialSubject": { - "college": "Test-University" - }, - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - ], - "proof": { - "type": "Ed25519Signature2018", - "created": "2021-11-17T22:20:27Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" - } - } - } - } - } - } - } - }, - "422": { - "description": "The input can not be processed due to semantic mismatches", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "400": { - "description": "The input does not comply to the syntax requirements", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "403": { - "description": "The request could not be completed due to a forbidden access.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - }, - "401": { - "description": "The request could not be completed due to a failed authorization.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExceptionResponse" - }, - "examples": { - "demo": { - "value": { - "message": "reason", - "error": true - } - } - } - } - } - } - }, - "deprecated": false - } - } - }, - "components": { - "schemas": { - "WalletDto": { - "properties": { - "bpn": { - "type": "string" - }, - "createdAt": { - "$ref": "#/components/schemas/LocalDateTime" - }, - "did": { - "type": "string" - }, - "name": { - "type": "string" - }, - "vcs": { - "items": { - "$ref": "#/components/schemas/VerifiableCredentialDto" - }, - "type": "array", - "maxItems": 100 - } - }, - "required": [ - "name", - "bpn", - "did", - "createdAt", - "vcs" - ], - "type": "object" - }, - "LocalDateTime": { - "properties": { - "date": { - "$ref": "#/components/schemas/LocalDate" - }, - "time": { - "$ref": "#/components/schemas/LocalTime" - } - }, - "type": "object" - }, - "LocalDate": { - "properties": { - "year": { - "format": "int32", - "type": "integer" - }, - "month": { - "$ref": "#/components/schemas/Short" - }, - "day": { - "$ref": "#/components/schemas/Short" - } - }, - "type": "object" - }, - "Short": { - "properties": {}, - "type": "object" - }, - "LocalTime": { - "properties": { - "hour": { - "$ref": "#/components/schemas/Short" - }, - "minute": { - "$ref": "#/components/schemas/Short" - }, - "second": { - "$ref": "#/components/schemas/Short" - }, - "nano": { - "format": "int32", - "type": "integer" - } - }, - "type": "object" - }, - "Byte": { - "properties": {}, - "type": "object" - }, - "VerifiableCredentialDto": { - "properties": { - "@context": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - }, - "credentialStatus": {}, - "credentialSubject": { - "additionalProperties": { - "$ref": "#/components/schemas/Any" - }, - "type": "object" - }, - "expirationDate": { - "type": "string", - "nullable": true - }, - "id": { - "type": "string", - "nullable": true - }, - "issuanceDate": { - "type": "string" - }, - "issuer": { - "type": "string" - }, - "proof": { - "$ref": "#/components/schemas/LdProofDto" - }, - "provenanceProof": { - "items": { - "$ref": "#/components/schemas/Any" - }, - "type": "array", - "maxItems": 100 - }, - "type": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - } - }, - "required": [ - "@context", - "type", - "issuer", - "issuanceDate", - "credentialSubject" - ], - "type": "object" - }, - "Any": { - "properties": {}, - "type": "object" - }, - "LdProofDto": { - "properties": { - "challenge": { - "type": "string", - "nullable": true - }, - "created": { - "type": "string" - }, - "creator": { - "type": "string", - "nullable": true - }, - "domain": { - "type": "string", - "nullable": true - }, - "jws": { - "type": "string", - "nullable": true - }, - "nonce": { - "type": "string", - "nullable": true - }, - "proofPurpose": { - "type": "string" - }, - "proofValue": { - "type": "string", - "nullable": true - }, - "type": { - "type": "string" - }, - "verificationMethod": { - "type": "string" - } - }, - "required": [ - "type", - "created", - "proofPurpose", - "verificationMethod" - ], - "type": "object" - }, - "ExceptionResponse": { - "properties": { - "error": { - "type": "boolean" - }, - "message": { - "type": "string" - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "WalletCreateDto": { - "properties": { - "bpn": { - "type": "string" - }, - "name": { - "type": "string" - } - }, - "required": [ - "bpn", - "name" - ], - "type": "object" - }, - "CreateMembershipVCDto": { - "properties": { - "bpn": { - "type": "string" - } - }, - "required": [ - "bpn" - ], - "type": "object" - }, - "SuccessResponse": { - "properties": { - "message": { - "type": "string" - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "IssuedVerifiableCredentialRequestDto": { - "properties": { - "@context": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - }, - "CredentialStatus": {}, - "credentialSubject": { - "additionalProperties": { - "$ref": "#/components/schemas/Any" - }, - "type": "object" - }, - "expirationDate": { - "type": "string", - "nullable": true - }, - "id": { - "type": "string", - "nullable": true - }, - "issuanceDate": { - "type": "string" - }, - "issuer": { - "type": "string" - }, - "proof": { - "$ref": "#/components/schemas/LdProofDto" - }, - "type": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - } - }, - "required": [ - "@context", - "type", - "issuer", - "issuanceDate", - "credentialSubject", - "proof" - ], - "type": "object" - }, - "DidDocumentDto": { - "properties": { - "alsoKnownAs": { - "type": "string", - "nullable": true - }, - "assertionMethodVerificationMethods": { - "items": { - "$ref": "#/components/schemas/Any" - }, - "type": "array", - "maxItems": 100 - }, - "authenticationVerificationMethods": { - "items": { - "$ref": "#/components/schemas/Any" - }, - "type": "array", - "maxItems": 100 - }, - "capabilityDelegationVerificationMethods": { - "items": { - "$ref": "#/components/schemas/Any" - }, - "type": "array", - "maxItems": 100 - }, - "capabilityInvocationVerificationMethods": { - "items": { - "$ref": "#/components/schemas/Any" - }, - "type": "array", - "maxItems": 100 - }, - "context": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - }, - "controller": { - "$ref": "#/components/schemas/Short" - }, - "id": { - "type": "string" - }, - "keyAgreementVerificationMethods": { - "items": { - "$ref": "#/components/schemas/Any" - }, - "type": "array", - "maxItems": 100 - }, - "services": { - "items": { - "$ref": "#/components/schemas/Any" - }, - "type": "array", - "maxItems": 100 - }, - "verificationMethods": { - "items": { - "$ref": "#/components/schemas/DidVerificationMethodDto" - }, - "type": "array", - "maxItems": 100 - } - }, - "required": [ - "id", - "context" - ], - "type": "object" - }, - "DidVerificationMethodDto": { - "properties": { - "controller": { - "type": "string" - }, - "id": { - "type": "string" - }, - "publicKeyBase58": { - "type": "string", - "nullable": true - }, - "publicKeyBase64": { - "type": "string", - "nullable": true - }, - "publicKeyHex": { - "type": "string", - "nullable": true - }, - "publicKeyJwk": { - "$ref": "#/components/schemas/PublicKeyJwkDto" - }, - "publicKeyMultibase": { - "type": "string", - "nullable": true - }, - "publicKeyPem": { - "type": "string", - "nullable": true - }, - "type": { - "type": "string" - } - }, - "required": [ - "id", - "type", - "controller" - ], - "type": "object" - }, - "PublicKeyJwkDto": { - "properties": { - "additionalAttributes": { - "additionalProperties": { - "$ref": "#/components/schemas/Any" - }, - "type": "object" - }, - "alg": { - "type": "string", - "nullable": true - }, - "crv": { - "type": "string", - "nullable": true - }, - "keyOps": { - "items": { - "type": "string", - "nullable": true - }, - "type": "array", - "maxItems": 100 - }, - "kid": { - "type": "string", - "nullable": true - }, - "kty": { - "type": "string" - }, - "use": { - "type": "string", - "nullable": true - }, - "x": { - "type": "string", - "nullable": true - }, - "y": { - "type": "string", - "nullable": true - } - }, - "required": [ - "kty" - ], - "type": "object" - }, - "VerifiableCredentialRequestDto": { - "properties": { - "@context": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - }, - "credentialSubject": { - "additionalProperties": { - "$ref": "#/components/schemas/Any" - }, - "type": "object" - }, - "expirationDate": { - "type": "string", - "nullable": true - }, - "holderIdentifier": { - "type": "string", - "nullable": true - }, - "id": { - "type": "string", - "nullable": true - }, - "isRevocable": { - "type": "boolean" - }, - "issuanceDate": { - "type": "string", - "nullable": true - }, - "issuerIdentifier": { - "type": "string" - }, - "type": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - } - }, - "required": [ - "@context", - "type", - "issuerIdentifier", - "issuanceDate", - "credentialSubject" - ], - "type": "object" - }, - "VerifiableCredentialRequestWithoutIssuerDto": { - "properties": { - "@context": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - }, - "credentialSubject": { - "additionalProperties": { - "$ref": "#/components/schemas/Any" - }, - "type": "object" - }, - "expirationDate": { - "type": "string", - "nullable": true - }, - "holderIdentifier": { - "type": "string" - }, - "id": { - "type": "string", - "nullable": true - }, - "isRevocable": { - "type": "boolean" - }, - "issuanceDate": { - "type": "string", - "nullable": true - }, - "type": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - } - }, - "required": [ - "@context", - "type", - "issuanceDate", - "credentialSubject", - "holderIdentifier" - ], - "type": "object" - }, - "VerifiablePresentationRequestDto": { - "properties": { - "holderIdentifier": { - "type": "string" - }, - "verifiableCredentials": { - "items": { - "$ref": "#/components/schemas/VerifiableCredentialDto" - }, - "type": "array", - "maxItems": 100 - } - }, - "required": [ - "holderIdentifier", - "verifiableCredentials" - ], - "type": "object" - }, - "VerifiablePresentationDto": { - "properties": { - "@context": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - }, - "holder": { - "type": "string", - "nullable": true - }, - "id": { - "type": "string", - "nullable": true - }, - "proof": { - "$ref": "#/components/schemas/LdProofDto" - }, - "type": { - "items": { - "type": "string" - }, - "type": "array", - "maxItems": 100 - }, - "verifiableCredential": { - "items": { - "$ref": "#/components/schemas/VerifiableCredentialDto" - }, - "type": "array", - "maxItems": 100 - } - }, - "required": [ - "@context", - "type" - ], - "type": "object" - }, - "VerifyResponse": { - "properties": { - "error": { - "type": "string", - "nullable": true - }, - "valid": { - "type": "boolean" - }, - "vp": { - "$ref": "#/components/schemas/VerifiablePresentationDto" - } - }, - "required": [ - "valid" - ], - "type": "object" - } - }, - "securitySchemes": { - "auth-token": { - "bearerFormat": "JWT", - "type": "http", - "scheme": "bearer" - } - } - }, - "security": { - "auth-token": {} - }, - "tags": [] -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index efaa4e5ae..927e22fbd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,5 +4,5 @@ jacocoVersion=0.8.9 springBootVersion=3.1.6 springDependencyVersion=1.1.0 groupName=org.eclipse.tractusx -applicationVersion=0.4.0 +applicationVersion=0.5.0-develop.20 openApiVersion=2.1.0 diff --git a/DEPENDENCIES b/miw/DEPENDENCIES similarity index 94% rename from DEPENDENCIES rename to miw/DEPENDENCIES index b1ca06d1b..de8b8b697 100644 --- a/DEPENDENCIES +++ b/miw/DEPENDENCIES @@ -1,6 +1,5 @@ maven/mavencentral/ch.qos.logback/logback-classic/1.4.12, EPL-1.0 OR LGPL-2.1-only, approved, #3435 maven/mavencentral/ch.qos.logback/logback-core/1.4.12, EPL-1.0 OR LGPL-2.1-only, approved, #3373 -maven/mavencentral/com.apicatalog/titanium-json-ld-jre8/1.3.1, Apache-2.0, approved, #10237 maven/mavencentral/com.apicatalog/titanium-json-ld/1.3.3, Apache-2.0, approved, #8912 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.3, Apache-2.0, approved, #7947 maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.15.3, MIT AND Apache-2.0, approved, #7932 @@ -39,9 +38,6 @@ maven/mavencentral/com.jayway.jsonpath/json-path/2.8.0, Apache-2.0, approved, cl maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.37.1, Apache-2.0, approved, #11701 maven/mavencentral/com.opencsv/opencsv/5.7.1, Apache-2.0, approved, clearlydefined maven/mavencentral/com.smartsensesolutions/commons-dao/0.0.5, Apache-2.0, approved, #9176 -maven/mavencentral/com.squareup.okhttp3/okhttp/4.10.0, Apache-2.0 AND MPL-2.0, approved, #3057 -maven/mavencentral/com.squareup.okio/okio-jvm/3.0.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/com.squareup.okio/okio/3.0.0, Apache-2.0, approved, clearlydefined maven/mavencentral/com.sun.activation/jakarta.activation/1.2.1, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf maven/mavencentral/com.sun.istack/istack-commons-runtime/4.1.2, BSD-3-Clause, approved, #2590 maven/mavencentral/com.sun.mail/jakarta.mail/1.6.5, EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, ee4j.mail @@ -78,10 +74,10 @@ maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.9, Apache-2.0, ap maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.9, Apache-2.0, approved, #5919 maven/mavencentral/jakarta.activation/jakarta.activation-api/2.1.2, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf maven/mavencentral/jakarta.annotation/jakarta.annotation-api/2.1.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.ca -maven/mavencentral/jakarta.inject/jakarta.inject-api/2.0.1, Apache-2.0, approved, clearlydefined -maven/mavencentral/jakarta.json/jakarta.json-api/2.1.3, EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, #7907 -maven/mavencentral/jakarta.persistence/jakarta.persistence-api/3.1.0, EPL-2.0 OR BSD-3-Clause AND (EPL-2.0 OR BSD-3-Clause AND BSD-3-Clause), approved, #7696 -maven/mavencentral/jakarta.transaction/jakarta.transaction-api/2.0.1, EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, #7697 +maven/mavencentral/jakarta.inject/jakarta.inject-api/2.0.1, Apache-2.0, approved, ee4j.cdi +maven/mavencentral/jakarta.json/jakarta.json-api/2.1.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jsonp +maven/mavencentral/jakarta.persistence/jakarta.persistence-api/3.1.0, EPL-2.0 OR BSD-3-Clause, approved, ee4j.jpa +maven/mavencentral/jakarta.transaction/jakarta.transaction-api/2.0.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jta maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.2, Apache-2.0, approved, ee4j.validation maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/4.0.1, BSD-3-Clause, approved, ee4j.jaxb maven/mavencentral/javax.activation/javax.activation-api/1.2.0, (CDDL-1.1 OR GPL-2.0 WITH Classpath-exception-2.0) AND Apache-2.0, approved, CQ18740 @@ -120,10 +116,10 @@ maven/mavencentral/org.codehaus.woodstox/stax2-api/4.2.1, BSD-2-Clause, approved maven/mavencentral/org.eclipse.angus/angus-activation/2.0.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.angus maven/mavencentral/org.eclipse.microprofile.config/microprofile-config-api/2.0, Apache-2.0, approved, technology.microprofile maven/mavencentral/org.eclipse.parsson/parsson/1.1.5, EPL-2.0, approved, ee4j.parsson -maven/mavencentral/org.eclipse.tractusx.ssi/cx-ssi-lib/0.0.18, Apache-2.0, approved, automotive.tractusx -maven/mavencentral/org.glassfish.jaxb/jaxb-core/4.0.4, BSD-3-Clause, approved, ee4j.jaxb -maven/mavencentral/org.glassfish.jaxb/jaxb-runtime/4.0.4, BSD-3-Clause, approved, ee4j.jaxb -maven/mavencentral/org.glassfish.jaxb/txw2/4.0.4, BSD-3-Clause, approved, ee4j.jaxb +maven/mavencentral/org.eclipse.tractusx.ssi/cx-ssi-lib/0.0.19, Apache-2.0, approved, automotive.tractusx +maven/mavencentral/org.glassfish.jaxb/jaxb-core/4.0.4, BSD-3-Clause, approved, ee4j.jaxb-impl +maven/mavencentral/org.glassfish.jaxb/jaxb-runtime/4.0.4, BSD-3-Clause, approved, ee4j.jaxb-impl +maven/mavencentral/org.glassfish.jaxb/txw2/4.0.4, BSD-3-Clause, approved, ee4j.jaxb-impl maven/mavencentral/org.hamcrest/hamcrest-core/2.2, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.hamcrest/hamcrest/2.2, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.hdrhistogram/HdrHistogram/2.1.12, BSD-2-Clause OR LicenseRef-Public-Domain, approved, CQ13192 @@ -148,11 +144,6 @@ maven/mavencentral/org.jboss.shrinkwrap/shrinkwrap-spi/1.2.6, Apache-2.0, approv maven/mavencentral/org.jboss.spec.javax.annotation/jboss-annotations-api_1.3_spec/2.0.1.Final, EPL-2.0 or GPL-2.0-only WITH Classpath-exception-2.0, approved, #1805 maven/mavencentral/org.jboss.spec.javax.ws.rs/jboss-jaxrs-api_2.1_spec/2.0.1.Final, Apache-2.0 AND (EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0), approved, #2124 maven/mavencentral/org.jboss.spec.javax.xml.bind/jboss-jaxb-api_2.3_spec/2.0.0.Final, BSD-3-Clause, approved, #2122 -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.8.22, Apache-2.0, approved, #8910 -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.8.22, Apache-2.0, approved, #8807 -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.22, Apache-2.0, approved, #8875 -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.8.22, Apache-2.0, approved, #8865 -maven/mavencentral/org.jetbrains/annotations/13.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.jetbrains/annotations/17.0.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.json/json/20230227, LicenseRef-Public-domain, approved, #9174 maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.9.3, EPL-2.0, approved, #3133 @@ -234,8 +225,12 @@ maven/mavencentral/org.springframework/spring-test/6.0.14, Apache-2.0, approved, maven/mavencentral/org.springframework/spring-tx/6.0.14, Apache-2.0, approved, #5926 maven/mavencentral/org.springframework/spring-web/6.0.14, Apache-2.0, approved, #5942 maven/mavencentral/org.springframework/spring-webmvc/6.0.14, Apache-2.0, approved, #5944 +maven/mavencentral/org.testcontainers/database-commons/1.19.3, Apache-2.0, approved, #10345 +maven/mavencentral/org.testcontainers/jdbc/1.19.3, Apache-2.0, approved, #10348 maven/mavencentral/org.testcontainers/junit-jupiter/1.19.3, MIT, approved, #10344 +maven/mavencentral/org.testcontainers/postgresql/1.19.3, MIT, approved, #10350 maven/mavencentral/org.testcontainers/testcontainers/1.19.3, Apache-2.0 AND MIT, approved, #10347 maven/mavencentral/org.webjars/swagger-ui/4.18.2, Apache-2.0, approved, #7850 +maven/mavencentral/org.wiremock/wiremock-standalone/3.4.2, MIT AND Apache-2.0, approved, #14889 maven/mavencentral/org.xmlunit/xmlunit-core/2.9.1, Apache-2.0, approved, #6272 maven/mavencentral/org.yaml/snakeyaml/2.0, Apache-2.0 AND (Apache-2.0 OR BSD-3-Clause OR EPL-1.0 OR GPL-2.0-or-later OR LGPL-2.1-or-later), approved, #7275 diff --git a/build.gradle b/miw/build.gradle similarity index 89% rename from build.gradle rename to miw/build.gradle index 68734b53b..9312d3728 100644 --- a/build.gradle +++ b/miw/build.gradle @@ -1,3 +1,22 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + plugins { id 'java' id 'org.springframework.boot' version "${springBootVersion}" @@ -77,7 +96,7 @@ dependencies { implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${openApiVersion}" implementation group: 'com.smartsensesolutions', name: 'commons-dao', version: '0.0.5' implementation 'org.liquibase:liquibase-core' - implementation 'org.eclipse.tractusx.ssi:cx-ssi-lib:0.0.18' + implementation 'org.eclipse.tractusx.ssi:cx-ssi-lib:0.0.19' //Added explicitly to mitigate CVE 2022-1471 implementation group: 'org.yaml', name: 'snakeyaml', version: '2.0' @@ -92,12 +111,14 @@ dependencies { developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.wiremock:wiremock-standalone:3.4.2' testImplementation 'org.projectlombok:lombok:1.18.28' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation "org.testcontainers:testcontainers" testImplementation 'com.h2database:h2:2.2.220' testImplementation "org.testcontainers:junit-jupiter" testImplementation group: 'com.github.dasniko', name: 'testcontainers-keycloak', version: '2.5.0' + testImplementation 'org.testcontainers:postgresql' testImplementation group: 'org.mockito', name: 'mockito-inline', version: '5.2.0' testImplementation group: 'org.json', name: 'json', version: '20230227' testImplementation group: 'com.github.curious-odd-man', name: 'rgxgen', version: '1.4' @@ -110,7 +131,7 @@ tasks.register('dashDownload', Download) { description = 'Download the Dash License Tool standalone jar' group = 'License' src 'https://repo.eclipse.org/service/local/artifact/maven/redirect?r=dash-licenses&g=org.eclipse.dash&a=org.eclipse.dash.licenses&v=LATEST' - dest layout.projectDirectory.file('dash.jar') + dest rootProject.file('dash.jar') // will not replace an existing file. If you know you need a new version // then manually delete the file yourself, or run `dashClean` overwrite false @@ -121,7 +142,7 @@ tasks.register('dashClean') { description = "Clean all files used by the 'License' group" group = 'License' logger.lifecycle("Removing 'dash.jar'") - file('dash.jar').delete() + rootProject.file('dash.jar').delete() logger.lifecycle("Removing 'deps.txt'") file('deps.txt').delete() } @@ -162,7 +183,7 @@ tasks.register('dashLicenseCheck', JavaExec) { dashLicenseCheck -> dashLicenseCheck.dependsOn('dashDownload') dashLicenseCheck.dependsOn('dashDependencies') doFirst { - classpath = files('dash.jar') + classpath = rootProject.files('dash.jar') // docs: https://eclipse-tractusx.github.io/docs/release/trg-7/trg-7-04 args('-project', 'automotive.tractusx', '-summary', 'DEPENDENCIES', 'deps.txt') } @@ -194,9 +215,9 @@ build { bootJar { metaInf { from 'DEPENDENCIES' - from 'SECURITY.md' - from 'NOTICE.md' - from 'LICENSE' + from '../SECURITY.md' + from '../NOTICE.md' + from '../LICENSE' } } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/ManagedIdentityWalletsApplication.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/ManagedIdentityWalletsApplication.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/ManagedIdentityWalletsApplication.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/ManagedIdentityWalletsApplication.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/DidDocumentControllerApiDocs.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/DidDocumentControllerApiDocs.java similarity index 96% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/DidDocumentControllerApiDocs.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/DidDocumentControllerApiDocs.java index 1a53d27de..385f61541 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/DidDocumentControllerApiDocs.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/DidDocumentControllerApiDocs.java @@ -11,6 +11,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; public class DidDocumentControllerApiDocs { @@ -72,7 +73,7 @@ public class DidDocumentControllerApiDocs { """) }) }) }) - @Operation(description = "Resolve the DID document for a given DID or BPN", summary = "Resolve DID Document") + @Operation(description = "Resolve the DID document for a given DID or BPN", summary = "Resolve DID Document", security = { @SecurityRequirement(name = "Authenticate using access_token") }) public @interface GetDidDocumentApiDocs { } @@ -135,7 +136,7 @@ public class DidDocumentControllerApiDocs { }) }) }) - @Operation(description = "Resolve the DID document for a given BPN", summary = "Resolve DID Document") + @Operation(description = "Resolve the DID document for a given BPN", summary = "Resolve DID Document", security = { @SecurityRequirement(name = "Authenticate using access_token") }) public @interface GetDidResolveApiDocs { } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/HoldersCredentialControllerApiDocs.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/HoldersCredentialControllerApiDocs.java similarity index 92% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/HoldersCredentialControllerApiDocs.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/HoldersCredentialControllerApiDocs.java index ef5c2ead5..45cb2c9de 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/HoldersCredentialControllerApiDocs.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/HoldersCredentialControllerApiDocs.java @@ -1,9 +1,25 @@ -package org.eclipse.tractusx.managedidentitywallets.apidocs; +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +package org.eclipse.tractusx.managedidentitywallets.apidocs; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -11,6 +27,12 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; public class HoldersCredentialControllerApiDocs { @@ -81,7 +103,7 @@ public class HoldersCredentialControllerApiDocs { "@context": [ "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/SummaryVC.json", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", "https://w3id.org/security/suites/jws-2020/v1" ], "id": "did:web:localhost:BPNL000000000000#954d43de-ebed-481d-9e35-e3bbb311b8f5", @@ -148,7 +170,7 @@ public class HoldersCredentialControllerApiDocs { """) }) }) }) - @Operation(description = "Permission: **view_wallets** OR **view_wallet** (The BPN of holderIdentifier must equal BPN of caller)\n\n Search verifiable credentials with filter criteria", summary = "Query Verifiable Credentials") + @Operation(description = "Permission: **view_wallets** OR **view_wallet** (The BPN of holderIdentifier must equal BPN of caller)\n\n Search verifiable credentials with filter criteria", summary = "Query Verifiable Credentials", security = { @SecurityRequirement(name = "Authenticate using access_token") }) public @interface GetCredentialsApiDocs { } @@ -161,7 +183,7 @@ public class HoldersCredentialControllerApiDocs { "@context": [ "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", "https://w3id.org/security/suites/jws-2020/v1" ], "id": "did:web:localhost:BPNL000000000000#319a2641-9407-4c39-bf51-a4a109b59604", @@ -255,7 +277,7 @@ public class HoldersCredentialControllerApiDocs { { "@context": [ "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", "https://w3id.org/security/suites/jws-2020/v1" ], "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", @@ -278,7 +300,7 @@ public class HoldersCredentialControllerApiDocs { } """)) }) - @Operation(summary = "Issue Verifiable Credential", description = "Permission: **update_wallets** OR **update_wallet** (The BPN of the issuer of the Verifiable Credential must equal BPN of caller)\nIssue a verifiable credential with a given issuer DID") + @Operation(summary = "Issue Verifiable Credential", description = "Permission: **update_wallets** OR **update_wallet** (The BPN of the issuer of the Verifiable Credential must equal BPN of caller)\nIssue a verifiable credential with a given issuer DID", security = { @SecurityRequirement(name = "Authenticate using access_token") }) public @interface IssueCredentialApiDoc { } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/IssuersCredentialControllerApiDocs.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/IssuersCredentialControllerApiDocs.java new file mode 100644 index 000000000..6f8571efe --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/IssuersCredentialControllerApiDocs.java @@ -0,0 +1,625 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.apidocs; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public class IssuersCredentialControllerApiDocs { + /** + * The constant API_TAG_VERIFIABLE_CREDENTIAL_ISSUER. + */ + public static final String API_TAG_VERIFIABLE_CREDENTIAL_ISSUER = "Verifiable Credential - Issuer"; + /** + * The constant API_TAG_VERIFIABLE_CREDENTIAL_VALIDATION. + */ + public static final String API_TAG_VERIFIABLE_CREDENTIAL_VALIDATION = "Verifiable Credential - Validation"; + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + @Tag(name = API_TAG_VERIFIABLE_CREDENTIAL_ISSUER) + @ApiResponses(value = { + @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { + @Content(examples = {}) }), + @ApiResponse(responseCode = "403", description = "The request could not be completed due to a forbidden access", content = { + @Content(examples = {}) }), + @ApiResponse(responseCode = "500", description = "Any other internal server error", content = { + @Content(examples = { + @ExampleObject(name = "Internal server error", value = """ + { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + """) + }) }), + @ApiResponse(responseCode = "400", description = "The input does not comply to the syntax requirements", content = { + @Content(examples = { + @ExampleObject(name = "Response in case of invalid data provided", value = """ + { + "type": "about:blank", + "title": "title", + "status": 400, + "detail": "details", + "instance": "API endpoint", + "properties": + { + "timestamp": 1689760833962, + "errors": + { + } + } + } + """) + }) + }), + @ApiResponse(responseCode = "200", description = "Issuer credential list", content = { + @Content(examples = { + @ExampleObject(name = "Issuer credential list", value = """ + { + "content": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#ae364f71-f054-4d91-b579-f001bcb3e59e", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T09:27:42Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:27:44Z", + "jws": "eyJhbGciOiJFZERTQSJ9..evDHQfW4EzJUt2HnS_WlmO8FFtywTGnwyywtCE7WP41my4Iscpqr4tbuVOqnZg85b4U8L3_ut8_pEONIhbExCQ", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + }, + { + "type": [ + "VerifiableCredential", + "SummaryCredential" + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T09:11:39Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "contractTemplate": "https://public.catena-x.org/contracts/", + "holderIdentifier": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "items": [ + "BpnCredential" + ], + "type": "SummaryCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:41Z", + "jws": "eyJhbGciOiJFZERTQSJ9..YvoFhDip3TQAfZUIu0yc843oA4uGTg049dMFt_GoaMmPjiNB_B1EFOL-gDpwjIxTYNlGOO_CLp9qStbzlDTNBg", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + }, + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T09:11:34Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + ], + "pageable": { + "sort": { + "empty": false, + "unsorted": false, + "sorted": true + }, + "offset": 0, + "pageNumber": 0, + "pageSize": 2147483647, + "paged": true, + "unpaged": false + }, + "last": true, + "totalPages": 1, + "totalElements": 3, + "first": true, + "size": 2147483647, + "number": 0, + "sort": { + "empty": false, + "unsorted": false, + "sorted": true + }, + "numberOfElements": 3, + "empty": false + } + """) + }) + }), + }) + @Operation(description = "Permission: **view_wallets** (The BPN of holderIdentifier must equal BPN of caller)\n\n Search verifiable credentials with filter criteria", summary = "Query Verifiable Credentials", security = { @SecurityRequirement(name = "Authenticate using access_token") }) + public @interface GetCredentialsApiDocs { + } + + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + @Tag(name = API_TAG_VERIFIABLE_CREDENTIAL_VALIDATION) + @ApiResponses(value = { + @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { + @Content(examples = {}) }), + @ApiResponse(responseCode = "403", description = "The request could not be completed due to a forbidden access", content = { + @Content(examples = {}) }), + @ApiResponse(responseCode = "500", description = "Any other internal server error", content = { + @Content(examples = { + @ExampleObject(name = "Internal server error", value = """ + { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + """) + }) }), + @ApiResponse(responseCode = "200", description = "Validate Verifiable Credentials", content = { + @Content(examples = { + @ExampleObject(name = "Verifiable Credentials without check expiry", value = """ + { + "valid": true, + "vc": { + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + }, + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z" + } + } + """), + @ExampleObject(name = "Verifiable Credentials with check expiry", value = """ + { + "valid": true, + "validateExpiryDate": true, + "vc": { + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + }, + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z" + } + } + """), + @ExampleObject(name = "Verifiable expired credentials with check expiry ", value = """ + { + "valid": false, + "validateExpiryDate": false, + "vc": { + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + }, + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2022-12-31T18:30:00Z" + } + } + """), + @ExampleObject(name = "Revocable Verifiable credentials with check expiry ", value = """ + { + "credentialStatus": "active", + "valid": true, + "validateExpiryDate": true, + "vc": { + "credentialSubject": [ + { + "holderIdentifier": "BPNL000000000001", + "allowedVehicleBrands": [ + "Audi", + "Abarth", + "Alfa Romeo", + "Chrysler" + ], + "id": "did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000001", + "activityType": "vehicleDismantle", + "type": "DismantlerCredential" + } + ], + "issuanceDate": "2024-01-05T05:42:53Z", + "id": "did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000000#8507aa50-b2a4-4532-8e45-f50e7654b23b", + "proof": { + "proofPurpose": "assertionMethod", + "verificationMethod": "did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000000#a39d8ccf-2a66-488d-bfec-916768082e91", + "type": "JsonWebSignature2020", + "created": "2024-01-05T05:42:53Z", + "jws": "eyJhbGciOiJFZERTQSJ9..15NdxA8L_Iw7Igxevm7YGMAQA-Kt6PMOpix6p0jaYHCtfQnTy3q61SDvsnsltGT6fzM90JOubOuig2WFy-GPDg" + }, + "type": [ + "VerifiableCredential", + "DismantlerCredential" + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1", + "https://w3id.org/vc/status-list/2021/v1" + ], + "issuer": "did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000000", + "credentialStatus": { + "id": "did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000000#0", + "statusPurpose": "revocation", + "statusListIndex": "0", + "statusListCredential": "https://ae96-203-129-213-107.ngrok-free.app/api/v1/revocations/credentials?issuerId=did:web:6e3e-203-129-213-107.ngrok-free.app:BPNL000000000000", + "type": "StatusList2021Entry" + }, + "expirationDate": "2024-12-31T18:30:00Z" + } + } + """), + @ExampleObject(name = "Verifiable Credentials with invalid signature", value = """ + { + "valid": false, + "vc": + { + "@context": + [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "type": + [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z", + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": + [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": + { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhf", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + } + """) + }) + }) }) + @Operation(summary = "Validate Verifiable Credentials", description = "Permission: **view_wallets** OR **view_wallet** \n\n Validate Verifiable Credentials", security = { @SecurityRequirement(name = "Authenticate using access_token") }) + @RequestBody(content = { + @Content(examples = { @ExampleObject(name = "Validate credential in JSON-LD format", value = """ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T09:11:34Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + """), + @ExampleObject(name = "Validate credential in JWT format", value = """ + { + "jwt": "eyJraWQiOiJkaWQ6d2ViOmFmODgtMjAzLTEyOS0yMTMtMTA3Lm5ncm9rLWZyZWUuYXBwOkJQTkwwMDAwMDAwMDAwMDAjOGYyZWU5ZDItYTM2Yy00MTM4LWJlMWYtYjZmZWZiNmY4MDI0IiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJpc3MiOiJkaWQ6d2ViOmFmODgtMjAzLTEyOS0yMTMtMTA3Lm5ncm9rLWZyZWUuYXBwOkJQTkwwMDAwMDAwMDAwMDAiLCJzdWIiOiJkaWQ6d2ViOmFmODgtMjAzLTEyOS0yMTMtMTA3Lm5ncm9rLWZyZWUuYXBwOkJQTkwwMDAwMDAwMDAwMTEiLCJleHAiOjE3MzU2Njk4MDAsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly9jb2Zpbml0eS14LmdpdGh1Yi5pby9zY2hlbWEtcmVnaXN0cnkvdjEuMS9Vc2VDYXNlVkMuanNvbiIsImh0dHBzOi8vdzNpZC5vcmcvc2VjdXJpdHkvc3VpdGVzL2p3cy0yMDIwL3YxIl0sImlkIjoiZGlkOndlYjphZjg4LTIwMy0xMjktMjEzLTEwNy5uZ3Jvay1mcmVlLmFwcDpCUE5MMDAwMDAwMDAwMDAwI2Q4Y2ZjZDBiLWY0NGQtNDVkMC05OGEzLTA4ZDZkNmU5Y2E5NSIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJVc2VDYXNlRnJhbWV3b3JrQ29uZGl0aW9uIl0sImlzc3VlciI6ImRpZDp3ZWI6YWY4OC0yMDMtMTI5LTIxMy0xMDcubmdyb2stZnJlZS5hcHA6QlBOTDAwMDAwMDAwMDAwMCIsImNyZWRlbnRpYWxTdWJqZWN0IjpbeyJob2xkZXJJZGVudGlmaWVyIjoiQlBOTDAwMDAwMDAwMDAxMSIsImlkIjoiZGlkOndlYjphZjg4LTIwMy0xMjktMjEzLTEwNy5uZ3Jvay1mcmVlLmFwcDpCUE5MMDAwMDAwMDAwMDExIiwidHlwZSI6IkJlaGF2aW9yVHdpbkNyZWRlbnRpYWwiLCJjb250cmFjdFRlbXBsYXRlIjoiaHR0cHM6Ly9wdWJsaWMuY2F0ZW5hLXgub3JnL2NvbnRyYWN0cy90cmFjZWFiaWx0eS52MS5wZGYiLCJjb250cmFjdFZlcnNpb24iOiIxLjAuMCJ9XSwiY3JlZGVudGlhbFN0YXR1cyI6bnVsbCwiaXNzdWFuY2VEYXRlIjoiMjAyNC0wMi0wOFQxNDowMjo1M1oiLCJleHBpcmF0aW9uRGF0ZSI6IjIwMjQtMTItMzFUMTg6MzA6MDBaIn0sImp0aSI6IjliYWFhMjIzLTAxMjctNDEyZS05NjZhLTA3ZTJmZGU4NGNlNCJ9.X3rkj8Gv4OD5nEaeFG5pSA-dogbcYA91YEPmHiKT4FhAiIr7QAdSEULGXHYOn8-eK0jSDHNdAxNYIK1UwYRsCA" + } + """) + } + ) + }) + public @interface ValidateVerifiableCredentialApiDocs { + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + @Tag(name = API_TAG_VERIFIABLE_CREDENTIAL_ISSUER) + @ApiResponses(value = { + @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { + @Content(examples = {}) }), + @ApiResponse(responseCode = "403", description = "The request could not be completed due to a forbidden access", content = { + @Content(examples = {}) }), + @ApiResponse(responseCode = "500", description = "Any other internal server error", content = { + @Content(examples = { + @ExampleObject(name = "Internal server error", value = """ + { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + """) + }) }), + @ApiResponse(responseCode = "404", description = "Wallet not found with provided identifier", content = { + @Content(examples = { + @ExampleObject(name = "Wallet not found with provided identifier", value = """ + { + "type": "about:blank", + "title": "Error Title", + "status": 404, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + """) + }) }), + @ApiResponse(responseCode = "400", description = "The input does not comply to the syntax requirements", content = { + @Content(examples = { + @ExampleObject(name = "Response in case of invalid data provided", value = """ + { + "type": "about:blank", + "title": "Invalid data provided", + "status": 400, + "detail": "details", + "instance": "API endpoint", + "properties": + { + "timestamp": 1689760833962, + "errors": + { + "filed": "filed error message" + } + } + } + """) + }) + }), + @ApiResponse(responseCode = "201", description = "Issuer credential", content = { + @Content(examples = { + @ExampleObject(name = "Issuer credential", value = """ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#ff084e7a-1b46-4a2f-a78d-3d701a0bd6e4", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T12:18:30Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#", + "created": "2023-07-19T12:18:34Z", + "jws": "eyJhbGciOiJFZERTQSJ9..0Ua1vcTQAYwQY3PPuHr4RQxqW6iIngrHQQx1oPgk2uzqUpcbfY2YUxXAnbNA333-lSuvNhiV_1NLfBnCEcI2DQ" + } + } + """) + }) + }) }) + @Operation(summary = "Issue Verifiable Credential", description = "Permission: **update_wallets** (The BPN of the base wallet must equal BPN of caller)\nIssue a verifiable credential with a given issuer DID", security = { @SecurityRequirement(name = "Authenticate using access_token") }) + @RequestBody(content = { + @Content(examples = @ExampleObject(""" + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "issuanceDate": "2023-07-19T09:11:34Z", + "expirationDate": "2024-12-31T18:30:00Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ] + } + """)) + }) + public @interface IssueVerifiableCredentialUsingBaseWalletApiDocs { + } + + @Parameter(description = "Specifies whether the VC (Verifiable Credential) should be created as a JWT (JSON Web Token). " + + + "If set to true, the VC will be generated in JWT format" + + + "Setting this parameter to false will result in the VC being created as JSON-LD " + + "Defaults to false if not specified.", examples = { + @ExampleObject(name = "Create VC as JWT", value = "true"), + @ExampleObject(name = "Do not create VC as JWT", value = "false") + }) + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.RUNTIME) + public @interface AsJwtParam { + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/PresentationControllerApiDocs.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/PresentationControllerApiDocs.java similarity index 70% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/PresentationControllerApiDocs.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/PresentationControllerApiDocs.java index 06a072e3a..a2fec8430 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/PresentationControllerApiDocs.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/PresentationControllerApiDocs.java @@ -1,9 +1,25 @@ -package org.eclipse.tractusx.managedidentitywallets.apidocs; +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +package org.eclipse.tractusx.managedidentitywallets.apidocs; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -11,8 +27,14 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + public class PresentationControllerApiDocs { public static final String API_TAG_VERIFIABLE_PRESENTATIONS_GENERATION = "Verifiable Presentations - Generation"; public static final String API_TAG_VERIFIABLE_PRESENTATIONS_VALIDATION = "Verifiable Presentations - Validation"; @@ -20,7 +42,7 @@ public class PresentationControllerApiDocs { @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Tag(name = API_TAG_VERIFIABLE_PRESENTATIONS_GENERATION) - @Operation(summary = "Create Verifiable Presentation", description = "Permission: **update_wallets** OR **update_wallet** (The BPN of the issuer of the Verifiable Presentation must equal to BPN of caller) \n\n Create a verifiable presentation from a list of verifiable credentials, signed by the holder") + @Operation(summary = "Create Verifiable Presentation", description = "Permission: **update_wallets** OR **update_wallet** (The BPN of the issuer of the Verifiable Presentation must equal to BPN of caller) \n\n Create a verifiable presentation from a list of verifiable credentials, signed by the holder", security = { @SecurityRequirement(name = "Authenticate using access_token") }) @ApiResponses(value = { @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { @@ -74,7 +96,7 @@ public class PresentationControllerApiDocs { "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", "@context": [ "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", "https://w3id.org/security/suites/jws-2020/v1" ], "type": [ @@ -120,7 +142,7 @@ public class PresentationControllerApiDocs { "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", "@context": [ "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", "https://w3id.org/security/suites/jws-2020/v1" ], "type": [ @@ -155,7 +177,7 @@ public class PresentationControllerApiDocs { @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Tag(name = API_TAG_VERIFIABLE_PRESENTATIONS_VALIDATION) - @Operation(summary = "Validate Verifiable Presentation", description = "Permission: **view_wallets** OR **view_wallet** \n\n Validate Verifiable Presentation with all included credentials") + @Operation(summary = "Validate Verifiable Presentation", description = "Permission: **view_wallets** OR **view_wallet** \n\n Validate Verifiable Presentation with all included credentials", security = { @SecurityRequirement(name = "Authenticate using access_token") }) @ApiResponses(value = { @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { @Content(examples = {}) }), @@ -251,7 +273,7 @@ public class PresentationControllerApiDocs { "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", "@context": [ "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", "https://w3id.org/security/suites/jws-2020/v1" ], "type": [ @@ -286,4 +308,100 @@ public class PresentationControllerApiDocs { public @interface PostVerifiablePresentationValidationApiDocs { } + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + @Tag(name = API_TAG_VERIFIABLE_PRESENTATIONS_GENERATION) + @Operation(summary = "Create Verifiable Presentation", description = "Create a verifiable presentation for the verifiable credential types listed in STS token", security = { @SecurityRequirement(name = "sts_token") }) + @ApiResponses(value = { + @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { + @Content(examples = {}) }), + @ApiResponse(responseCode = "403", description = "The request could not be completed due to a forbidden scope value", content = { + @Content(examples = {}) }), + @ApiResponse(responseCode = "500", description = "Any other internal server error", content = { + @Content(examples = { + @ExampleObject(name = "Internal server error", value = """ + { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + """) + }) }), + @ApiResponse(responseCode = "404", description = "One or more of the requested verifiable credential types were not found", content = { + @Content(examples = { + @ExampleObject(name = "One or more of the requested verifiable credential types were not found", value = """ + { + "type": "about:blank", + "title": "Error Title", + "status": 404, + "detail": "Verifiable credential types that were not found", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + """) + }) }), + @ApiResponse(responseCode = "200", description = "Verifiable Presentation", content = { + @Content(examples = { + @ExampleObject(name = "VP as Json-LD", value = """ + { + "vp": { + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "id": "did:web:localhost:BPNL000000000000#b2e69e47-95f3-48ff-af30-eaaab36431d5", + "type": [ + "VerifiablePresentation" + ], + "verifiableCredential": [ + { + "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "type": [ + "VerifiableCredential", + "BpnCredential" + ], + "issuer": "did:web:localhost:BPNL000000000000", + "expirationDate": "2024-12-31T18:30:00Z", + "issuanceDate": "2023-07-19T09:11:34Z", + "credentialSubject": [ + { + "bpn": "BPNL000000000000", + "id": "did:web:localhost:BPNL000000000000", + "type": "BpnCredential" + } + ], + "proof": { + "created": "2023-07-19T09:11:39Z", + "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", + "proofPurpose": "proofPurpose", + "type": "JsonWebSignature2020", + "verificationMethod": "did:web:localhost:BPNL000000000000#" + } + } + ] + } + } + """), + @ExampleObject(name = "VP as JWT", value = """ + { + "vp": "eyJraWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJzdWIiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwiYXVkIjoic21hcnQiLCJpc3MiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwidnAiOnsiaWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIzM4ZTU2ZTg1LTNkODQtNGEyNS1iZjg1LWFiMjRlYzY4MmMwOSIsInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vY2F0ZW5heC1uZy5naXRodWIuaW8vcHJvZHVjdC1jb3JlLXNjaGVtYXMvYnVzaW5lc3NQYXJ0bmVyRGF0YS5qc29uIiwiaHR0cHM6Ly93M2lkLm9yZy9zZWN1cml0eS9zdWl0ZXMvandzLTIwMjAvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIkJwbkNyZWRlbnRpYWwiXSwiaWQiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwI2Y3M2UzNjMxLWJhODctNGEwMy1iZWEzLWIyODcwMDA1Njg3OSIsImlzc3VlciI6ImRpZDp3ZWI6bG9jYWxob3N0OkJQTkwwMDAwMDAwMDAwMDAiLCJpc3N1YW5jZURhdGUiOiIyMDIzLTA3LTE5VDA5OjExOjM0WiIsImV4cGlyYXRpb25EYXRlIjoiMjAyNC0xMi0zMVQxODozMDowMFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDp3ZWI6bG9jYWxob3N0OkJQTkwwMDAwMDAwMDAwMDAiLCJicG4iOiJCUE5MMDAwMDAwMDAwMDAwIiwidHlwZSI6IkJwbkNyZWRlbnRpYWwifSwicHJvb2YiOnsicHJvb2ZQdXJwb3NlIjoicHJvb2ZQdXJwb3NlIiwidHlwZSI6Ikpzb25XZWJTaWduYXR1cmUyMDIwIiwidmVyaWZpY2F0aW9uTWV0aG9kIjoiZGlkOndlYjpsb2NhbGhvc3Q6QlBOTDAwMDAwMDAwMDAwMCMiLCJjcmVhdGVkIjoiMjAyMy0wNy0xOVQwOToxMTozOVoiLCJqd3MiOiJleUpoYkdjaU9pSkZaRVJUUVNKOS4uZmRuMnFVODVhdU9sdGRIRExkSEk3c0pWVjFaUGRmdHBpWGRfbmRYTjBkRmdTRFdpSXJTY2REMDN3dHZLTHFfSC1zaFFXZmgyUlllTW1ybEV6QWhmRHcifX19LCJleHAiOjE2ODk4MzQ4MDUsImp0aSI6ImIwODYzOWZiLWQ5MWEtNGUwZS1iNmY4LTYzYjdhMzQ1ZTRhZiJ9.80x0AB-OauefdeZfx1cwhitdVKRvCRFeFzYwU73DL7y4w34vu6BdfHWLBGjkwELxkQEoFfiTPOqtuyqhtsyDBg" + } + """) + }) + }) + }) + public @interface GetVerifiablePresentationIATPApiDocs { + } + } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/SecureTokenControllerApiDoc.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/SecureTokenControllerApiDoc.java new file mode 100644 index 000000000..6219ce535 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/SecureTokenControllerApiDoc.java @@ -0,0 +1,140 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.apidocs; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirements; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public class SecureTokenControllerApiDoc { + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + @SecurityRequirements + @RequestBody(content = { + @Content(examples = { + @ExampleObject(name = "Request Secure Token using Scopes", value = """ + { + "audience": "BPNL000000000009", + "client_id": "your_client_id", + "client_secret": "your_client_secret", + "grant_type": "client_credentials", + "bearer_access_scope": "org.eclipse.tractusx.vc.type:ValidCredentialType:read" + } + """ + ), + @ExampleObject(name = "Request Secure Token using Access Token", value = """ + { + "audience": "BPNL000000000009", + "client_id": "your_client_id", + "client_secret": "your_client_secret", + "grant_type": "client_credentials", + "access_token": "a_jwt_token" + } + """ + ) + }) + }) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", content = { + @Content(examples = { + @ExampleObject(name = "Success response", value = """ + { + "token": "a_jwt_token", + "expiresAt": 1706888709315 + } + """ + ) + }) + }), + + @ApiResponse(responseCode = "400", content = { + @Content(examples = { + @ExampleObject(name = "Unknown BPN", value = """ + { + "error": "UnknownBusinessPartnerNumber", + "errorDescription": "The provided BPN 'BPNL000000000001' is unknown" + } + """ + ), + + @ExampleObject(name = "Wrong Grant Type", value = """ + { + "error": "UnsupportedGrantTypeException", + "errorDescription": "The provided 'grant_type' is not valid. Use 'client_credentials'." + } + """ + ), + + @ExampleObject(name = "Invalid idp Token Response", value = """ + { + "error": "InvalidIdpTokenResponse", + "errorDescription": "The idp response cannot be null. Possible causes for this are: the 'clientId' is invalid, or the 'client' is not enabled." + } + """ + ), + + @ExampleObject(name = "Invalid Secure Token Request", value = """ + { + "error": "InvalidSecureTokenRequest", + "errorDescription": "The provided data could not be used to create and sign a token." + } + """ + ) + }) + }), + + @ApiResponse(responseCode = "500", description = "Any other internal server error", content = { + @Content(examples = { + @ExampleObject(name = "Internal server error", value = """ + { + "type": "about:blank", + "title": "Error Title", + "status": 500, + "detail": "Error Details", + "instance": "API endpoint", + "properties": { + "timestamp": 1689762476720 + } + } + """ + ) + }) + }) + }) + @Operation(summary = "Create and Sign Access Tokens", description = "The endpoint for creating and signing access tokens which are to be used during a verifiable presentation flow.", method = "tokenJson") + public @interface PostSecureTokenDocJson { + } + + @Operation(summary = "Create and Sign Access Tokens", description = "The endpoint for creating and signing access tokens which are to be used during a verifiable presentation flow.", method = "tokenFormUrlencoded") + public @interface PostSecureTokenDocFormUrlencoded { + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/WalletControllerApiDocs.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/WalletControllerApiDocs.java similarity index 91% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/WalletControllerApiDocs.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/WalletControllerApiDocs.java index e681a14bb..f7ac51627 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/WalletControllerApiDocs.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/WalletControllerApiDocs.java @@ -1,9 +1,25 @@ -package org.eclipse.tractusx.managedidentitywallets.apidocs; +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +package org.eclipse.tractusx.managedidentitywallets.apidocs; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -12,6 +28,12 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; public class WalletControllerApiDocs { @@ -22,8 +44,9 @@ public class WalletControllerApiDocs { @ExampleObject(name = "Create wallet with BPN", value = """ { - "bpn": "BPNL000000000001", - "name": "companyA" + "businessPartnerNumber": "BPNL000000000001", + "companyName": "companyA", + "didUrl": "portal.com:BPNL000000000001" } """) }) @@ -104,7 +127,7 @@ public class WalletControllerApiDocs { [ { "controller": "did:web:localhost:BPNL000000000001", - "id": "did:web:localhost:BPNL000000000001#", + "id": "did:web:localhost:BPNL000000000001#key-1", "publicKeyJwk": { "crv": "Ed25519", @@ -112,6 +135,18 @@ public class WalletControllerApiDocs { "x": "0Ap6FsX5UuRBIoOzxWtcFA2ymnqXw0U08Ino_mIuYM4" }, "type": "JsonWebKey2020" + }, + { + "controller": "did:web:localhost:BPNL000000000001", + "id": "did:web:localhost:BPNL000000000001#key-2", + "publicKeyJwk": + { + "crv": "secp256k1", + "kty": "EC", + "x": "f9PkTOpsbcgKe_-s6bNCve3-aB1VZAFsCub8C5bhDn0", + "y": "xH1d7jCFavolGVZtaWcZZGP2nLuEsamDCotD56llxUk" + }, + "type": "JsonWebKey2020" } ] } @@ -120,13 +155,13 @@ public class WalletControllerApiDocs { }) }) }) - @Operation(summary = "Create Wallet", description = "Permission: **add_wallets** (The BPN of the base wallet must equal BPN of caller)\n\n Create a wallet and store it") + @Operation(summary = "Create Wallet", description = "Permission: **add_wallets** (The BPN of the base wallet must equal BPN of caller)\n\n Create a wallet and store it", security = { @SecurityRequirement(name = "Authenticate using access_token") }) public @interface CreateWalletApiDoc { } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) - @Operation(summary = "Store Verifiable Credential", description = "Permission: **update_wallets** OR **update_wallet** (The BPN of wallet to extract credentials from must equal BPN of caller) \n\n Store a verifiable credential in the wallet of the given identifier") + @Operation(summary = "Store Verifiable Credential", description = "Permission: **update_wallets** OR **update_wallet** (The BPN of wallet to extract credentials from must equal BPN of caller) \n\n Store a verifiable credential in the wallet of the given identifier", security = { @SecurityRequirement(name = "Authenticate using access_token") }) @RequestBody(content = { @Content(examples = @ExampleObject(""" { @@ -363,7 +398,7 @@ public class WalletControllerApiDocs { "@context": [ "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", + "https://eclipse-tractusx.github.io/tractusx-profiles/cx/context/credentials.context.json", "https://w3id.org/security/suites/jws-2020/v1" ], "id": "did:web:localhost:BPNL000000000000#a1f8ae36-9919-4ed8-8546-535280acc5bf", @@ -396,7 +431,7 @@ public class WalletControllerApiDocs { } """) }) }) }) - @Operation(summary = "Retrieve wallet by BPN", description = "Permission: **view_wallets** OR **view_wallet** (The BPN of Wallet to retrieve must equal the BPN of caller or Base wallet, authority wallet can see all wallets) \n\n Retrieve single wallet by identifier, with or without its credentials") + @Operation(summary = "Retrieve wallet by BPN", description = "Permission: **view_wallets** OR **view_wallet** (The BPN of Wallet to retrieve must equal the BPN of caller or Base wallet, authority wallet can see all wallets) \n\n Retrieve single wallet by identifier, with or without its credentials", security = { @SecurityRequirement(name = "Authenticate using access_token") }) public @interface RetrieveWalletApiDoc { } @@ -503,7 +538,7 @@ public class WalletControllerApiDocs { }) }) }) - @Operation(summary = "List of wallets", description = "Permission: **view_wallets** \n\n Retrieve list of registered wallets") + @Operation(summary = "List of wallets", description = "Permission: **view_wallets** \n\n Retrieve list of registered wallets", security = { @SecurityRequirement(name = "Authenticate using access_token") }) public @interface RetrieveWalletsApiDoc { } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/command/GetCredentialsCommand.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/command/GetCredentialsCommand.java new file mode 100644 index 000000000..c9d563008 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/command/GetCredentialsCommand.java @@ -0,0 +1,43 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.command; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Builder +@Getter +@Setter +public class GetCredentialsCommand { + private String credentialId; + private String identifier; + private List type; + private String sortColumn; + private String sortType; + private int pageNumber; + private int size; + private boolean asJwt; + private String callerBPN; +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ApplicationConfig.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ApplicationConfig.java similarity index 72% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ApplicationConfig.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ApplicationConfig.java index a8cb28daf..9cefda803 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ApplicationConfig.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ApplicationConfig.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -28,6 +28,10 @@ import com.smartsensesolutions.java.commons.specification.SpecificationUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.text.StringEscapeUtils; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.signing.KeyProvider; +import org.eclipse.tractusx.managedidentitywallets.signing.LocalSigningService; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; import org.springdoc.core.properties.SwaggerUiConfigProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -40,6 +44,9 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.nio.charset.StandardCharsets; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; /** * The type Application config. @@ -50,11 +57,13 @@ public class ApplicationConfig implements WebMvcConfigurer { private final SwaggerUiConfigProperties properties; private final String resourceBundlePath; + private final MIWSettings miwSettings; @Autowired - public ApplicationConfig(@Value("${resource.bundle.path:classpath:i18n/language}") String resourceBundlePath, SwaggerUiConfigProperties properties) { + public ApplicationConfig(@Value("${resource.bundle.path:classpath:i18n/language}") String resourceBundlePath, SwaggerUiConfigProperties properties, MIWSettings miwSettings) { this.resourceBundlePath = resourceBundlePath; this.properties = properties; + this.miwSettings = miwSettings; } /** @@ -98,4 +107,23 @@ public LocalValidatorFactoryBean validator() { beanValidatorFactory.setValidationMessageSource(messageSource()); return beanValidatorFactory; } + + @Bean + public Map availableKeyStorages(List storages, List keyProviders) { + KeyProvider localSigningKeyProvider = keyProviders.stream().filter(s -> s.getKeyStorageType().equals(miwSettings.localSigningKeyStorageType())) + .findFirst() + .orElseThrow(() -> new IllegalStateException("no key provider with type %s found".formatted(miwSettings.localSigningKeyStorageType()))); + + Map available = new EnumMap<>(SigningServiceType.class); + storages.forEach( + s -> { + if(s instanceof LocalSigningService local){ + local.setKeyProvider(localSigningKeyProvider); + } + available.put(s.getSupportedServiceType(), s); + } + ); + + return available; + } } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ExceptionHandling.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ExceptionHandling.java similarity index 86% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ExceptionHandling.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ExceptionHandling.java index 408633976..95b00550c 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ExceptionHandling.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ExceptionHandling.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -26,8 +26,14 @@ import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.eclipse.tractusx.managedidentitywallets.exception.*; -import org.eclipse.tractusx.ssi.lib.exception.NoVerificationKeyFoundExcpetion; +import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; +import org.eclipse.tractusx.managedidentitywallets.exception.CredentialNotFoundProblem; +import org.eclipse.tractusx.managedidentitywallets.exception.DuplicateWalletProblem; +import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; +import org.eclipse.tractusx.managedidentitywallets.exception.MissingVcTypesException; +import org.eclipse.tractusx.managedidentitywallets.exception.PermissionViolationException; +import org.eclipse.tractusx.managedidentitywallets.exception.WalletNotFoundProblem; +import org.eclipse.tractusx.ssi.lib.exception.proof.NoVerificationKeyFoundException; import org.springframework.data.mapping.PropertyReferenceException; import org.springframework.http.HttpStatus; import org.springframework.http.ProblemDetail; @@ -141,19 +147,6 @@ ProblemDetail handleValidation(ConstraintViolationException exception) { return problemDetail; } - /** - * Handle duplicate credential problem problem detail. - * - * @param e the e - * @return the problem detail - */ - @ExceptionHandler({DuplicateCredentialProblem.class, DuplicateSummaryCredentialProblem.class}) - ProblemDetail handleDuplicateCredentialProblem(RuntimeException e) { - ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT, e.getMessage()); - problemDetail.setTitle(e.getMessage()); - problemDetail.setProperty(TIMESTAMP, System.currentTimeMillis()); - return problemDetail; - } /** * Handle not found credential problem detail. @@ -205,8 +198,8 @@ ProblemDetail handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismat * @param exception the exception * @return the problem detail */ - @ExceptionHandler(NoVerificationKeyFoundExcpetion.class) - ProblemDetail handleNoVerificationKeyFoundException(NoVerificationKeyFoundExcpetion exception) { + @ExceptionHandler(NoVerificationKeyFoundException.class) + ProblemDetail handleNoVerificationKeyFoundException(NoVerificationKeyFoundException exception) { ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, ExceptionUtils.getMessage(exception)); problemDetail.setTitle(ExceptionUtils.getMessage(exception)); problemDetail.setProperty(TIMESTAMP, System.currentTimeMillis()); @@ -244,6 +237,21 @@ ProblemDetail handleJsonLdError(JsonLdError exception) { return problemDetail; } + @ExceptionHandler(MissingVcTypesException.class) + ProblemDetail handleMissingVcTypesException(MissingVcTypesException exception) { + ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ExceptionUtils.getMessage(exception)); + problemDetail.setTitle(ExceptionUtils.getMessage(exception)); + problemDetail.setProperty(TIMESTAMP, System.currentTimeMillis()); + return problemDetail; + } + + @ExceptionHandler(PermissionViolationException.class) + ProblemDetail handlePermissionViolationException(PermissionViolationException exception) { + ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.FORBIDDEN, ExceptionUtils.getMessage(exception)); + problemDetail.setTitle(ExceptionUtils.getMessage(exception)); + problemDetail.setProperty(TIMESTAMP, System.currentTimeMillis()); + return problemDetail; + } /** * Handle exception problem detail. diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java similarity index 73% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java index 95c3bb747..7465f6517 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -21,13 +21,14 @@ package org.eclipse.tractusx.managedidentitywallets.config; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyStorageType; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.format.annotation.DateTimeFormat; import java.net.URI; import java.util.Date; import java.util.List; -import java.util.Set; /** * The type Miw settings. @@ -35,9 +36,11 @@ @ConfigurationProperties(prefix = "miw") public record MIWSettings(String host, String encryptionKey, String authorityWalletBpn, String authorityWalletDid, String authorityWalletName, - List vcContexts, List summaryVcContexts, + List vcContexts, @DateTimeFormat(pattern = "dd-MM-yyyy") Date vcExpiryDate, - Set supportedFrameworkVCTypes, - boolean enforceHttps, String contractTemplatesUrl, - List didDocumentContextUrls) { -} \ No newline at end of file + boolean enforceHttps, + List didDocumentContextUrls, + KeyStorageType localSigningKeyStorageType, + SigningServiceType authoritySigningServiceType) { +} + diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/openapi/OpenApiConfig.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/openapi/OpenApiConfig.java similarity index 77% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/config/openapi/OpenApiConfig.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/openapi/OpenApiConfig.java index 76dd3f645..957aad231 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/openapi/OpenApiConfig.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/openapi/OpenApiConfig.java @@ -99,15 +99,30 @@ private OpenAPI enableSecurity(OpenAPI openAPI) { String accessTokenAuth = "Authenticate using access_token"; components.addSecuritySchemes(accessTokenAuth, new SecurityScheme().name(accessTokenAuth) - .description("**Bearer (apiKey)** \n" + - "JWT Authorization header using the Bearer scheme.\n" + - "\n" + - "Enter **Bearer** [space] and then your token in the text input below.\n" + - "\n" + - "Example: Bearer 12345abcdef") + .description(""" + **Bearer (apiKey)** + JWT Authorization header using the Bearer scheme. + Enter **Bearer** [space] and then your token in the text input below: + Example: Bearer 12345abcdef + """) .type(SecurityScheme.Type.APIKEY).in(SecurityScheme.In.HEADER).name(HttpHeaders.AUTHORIZATION)); + + //Auth using sts_token + String stsTokenAuth = "sts_token"; + components.addSecuritySchemes(stsTokenAuth, + new SecurityScheme().name(stsTokenAuth) + .description(""" + **STS token** + JWT Authorization header. + Enter your token in the text input below: + Example: 12345abcdef + """) + .type(SecurityScheme.Type.APIKEY).in(SecurityScheme.In.HEADER).name(HttpHeaders.AUTHORIZATION)); + return openAPI.components(components) .addSecurityItem(new SecurityRequirement() - .addList(accessTokenAuth, Collections.emptyList())); + .addList(accessTokenAuth, Collections.emptyList())) + .addSecurityItem(new SecurityRequirement() + .addList(stsTokenAuth, Collections.emptyList())); } -} \ No newline at end of file +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/CustomAuthenticationConverter.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/CustomAuthenticationConverter.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/CustomAuthenticationConverter.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/CustomAuthenticationConverter.java diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/PresentationIatpFilter.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/PresentationIatpFilter.java new file mode 100644 index 000000000..61ca5acd0 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/PresentationIatpFilter.java @@ -0,0 +1,82 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.config.security; + +import io.micrometer.common.util.StringUtils; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; +import org.eclipse.tractusx.managedidentitywallets.dto.ValidationResult; +import org.eclipse.tractusx.managedidentitywallets.service.STSTokenValidationService; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.web.filter.GenericFilterBean; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COMA_SEPARATOR; + +public class PresentationIatpFilter extends GenericFilterBean { + + RequestMatcher customFilterUrl = new AntPathRequestMatcher(RestURI.API_PRESENTATIONS_IATP); + + STSTokenValidationService validationService; + + public PresentationIatpFilter(STSTokenValidationService validationService) { + this.validationService = validationService; + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + + if (customFilterUrl.matches(httpServletRequest)) { + String authHeader = httpServletRequest.getHeader("Authorization"); + if (StringUtils.isEmpty(authHeader)) { + httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } else { + ValidationResult result = validationService.validateToken(authHeader); + if (!result.isValid()) { + List errorValues = new ArrayList<>(); + result.getErrors().forEach(c -> errorValues.add(c.name())); + String content = String.join(COMA_SEPARATOR, errorValues); + + httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + httpServletResponse.setContentLength(content.length()); + httpServletResponse.getWriter().write(content); + } else { + chain.doFilter(request, response); + } + } + } else { + chain.doFilter(request, response); + } + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java similarity index 85% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java index aa3eafe0c..59bce9fad 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -25,9 +25,13 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.managedidentitywallets.constant.ApplicationRole; import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; +import org.eclipse.tractusx.managedidentitywallets.service.STSTokenValidationService; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationEventPublisher; +import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -36,6 +40,7 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import static org.springframework.http.HttpMethod.GET; @@ -51,6 +56,8 @@ @AllArgsConstructor public class SecurityConfig { + private final STSTokenValidationService validationService; + private final SecurityConfigProperties securityConfigProperties; /** @@ -65,12 +72,16 @@ public class SecurityConfig { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.cors(Customizer.withDefaults()) .csrf(AbstractHttpConfigurer::disable) - .headers(httpSecurityHeadersConfigurer -> httpSecurityHeadersConfigurer.xssProtection(Customizer.withDefaults()).contentSecurityPolicy(contentSecurityPolicyConfig -> contentSecurityPolicyConfig.policyDirectives("script-src 'self'"))) + .headers(httpSecurityHeadersConfigurer -> httpSecurityHeadersConfigurer + .xssProtection(Customizer.withDefaults()) + .contentSecurityPolicy(contentSecurityPolicyConfig -> contentSecurityPolicyConfig.policyDirectives("script-src 'self'"))) .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests.requestMatchers(new AntPathRequestMatcher("/")).permitAll() // forwards to swagger .requestMatchers(new AntPathRequestMatcher("/docs/api-docs/**")).permitAll() .requestMatchers(new AntPathRequestMatcher("/ui/swagger-ui/**")).permitAll() .requestMatchers(new AntPathRequestMatcher("/actuator/health/**")).permitAll() + .requestMatchers(new AntPathRequestMatcher("/api/token", POST.name())).permitAll() + .requestMatchers(new AntPathRequestMatcher("/api/presentations/iatp", GET.name())).permitAll() .requestMatchers(new AntPathRequestMatcher("/actuator/loggers/**")).hasRole(ApplicationRole.ROLE_MANAGE_APP) //did document resolve APIs @@ -99,14 +110,13 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { //VC - Issuer .requestMatchers(new AntPathRequestMatcher(RestURI.ISSUERS_CREDENTIALS, GET.name())).hasAnyRole(ApplicationRole.ROLE_UPDATE_WALLETS) //Lis of issuer VC .requestMatchers(new AntPathRequestMatcher(RestURI.ISSUERS_CREDENTIALS, POST.name())).hasAnyRole(ApplicationRole.ROLE_UPDATE_WALLETS) //Issue VC - .requestMatchers(new AntPathRequestMatcher(RestURI.CREDENTIALS_ISSUER_MEMBERSHIP, POST.name())).hasAnyRole(ApplicationRole.ROLE_UPDATE_WALLETS) //issue Membership Credential - .requestMatchers(new AntPathRequestMatcher(RestURI.CREDENTIALS_ISSUER_DISMANTLER, POST.name())).hasAnyRole(ApplicationRole.ROLE_UPDATE_WALLETS) //issue dismantler Credential - .requestMatchers(new AntPathRequestMatcher(RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, POST.name())).hasAnyRole(ApplicationRole.ROLE_UPDATE_WALLETS) //issue dismantler Credential //error .requestMatchers(new AntPathRequestMatcher("/error")).permitAll() ).oauth2ResourceServer(resourceServer -> resourceServer.jwt(jwt -> - jwt.jwtAuthenticationConverter(new CustomAuthenticationConverter(securityConfigProperties.clientId())))); + jwt.jwtAuthenticationConverter(new CustomAuthenticationConverter(securityConfigProperties.clientId())))) + .addFilterAfter(new PresentationIatpFilter(validationService), BasicAuthenticationFilter.class); + return http.build(); } @@ -121,4 +131,13 @@ public WebSecurityCustomizer securityCustomizer() { log.warn("Disable security : This is not recommended to use in production environments."); return web -> web.ignoring().requestMatchers(new AntPathRequestMatcher("**")); } + + /** + * Needed to enable an event-listener for failed login attempts. + */ + @Bean + public AuthenticationEventPublisher authenticationEventPublisher + (ApplicationEventPublisher applicationEventPublisher) { + return new DefaultAuthenticationEventPublisher(applicationEventPublisher); + } } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfigProperties.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfigProperties.java similarity index 92% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfigProperties.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfigProperties.java index 12156833f..c8c0c87f5 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfigProperties.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfigProperties.java @@ -29,7 +29,9 @@ @ConfigurationProperties("miw.security") public record SecurityConfigProperties(Boolean enabled, String clientId, + String realm, + String authServerUrl, String authUrl, String tokenUrl, String refreshTokenUrl) { -} \ No newline at end of file +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityEvents.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityEvents.java new file mode 100644 index 000000000..841bd3fdf --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityEvents.java @@ -0,0 +1,44 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.config.security; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent; +import org.springframework.security.authorization.event.AuthorizationDeniedEvent; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class SecurityEvents { + @EventListener + public void onFailure(AbstractAuthenticationFailureEvent failures) { + String excMessage = failures.getException().getMessage(); + log.warn("Failed Authentication: Invalid 'Bearer' token. {}", excMessage); + } + + @EventListener + public void onFailure(AuthorizationDeniedEvent failure) { + log.warn("Failed Authorization: Missing 'Authorization' header."); + } +} + diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/ApplicationRole.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/ApplicationRole.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/ApplicationRole.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/ApplicationRole.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java similarity index 80% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java index a52149feb..764a0af4d 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -39,6 +39,9 @@ private RestURI() { * The constant DID_DOCUMENTS. */ public static final String DID_DOCUMENTS = "/api/didDocuments/{identifier}"; + /** + * The constant DID_RESOLVE. + */ public static final String DID_RESOLVE = "/{bpn}/did.json"; /** * The constant WALLETS_BY_BPN. @@ -54,28 +57,27 @@ private RestURI() { */ public static final String CREDENTIALS = "/api/credentials"; - - public static final String CREDENTIALS_VALIDATION = "/api/credentials/validation"; - - - public static final String ISSUERS_CREDENTIALS = "/api/credentials/issuer"; - /** - * The constant CREDENTIALS_ISSUER_MEMBERSHIP. + * The constant CREDENTIALS_VALIDATION. */ - public static final String CREDENTIALS_ISSUER_MEMBERSHIP = "/api/credentials/issuer/membership"; + public static final String CREDENTIALS_VALIDATION = "/api/credentials/validation"; /** - * The constant CREDENTIALS_ISSUER_DISMANTLER. + * The constant ISSUERS_CREDENTIALS. */ - public static final String CREDENTIALS_ISSUER_DISMANTLER = "/api/credentials/issuer/dismantler"; + public static final String ISSUERS_CREDENTIALS = "/api/credentials/issuer"; /** - * The constant API_CREDENTIALS_ISSUER_FRAMEWORK. + * The constant API_PRESENTATIONS. */ - public static final String API_CREDENTIALS_ISSUER_FRAMEWORK = "/api/credentials/issuer/framework"; - public static final String API_PRESENTATIONS = "/api/presentations"; + /** + * The constant API_PRESENTATIONS_VALIDATION. + */ public static final String API_PRESENTATIONS_VALIDATION = "/api/presentations/validation"; + /** + * The constant API_PRESENTATIONS_IATP. + */ + public static final String API_PRESENTATIONS_IATP = "/api/presentations/iatp"; } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/StringPool.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/StringPool.java similarity index 74% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/StringPool.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/StringPool.java index 1d05be7d2..dc137b8b8 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/StringPool.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/StringPool.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -27,19 +27,15 @@ public class StringPool { public static final String CREDENTIAL_ID = "credentialId"; - public static final String VALUE = "value"; - public static final String CONTRACT_VERSION = "contractVersion"; - public static final String ACTIVITY_TYPE = "activityType"; - public static final String ALLOWED_VEHICLE_BRANDS = "allowedVehicleBrands"; + public static final String VERIFIABLE_CREDENTIALS = "verifiableCredentials"; public static final String VP = "vp"; + public static final String VC = "vc"; public static final String VALID = "valid"; public static final String VALIDATE_AUDIENCE = "validateAudience"; public static final String VALIDATE_EXPIRY_DATE = "validateExpiryDate"; public static final String VALIDATE_JWT_EXPIRY_DATE = "validateJWTExpiryDate"; public static final String DID_DOCUMENT = "didDocument"; - public static final String VEHICLE_DISMANTLE = "vehicleDismantle"; - public static final String CREATED_AT = "createdAt"; private StringPool() { throw new IllegalStateException("Constant class"); @@ -48,13 +44,7 @@ private StringPool() { public static final String ISSUER_DID = "issuerDid"; public static final String HOLDER_DID = "holderDid"; public static final String HOLDER_IDENTIFIER = "holderIdentifier"; - public static final String NAME = "name"; - public static final String CONTRACT_TEMPLATE = "contractTemplate"; public static final String TYPE = "type"; - public static final String MEMBER_OF = "memberOf"; - public static final String STATUS = "status"; - public static final String START_TIME = "startTime"; - public static final String ED_25519 = "ED25519"; @@ -68,13 +58,8 @@ private StringPool() { */ public static final String BPN = "bpn"; - public static final String BPN_UPPER_CASE = "BPN"; - public static final String ID = "id"; - public static final String ITEMS = "items"; - - public static final String CLIENT_ID = "miw_private_client"; public static final String CLIENT_SECRET = "miw_private_client_secret"; @@ -91,4 +76,22 @@ private StringPool() { public static final String BEARER_SPACE = "Bearer "; public static final String BPN_NUMBER_REGEX = "^(BPN)(L|S|A)[0-9A-Z]{12}"; + + public static final String W3_ID_JWS_2020_V1_CONTEXT_URL = "https://w3id.org/security/suites/jws-2020/v1"; + + public static final String COMA_SEPARATOR = ", "; + public static final String BLANK_SEPARATOR = " "; + public static final String COLON_SEPARATOR = ":"; + public static final String UNDERSCORE = "_"; + + public static final String REFERENCE_KEY = "dummy ref key, removed once vault setup is ready"; + public static final String VAULT_ACCESS_TOKEN = "dummy vault access token, removed once vault setup is ready"; + + public static final String PRIVATE_KEY = "PRIVATE KEY"; + public static final String PUBLIC_KEY = "PUBLIC KEY"; + public static final String VC_JWT_KEY = "jwt"; + + public static final String AS_JWT = "asJwt"; + + public static final String BPN_CREDENTIAL = "BpnCredential"; } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/MIWVerifiableCredentialType.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/SupportedAlgorithms.java similarity index 58% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/MIWVerifiableCredentialType.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/SupportedAlgorithms.java index a8c49e3f1..227fa1347 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/MIWVerifiableCredentialType.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/SupportedAlgorithms.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -21,23 +21,19 @@ package org.eclipse.tractusx.managedidentitywallets.constant; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; +public enum SupportedAlgorithms { -/** - * The type Miw verifiable credential type. - */ -public class MIWVerifiableCredentialType extends VerifiableCredentialType { - - public static final String DISMANTLER_CREDENTIAL = "DismantlerCredential"; - - /** - * The constant USE_CASE_FRAMEWORK_CONDITION_CX. - */ - public static final String USE_CASE_FRAMEWORK_CONDITION = "UseCaseFrameworkCondition"; - - public static final String BPN_CREDENTIAL = "BpnCredential"; + ED25519("ED25519"), + ES256K("ES256K"); - public static final String SUMMARY_CREDENTIAL = "SummaryCredential"; + private String value; + SupportedAlgorithms(String value){ + this.value = value; + } + @Override + public String toString() { + return value; + } } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/TokenValidationErrors.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/TokenValidationErrors.java new file mode 100644 index 000000000..e90b784cf --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/TokenValidationErrors.java @@ -0,0 +1,42 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.constant; + + +public enum TokenValidationErrors { + + ACCESS_TOKEN_MISSING, + ISS_AND_SUB_NOT_EQUAL, + SUB_NOT_MATCH_ANY_DID, + SUB_NOT_DID, + EXP_MISSING, + TOKEN_ALREADY_EXPIRED, + IAT_AFTER_EXPIRATION, + CURRENT_TIME_BEFORE_IAT, + AUD_MISSING, + AUD_NOT_DID, + AUD_CLAIMS_NOT_EQUAL, + NONCE_MISSING, + NONCE_CLAIMS_NOT_EQUAL, + SIGNATURE_NOT_VERIFIED, + IAT_MISSING +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/BaseController.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/BaseController.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/BaseController.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/BaseController.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/DidDocumentController.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/DidDocumentController.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/DidDocumentController.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/DidDocumentController.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/HoldersCredentialController.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/HoldersCredentialController.java similarity index 76% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/HoldersCredentialController.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/HoldersCredentialController.java index d2d17a20b..9d55124df 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/HoldersCredentialController.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/HoldersCredentialController.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -22,23 +22,30 @@ package org.eclipse.tractusx.managedidentitywallets.controller; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.managedidentitywallets.apidocs.HoldersCredentialControllerApiDocs.GetCredentialsApiDocs; import org.eclipse.tractusx.managedidentitywallets.apidocs.HoldersCredentialControllerApiDocs.IssueCredentialApiDoc; +import org.eclipse.tractusx.managedidentitywallets.apidocs.IssuersCredentialControllerApiDocs.AsJwtParam; +import org.eclipse.tractusx.managedidentitywallets.command.GetCredentialsCommand; import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; +import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.dto.CredentialsResponse; import org.eclipse.tractusx.managedidentitywallets.service.HoldersCredentialService; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; import org.springframework.data.domain.PageImpl; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.ExampleObject; -import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import java.security.Principal; import java.util.List; @@ -69,7 +76,7 @@ public class HoldersCredentialController extends BaseController { */ @GetCredentialsApiDocs @GetMapping(path = RestURI.CREDENTIALS, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> getCredentials(@Parameter(name = "credentialId", description = "Credential Id", examples = {@ExampleObject(name = "Credential Id", value = "did:web:localhost:BPNL000000000000#12528899-160a-48bd-ba15-f396c3959ae9")}) @RequestParam(required = false) String credentialId, + public ResponseEntity> getCredentials(@Parameter(name = "credentialId", description = "Credential Id", examples = {@ExampleObject(name = "Credential Id", value = "did:web:localhost:BPNL000000000000#12528899-160a-48bd-ba15-f396c3959ae9")}) @RequestParam(required = false) String credentialId, @Parameter(name = "issuerIdentifier", description = "Issuer identifier(did of BPN)", examples = {@ExampleObject(name = "bpn", value = "BPNL000000000000", description = "bpn"), @ExampleObject(description = "did", name = "did", value = "did:web:localhost:BPNL000000000000")}) @RequestParam(required = false) String issuerIdentifier, @Parameter(name = "type", description = "Type of VC", examples = {@ExampleObject(name = "SummaryCredential", value = "SummaryCredential", description = "SummaryCredential"), @ExampleObject(description = "BpnCredential", name = "BpnCredential", value = "BpnCredential")}) @RequestParam(required = false) List type, @Parameter(name = "sortColumn", description = "Sort column name", @@ -85,9 +92,23 @@ public ResponseEntity> getCredentials(@Parameter( @Parameter(name = "sortTpe", description = "Sort order", examples = {@ExampleObject(value = "desc", name = "Descending order"), @ExampleObject(value = "asc", name = "Ascending order")}) @RequestParam(required = false, defaultValue = "desc") String sortTpe, @Min(0) @Max(Integer.MAX_VALUE) @Parameter(description = "Page number, Page number start with zero") @RequestParam(required = false, defaultValue = "0") int pageNumber, @Min(0) @Max(Integer.MAX_VALUE) @Parameter(description = "Number of records per page") @RequestParam(required = false, defaultValue = Integer.MAX_VALUE + "") int size, + @AsJwtParam @RequestParam(name = StringPool.AS_JWT, defaultValue = "false") boolean asJwt, + Principal principal) { log.debug("Received request to get credentials. BPN: {}", getBPNFromToken(principal)); - return ResponseEntity.status(HttpStatus.OK).body(holdersCredentialService.getCredentials(credentialId, issuerIdentifier, type, sortColumn, sortTpe, pageNumber, size, getBPNFromToken(principal))); + final GetCredentialsCommand command; + command = GetCredentialsCommand.builder() + .credentialId(credentialId) + .identifier(issuerIdentifier) + .type(type) + .sortColumn(sortColumn) + .sortType(sortTpe) + .pageNumber(pageNumber) + .size(size) + .asJwt(asJwt) + .callerBPN(getBPNFromToken(principal)) + .build(); + return ResponseEntity.status(HttpStatus.OK).body(holdersCredentialService.getCredentials(command)); } @@ -98,11 +119,13 @@ public ResponseEntity> getCredentials(@Parameter( * @param principal the principal * @return the response entity */ - + @IssueCredentialApiDoc @PostMapping(path = RestURI.CREDENTIALS, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity issueCredential(@RequestBody Map data, Principal principal) { + public ResponseEntity issueCredential(@RequestBody Map data, Principal principal, + @AsJwtParam @RequestParam(name = "asJwt", defaultValue = "false") boolean asJwt + ) { log.debug("Received request to issue credential. BPN: {}", getBPNFromToken(principal)); - return ResponseEntity.status(HttpStatus.CREATED).body(holdersCredentialService.issueCredential(data, getBPNFromToken(principal))); + return ResponseEntity.status(HttpStatus.CREATED).body(holdersCredentialService.issueCredential(data, getBPNFromToken(principal), asJwt)); } } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/IssuersCredentialController.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/IssuersCredentialController.java similarity index 61% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/IssuersCredentialController.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/IssuersCredentialController.java index 90ff993e3..5d3ca437f 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/IssuersCredentialController.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/IssuersCredentialController.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -23,29 +23,29 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ExampleObject; -import jakarta.validation.Valid; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; - +import org.eclipse.tractusx.managedidentitywallets.apidocs.IssuersCredentialControllerApiDocs.AsJwtParam; import org.eclipse.tractusx.managedidentitywallets.apidocs.IssuersCredentialControllerApiDocs.GetCredentialsApiDocs; -import org.eclipse.tractusx.managedidentitywallets.apidocs.IssuersCredentialControllerApiDocs.IssueDismantlerCredentialApiDoc; -import org.eclipse.tractusx.managedidentitywallets.apidocs.IssuersCredentialControllerApiDocs.IssueMembershipCredentialApiDoc; -import org.eclipse.tractusx.managedidentitywallets.apidocs.IssuersCredentialControllerApiDocs.IssueFrameworkCredentialApiDocs; import org.eclipse.tractusx.managedidentitywallets.apidocs.IssuersCredentialControllerApiDocs.IssueVerifiableCredentialUsingBaseWalletApiDocs; import org.eclipse.tractusx.managedidentitywallets.apidocs.IssuersCredentialControllerApiDocs.ValidateVerifiableCredentialApiDocs; +import org.eclipse.tractusx.managedidentitywallets.command.GetCredentialsCommand; import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueDismantlerCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueFrameworkCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueMembershipCredentialRequest; +import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.dto.CredentialVerificationRequest; +import org.eclipse.tractusx.managedidentitywallets.dto.CredentialsResponse; import org.eclipse.tractusx.managedidentitywallets.service.IssuersCredentialService; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; import org.springframework.data.domain.PageImpl; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import java.security.Principal; import java.util.List; @@ -86,7 +86,7 @@ public class IssuersCredentialController extends BaseController { */ @GetCredentialsApiDocs @GetMapping(path = RestURI.ISSUERS_CREDENTIALS, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> getCredentials(@Parameter(name = "credentialId", description = "Credential Id", examples = {@ExampleObject(name = "Credential Id", value = "did:web:localhost:BPNL000000000000#12528899-160a-48bd-ba15-f396c3959ae9")}) @RequestParam(required = false) String credentialId, + public ResponseEntity> getCredentials(@Parameter(name = "credentialId", description = "Credential Id", examples = {@ExampleObject(name = "Credential Id", value = "did:web:localhost:BPNL000000000000#12528899-160a-48bd-ba15-f396c3959ae9")}) @RequestParam(required = false) String credentialId, @Parameter(name = "holderIdentifier", description = "Holder identifier(did of BPN)", examples = {@ExampleObject(name = "bpn", value = "BPNL000000000001", description = "bpn"), @ExampleObject(description = "did", name = "did", value = "did:web:localhost:BPNL000000000001")}) @RequestParam(required = false) String holderIdentifier, @Parameter(name = "type", description = "Type of VC", examples = {@ExampleObject(name = "SummaryCredential", value = "SummaryCredential", description = "SummaryCredential"), @ExampleObject(description = "BpnCredential", name = "BpnCredential", value = "BpnCredential")}) @RequestParam(required = false) List type, @Min(0) @Max(Integer.MAX_VALUE) @Parameter(description = "Page number, Page number start with zero") @RequestParam(required = false, defaultValue = "0") int pageNumber, @@ -99,66 +99,39 @@ public ResponseEntity> getCredentials(@Parameter( @ExampleObject(value = "credentialId", name = "Credential id") } ) @RequestParam(required = false, defaultValue = "createdAt") String sortColumn, - @Parameter(name = "sortTpe", description = "Sort order", examples = {@ExampleObject(value = "desc", name = "Descending order"), @ExampleObject(value = "asc", name = "Ascending order")}) @RequestParam(required = false, defaultValue = "desc") String sortTpe, Principal principal) { + @Parameter(name = "sortTpe", description = "Sort order", examples = { @ExampleObject(value = "desc", name = "Descending order"), @ExampleObject(value = "asc", name = "Ascending order") }) @RequestParam(required = false, defaultValue = "desc") String sortTpe, + @AsJwtParam @RequestParam(name = StringPool.AS_JWT, defaultValue = "false") boolean asJwt, + Principal principal) { log.debug("Received request to get credentials. BPN: {}", getBPNFromToken(principal)); - return ResponseEntity.status(HttpStatus.OK).body(issuersCredentialService.getCredentials(credentialId, holderIdentifier, type, sortColumn, sortTpe, pageNumber, size, getBPNFromToken(principal))); - } - - /** - * Issue membership credential response entity. - * - * @param issueMembershipCredentialRequest the issue membership credential request - * @param principal the principal - * @return the response entity - */ - @IssueMembershipCredentialApiDoc - @PostMapping(path = RestURI.CREDENTIALS_ISSUER_MEMBERSHIP, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity issueMembershipCredential(@Valid @RequestBody IssueMembershipCredentialRequest issueMembershipCredentialRequest, Principal principal) { - log.debug("Received request to issue membership credential. BPN: {}", getBPNFromToken(principal)); - return ResponseEntity.status(HttpStatus.CREATED).body(issuersCredentialService.issueMembershipCredential(issueMembershipCredentialRequest, getBPNFromToken(principal))); + final GetCredentialsCommand command; + command = GetCredentialsCommand.builder() + .credentialId(credentialId) + .identifier(holderIdentifier) + .type(type) + .sortColumn(sortColumn) + .sortType(sortTpe) + .pageNumber(pageNumber) + .size(size) + .asJwt(asJwt) + .callerBPN(getBPNFromToken(principal)) + .build(); + return ResponseEntity.status(HttpStatus.OK).body(issuersCredentialService.getCredentials(command)); } - /** - * Issue dismantler credential response entity. - * - * @param request the request - * @param principal the principal - * @return the response entity - */ - @IssueDismantlerCredentialApiDoc - @PostMapping(path = RestURI.CREDENTIALS_ISSUER_DISMANTLER, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity issueDismantlerCredential(@Valid @RequestBody IssueDismantlerCredentialRequest request, Principal principal) { - log.debug("Received request to issue dismantler credential. BPN: {}", getBPNFromToken(principal)); - return ResponseEntity.status(HttpStatus.CREATED).body(issuersCredentialService.issueDismantlerCredential(request, getBPNFromToken(principal))); - } - - /** - * Issue framework credential response entity. - * - * @param request the request - * @param principal the principal - * @return the response entity - */ - @IssueFrameworkCredentialApiDocs - @PostMapping(path = RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity issueFrameworkCredential(@Valid @RequestBody IssueFrameworkCredentialRequest request, Principal principal) { - log.debug("Received request to issue framework credential. BPN: {}", getBPNFromToken(principal)); - return ResponseEntity.status(HttpStatus.CREATED).body(issuersCredentialService.issueFrameworkCredential(request, getBPNFromToken(principal))); - } /** * Credentials validation response entity. * - * @param data the data + * @param credentialVerificationRequest the request * @param withCredentialExpiryDate the with credential expiry date * @return the response entity */ @PostMapping(path = RestURI.CREDENTIALS_VALIDATION, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ValidateVerifiableCredentialApiDocs - public ResponseEntity> credentialsValidation(@RequestBody Map data, + public ResponseEntity> credentialsValidation(@RequestBody CredentialVerificationRequest credentialVerificationRequest, @Parameter(description = "Check expiry of VC") @RequestParam(name = "withCredentialExpiryDate", defaultValue = "false", required = false) boolean withCredentialExpiryDate) { log.debug("Received request to validate verifiable credentials"); - return ResponseEntity.status(HttpStatus.OK).body(issuersCredentialService.credentialsValidation(data, withCredentialExpiryDate)); + return ResponseEntity.status(HttpStatus.OK).body(issuersCredentialService.credentialsValidation(credentialVerificationRequest, withCredentialExpiryDate)); } /** @@ -171,8 +144,9 @@ public ResponseEntity> credentialsValidation(@RequestBody Ma */ @PostMapping(path = RestURI.ISSUERS_CREDENTIALS, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @IssueVerifiableCredentialUsingBaseWalletApiDocs - public ResponseEntity issueCredentialUsingBaseWallet(@Parameter(description = "Holder DID", examples = {@ExampleObject(description = "did", name = "did", value = "did:web:localhost:BPNL000000000000")}) @RequestParam(name = "holderDid") String holderDid, @RequestBody Map data, Principal principal) { + public ResponseEntity issueCredentialUsingBaseWallet(@Parameter(description = "Holder DID", examples = {@ExampleObject(description = "did", name = "did", value = "did:web:localhost:BPNL000000000000")}) @RequestParam(name = "holderDid") String holderDid, @RequestBody Map data, Principal principal, + @AsJwtParam @RequestParam(name = StringPool.AS_JWT, defaultValue = "false") boolean asJwt) { log.debug("Received request to issue verifiable credential. BPN: {}", getBPNFromToken(principal)); - return ResponseEntity.status(HttpStatus.CREATED).body(issuersCredentialService.issueCredentialUsingBaseWallet(holderDid, data, getBPNFromToken(principal))); + return ResponseEntity.status(HttpStatus.CREATED).body(issuersCredentialService.issueCredentialUsingBaseWallet(holderDid, data, asJwt, getBPNFromToken(principal))); } } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java similarity index 78% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java index fe052f5cf..1a2f7d878 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -21,10 +21,11 @@ package org.eclipse.tractusx.managedidentitywallets.controller; +import com.nimbusds.jwt.SignedJWT; import io.swagger.v3.oas.annotations.Parameter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; - +import org.eclipse.tractusx.managedidentitywallets.apidocs.PresentationControllerApiDocs.GetVerifiablePresentationIATPApiDocs; import org.eclipse.tractusx.managedidentitywallets.apidocs.PresentationControllerApiDocs.PostVerifiablePresentationApiDocs; import org.eclipse.tractusx.managedidentitywallets.apidocs.PresentationControllerApiDocs.PostVerifiablePresentationValidationApiDocs; import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; @@ -32,14 +33,18 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.security.Principal; import java.util.Map; +import static org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils.getAccessToken; + /** * The type Presentation controller. */ @@ -48,8 +53,6 @@ @Slf4j public class PresentationController extends BaseController { - public static final String API_TAG_VERIFIABLE_PRESENTATIONS_GENERATION = "Verifiable Presentations - Generation"; - public static final String API_TAG_VERIFIABLE_PRESENTATIONS_VALIDATION = "Verifiable Presentations - Validation"; private final PresentationService presentationService; /** @@ -90,4 +93,21 @@ public ResponseEntity> validatePresentation(@RequestBody Map log.debug("Received request to validate presentation"); return ResponseEntity.status(HttpStatus.OK).body(presentationService.validatePresentation(data, asJwt, withCredentialExpiryDate, audience)); } + + /** + * Create presentation response entity for VC types provided in STS token. + * + * @param stsToken the STS token with required scopes + * @param asJwt as JWT VP response + * @return the VP response entity + */ + + @GetMapping(path = RestURI.API_PRESENTATIONS_IATP, produces = { MediaType.APPLICATION_JSON_VALUE }) + @GetVerifiablePresentationIATPApiDocs + public ResponseEntity> createPresentation(@Parameter(hidden = true) @RequestHeader(name = "Authorization") String stsToken, + @RequestParam(name = "asJwt", required = false, defaultValue = "false") boolean asJwt) { + SignedJWT accessToken = getAccessToken(stsToken); + Map vp = presentationService.createVpWithRequiredScopes(accessToken, asJwt); + return ResponseEntity.ok(vp); + } } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/SecureTokenController.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/SecureTokenController.java new file mode 100644 index 000000000..3cfd78657 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/SecureTokenController.java @@ -0,0 +1,161 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.controller; + +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTParser; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.tractusx.managedidentitywallets.apidocs.SecureTokenControllerApiDoc; +import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.BusinessPartnerNumber; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.IdpTokenResponse; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.domain.StsTokenErrorResponse; +import org.eclipse.tractusx.managedidentitywallets.domain.StsTokenResponse; +import org.eclipse.tractusx.managedidentitywallets.dto.SecureTokenRequest; +import org.eclipse.tractusx.managedidentitywallets.exception.InvalidIdpTokenResponseException; +import org.eclipse.tractusx.managedidentitywallets.exception.InvalidSecureTokenRequestException; +import org.eclipse.tractusx.managedidentitywallets.exception.UnknownBusinessPartnerNumberException; +import org.eclipse.tractusx.managedidentitywallets.exception.UnsupportedGrantTypeException; +import org.eclipse.tractusx.managedidentitywallets.service.IdpAuthorization; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; +import org.eclipse.tractusx.managedidentitywallets.validator.SecureTokenRequestValidator; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.text.ParseException; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import static org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils.getSecureTokenRequest; + + +@RestController +@Slf4j +@RequiredArgsConstructor +@Tag(name = "STS") +public class SecureTokenController { + + + private final IdpAuthorization idpAuthorization; + + private final WalletRepository walletRepo; + + private final Map availableSigningServices; + + @InitBinder + void initBinder(WebDataBinder webDataBinder) { + webDataBinder.addValidators(new SecureTokenRequestValidator()); + } + + + @SneakyThrows + @PostMapping(path = "/api/token", consumes = { MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE }) + @SecureTokenControllerApiDoc.PostSecureTokenDocJson + public ResponseEntity tokenJson( + @Valid @RequestBody SecureTokenRequest secureTokenRequest + ) { + return processTokenRequest(secureTokenRequest); + } + + @SneakyThrows + @PostMapping(path = "/api/token", consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE }) + @SecureTokenControllerApiDoc.PostSecureTokenDocFormUrlencoded + public ResponseEntity tokenFormUrlencoded( + @Valid @RequestBody MultiValueMap requestParameters + ) { + final SecureTokenRequest secureTokenRequest = getSecureTokenRequest(requestParameters); + return processTokenRequest(secureTokenRequest); + } + + private ResponseEntity processTokenRequest(SecureTokenRequest secureTokenRequest) throws ParseException { + // handle idp authorization + IdpTokenResponse idpResponse = idpAuthorization.fromSecureTokenRequest(secureTokenRequest); + BusinessPartnerNumber bpn = idpResponse.bpn(); + Wallet selfWallet = walletRepo.getByBpn(bpn.toString()); + DID selfDid = new DID(selfWallet.getDid()); + DID partnerDid; + if (Pattern.compile(StringPool.BPN_NUMBER_REGEX).matcher(secureTokenRequest.getAudience()).matches()) { + partnerDid = new DID(walletRepo.getByBpn(secureTokenRequest.getAudience()).getDid()); + } else if (StringUtils.startsWith(secureTokenRequest.getAudience(), "did:")) { + partnerDid = new DID(secureTokenRequest.getAudience()); + } else { + throw new InvalidSecureTokenRequestException("You must provide an audience either as a BPN or DID."); + } + + SigningServiceType signingServiceType = selfWallet.getSigningServiceType(); + SigningService signingService = availableSigningServices.get(signingServiceType); + + // create the SI token and put/create the access_token inside + JWT responseJwt; + if (secureTokenRequest.assertValidWithAccessToken()) { + log.debug("Signing si token."); + responseJwt = signingService.issueToken( + selfDid, + partnerDid, + JWTParser.parse(secureTokenRequest.getAccessToken()) + ); + } else if (secureTokenRequest.assertValidWithScopes()) { + log.debug("Creating access token and signing si token."); + responseJwt = signingService.issueToken( + selfDid, + partnerDid, + Set.of(secureTokenRequest.getBearerAccessScope()) + ); + } else { + throw new InvalidSecureTokenRequestException("The provided data could not be used to create and sign a token."); + } + + // create the response + log.debug("Preparing StsTokenResponse."); + StsTokenResponse response = StsTokenResponse.builder() + .token(responseJwt.serialize()) + .expiresAt(responseJwt.getJWTClaimsSet().getExpirationTime().getTime()) + .build(); + return ResponseEntity.status(HttpStatus.OK).body(response); + } + + @ExceptionHandler({ UnsupportedGrantTypeException.class, InvalidSecureTokenRequestException.class, UnknownBusinessPartnerNumberException.class, InvalidIdpTokenResponseException.class }) + public ResponseEntity getErrorResponse(RuntimeException e) { + StsTokenErrorResponse response = new StsTokenErrorResponse(); + response.setError(e.getClass().getSimpleName()); + response.setErrorDescription(e.getMessage()); + return ResponseEntity.badRequest().body(response); + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/WalletController.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/WalletController.java similarity index 98% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/WalletController.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/WalletController.java index 5cfefb72e..fb41bdf03 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/WalletController.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/WalletController.java @@ -68,7 +68,7 @@ public class WalletController extends BaseController { @CreateWalletApiDoc @PostMapping(path = RestURI.WALLETS, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity createWallet(@Valid @RequestBody CreateWalletRequest request, Principal principal) { - log.debug("Received request to create wallet with BPN {}. authorized by BPN: {}", request.getBpn(), getBPNFromToken(principal)); + log.debug("Received request to create wallet with BPN {}. authorized by BPN: {}", request.getBusinessPartnerNumber(), getBPNFromToken(principal)); return ResponseEntity.status(HttpStatus.CREATED).body(service.createWallet(request, getBPNFromToken(principal))); } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/BaseCredential.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/BaseCredential.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/BaseCredential.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/BaseCredential.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/HoldersCredential.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/HoldersCredential.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/HoldersCredential.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/HoldersCredential.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/IssuersCredential.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/IssuersCredential.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/IssuersCredential.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/IssuersCredential.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/WalletKey.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/JtiRecord.java similarity index 67% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/WalletKey.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/JtiRecord.java index 565ec9692..d18689e2a 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/WalletKey.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/JtiRecord.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -22,19 +22,28 @@ package org.eclipse.tractusx.managedidentitywallets.dao.entity; import com.fasterxml.jackson.annotation.JsonIgnore; -import jakarta.persistence.*; -import lombok.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.UUID; -/** - * The type Wallet key. - */ +@Entity +@Table(name = "jti") @Getter @Setter -@Entity -@AllArgsConstructor @NoArgsConstructor +@AllArgsConstructor @Builder -public class WalletKey extends MIWBaseEntity { +public class JtiRecord extends MIWBaseEntity { @Id @JsonIgnore @@ -43,19 +52,8 @@ public class WalletKey extends MIWBaseEntity { private Long id; @Column(nullable = false) - private Long walletId; - - @Column(nullable = false) - private String vaultAccessToken; - - @Column(nullable = false) - private String referenceKey; - - @Column(nullable = false) - private String privateKey; - - @Column(nullable = false) - private String publicKey; + private UUID jti; - private String keyId; + @Column(name = "is_used_status", nullable = false) + private boolean isUsedStatus; } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/MIWBaseEntity.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/MIWBaseEntity.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/MIWBaseEntity.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/MIWBaseEntity.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/Wallet.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/Wallet.java similarity index 67% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/Wallet.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/Wallet.java index 4cb65dba9..b3db2d121 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/Wallet.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/Wallet.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -23,8 +23,23 @@ import com.fasterxml.jackson.annotation.JsonIgnore; -import jakarta.persistence.*; -import lombok.*; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Transient; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; import org.eclipse.tractusx.managedidentitywallets.utils.StringToDidDocumentConverter; import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; @@ -60,10 +75,18 @@ public class Wallet extends MIWBaseEntity { @Column(nullable = false) private String algorithm; + @Enumerated(EnumType.STRING) + @Column(name = "signing_service_type", nullable = false) + private SigningServiceType signingServiceType; + @Column(nullable = false) @Convert(converter = StringToDidDocumentConverter.class) private DidDocument didDocument; + @OneToMany(mappedBy = "wallet", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonIgnore + private List walletKeys; + @Transient private List verifiableCredentials; } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/WalletKey.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/WalletKey.java new file mode 100644 index 000000000..d53a38698 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/WalletKey.java @@ -0,0 +1,84 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.dao.entity; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyPair; + +/** + * The type Wallet key. + */ +@Getter +@Setter +@Entity +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Table(name="wallet_key") +public class WalletKey extends MIWBaseEntity { + + @Id + @JsonIgnore + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", columnDefinition = "serial", nullable = false, unique = true) + private long id; + + @Column(nullable = false) + private String vaultAccessToken; + + @Column(nullable = false) + private String referenceKey; + + @Column(nullable = false) + private String privateKey; + + @Column(nullable = false) + private String publicKey; + + @ManyToOne + @JoinColumn(name = "wallet_id", columnDefinition = "bigint") + @JsonBackReference + private Wallet wallet; + + private String keyId; + + @Column(nullable = false) + private String algorithm; + + public KeyPair toDto() { + return new KeyPair(keyId, privateKey, publicKey); + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/HoldersCredentialRepository.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/HoldersCredentialRepository.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/HoldersCredentialRepository.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/HoldersCredentialRepository.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/IssuersCredentialRepository.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/IssuersCredentialRepository.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/IssuersCredentialRepository.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/IssuersCredentialRepository.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletKeyRepository.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/JtiRepository.java similarity index 74% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletKeyRepository.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/JtiRepository.java index 26874d35e..69c83a4f9 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletKeyRepository.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/JtiRepository.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -22,19 +22,19 @@ package org.eclipse.tractusx.managedidentitywallets.dao.repository; import com.smartsensesolutions.java.commons.base.repository.BaseRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.JtiRecord; import org.springframework.stereotype.Repository; -/** - * The interface Wallet key repository. - */ +import java.util.UUID; + @Repository -public interface WalletKeyRepository extends BaseRepository { +public interface JtiRepository extends BaseRepository { + /** - * Gets by wallet id. + * Gets by jti. * - * @param id the id - * @return the by wallet id + * @param jtiNumber the jti + * @return the by jti */ - WalletKey getByWalletId(Long id); + JtiRecord getByJti(UUID jtiNumber); } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletKeyRepository.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletKeyRepository.java new file mode 100644 index 000000000..54196d73b --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletKeyRepository.java @@ -0,0 +1,75 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.dao.repository; + +import com.smartsensesolutions.java.commons.base.repository.BaseRepository; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.springframework.stereotype.Repository; + +/** + * The interface Wallet key repository. + */ +@Repository +public interface WalletKeyRepository extends BaseRepository { + /** + * Gets by wallet id and algorithm. + * + * @param id the wallet id + * @param algorithm the algorithm + * @return the by wallet id + */ + WalletKey getByWalletIdAndAlgorithm(Long id, String algorithm); + + /** + * Find first by wallet bpn wallet key. + * + * @param bpn the bpn + * @return the wallet key + */ + WalletKey findFirstByWallet_Bpn(String bpn); + + /** + * Find first by wallet did wallet key. + * + * @param did the did + * @return the wallet key + */ + WalletKey findFirstByWallet_Did(String did); + + /** + * Gets by key id and algorithm. + * + * @param keyId the key id + * @param algorithm the algorithm + * @return the by key id and algorithm + */ + WalletKey getByKeyIdAndAlgorithm(String keyId, String algorithm); + + /** + * Gets by algorithm and wallet bpn. + * + * @param name the name + * @param keyName the key name + * @return the by algorithm and wallet bpn + */ + WalletKey getByAlgorithmAndWallet_Bpn(String name, String keyName); +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletRepository.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletRepository.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletRepository.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletRepository.java diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/BusinessPartnerNumber.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/BusinessPartnerNumber.java new file mode 100644 index 000000000..94be8dc46 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/BusinessPartnerNumber.java @@ -0,0 +1,30 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +public record BusinessPartnerNumber(String bpn) { + + public String toString() { + return bpn; + } + +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfig.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfig.java new file mode 100644 index 000000000..f75ae464b --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfig.java @@ -0,0 +1,96 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialStatus; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; + +import java.net.URI; +import java.util.Date; +import java.util.List; + +@Builder +@Getter +public class CredentialCreationConfig { + + @NonNull + private VerifiableCredentialSubject subject; + + private VerifiableCredentialStatus verifiableCredentialStatus; + + @NonNull + private DidDocument issuerDoc; + + @NonNull + private String holderDid; + + @NonNull + private List types; + + @NonNull + private List contexts; + + @NonNull + private SupportedAlgorithms algorithm; + + private URI vcId; + + private Date expiryDate; + + private boolean selfIssued; + + // this will be used by the DB-Impl of storage to retrieve privateKey + @NonNull + private String keyName; + + @NonNull + @Setter + private VerifiableEncoding encoding; + + @Setter + //This is used when we issue VC as JWT + private VerifiableCredential verifiableCredential; + + public static class CredentialCreationConfigBuilder { + public CredentialCreationConfigBuilder vcId(Object object) { + if (!(object instanceof URI) && !(object instanceof String)) { + throw new IllegalArgumentException("vcId must be of type String or URI, argument has type%s".formatted(object.getClass().getName())); + } + + if (object instanceof URI uri) { + this.vcId = uri; + } else { + this.vcId = URI.create((String) object); + } + + return this; + + } + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/DID.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/DID.java new file mode 100644 index 000000000..f43ab3683 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/DID.java @@ -0,0 +1,30 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +public record DID(String did) { + + public String toString() { + return did; + } + +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/IdpTokenResponse.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/IdpTokenResponse.java new file mode 100644 index 000000000..4735a8b54 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/IdpTokenResponse.java @@ -0,0 +1,64 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTParser; +import lombok.SneakyThrows; + +import java.text.ParseException; + + +@JsonIgnoreProperties(ignoreUnknown = true) +public class IdpTokenResponse { + private JWT idToken; + + @JsonProperty("id_token") + public JWT idToken() { + return idToken; + } + + public void setIdToken(String idToken) throws ParseException { + // todo bri: add validation for the id token + this.idToken = JWTParser.parse(idToken); + } + + @SneakyThrows + public String bpnClaim() { + return this.idToken.getJWTClaimsSet().getClaim("BPN").toString(); + } + + @SneakyThrows + public BusinessPartnerNumber bpn() { + return new BusinessPartnerNumber(this.bpnClaim()); + } + + @Override + @SneakyThrows + public String toString() { + return "IdpResponse{" + + "id_token='" + idToken.getJWTClaimsSet().toString() + '\'' + + '}'; + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyCreationConfig.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyCreationConfig.java new file mode 100644 index 000000000..59d9b1ba7 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyCreationConfig.java @@ -0,0 +1,43 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.KeyType; +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; + +import java.util.List; + +@Builder +@Getter +public class KeyCreationConfig { + + @NonNull + private String keyName; + + private Curve curve; + + @NonNull + private List keyTypes; +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyPair.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyPair.java new file mode 100644 index 000000000..31b29da1c --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyPair.java @@ -0,0 +1,25 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +public record KeyPair(String keyId, String privateKey, String publicKey) { +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyStorageType.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyStorageType.java new file mode 100644 index 000000000..1fd489de4 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyStorageType.java @@ -0,0 +1,27 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +public enum KeyStorageType { + DB, + REMOTE +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/PresentationCreationConfig.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/PresentationCreationConfig.java new file mode 100644 index 000000000..0ff581e0a --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/PresentationCreationConfig.java @@ -0,0 +1,59 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.ssi.lib.model.did.Did; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; + +import java.net.URI; +import java.util.List; + +@Builder +@Getter +public class PresentationCreationConfig { + + @NonNull + private VerifiableEncoding encoding; + + @NonNull + private String keyName; + + @NonNull + private List verifiableCredentials; + + @NonNull + private Did vpIssuerDid; + + // all for JWT + private String audience; + + // all for JsonLD + URI verificationMethod; + + @NonNull + private SupportedAlgorithms algorithm; + +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/SigningServiceType.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/SigningServiceType.java new file mode 100644 index 000000000..1b31137a5 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/SigningServiceType.java @@ -0,0 +1,28 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +public enum SigningServiceType { + LOCAL, + AZURE, + REMOTE +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/StsTokenErrorResponse.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/StsTokenErrorResponse.java new file mode 100644 index 000000000..0ec307d63 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/StsTokenErrorResponse.java @@ -0,0 +1,34 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@Getter +@Setter +public class StsTokenErrorResponse { + private String error; + private String errorDescription; +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/StsTokenResponse.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/StsTokenResponse.java new file mode 100644 index 000000000..50161b118 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/StsTokenResponse.java @@ -0,0 +1,42 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class StsTokenResponse { + + @JsonProperty("access_token") + private String token; + + private long expiresAt; +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/VerifiableEncoding.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/VerifiableEncoding.java new file mode 100644 index 000000000..64a1e23cf --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/VerifiableEncoding.java @@ -0,0 +1,27 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +public enum VerifiableEncoding { + JWT, + JSON_LD +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CreateWalletRequest.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CreateWalletRequest.java similarity index 73% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CreateWalletRequest.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CreateWalletRequest.java index 6c6f4d62e..4d5b253d6 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CreateWalletRequest.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CreateWalletRequest.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -24,8 +24,13 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; /** @@ -40,9 +45,15 @@ public class CreateWalletRequest { @NotBlank(message = "Please provide BPN") @Pattern(regexp = StringPool.BPN_NUMBER_REGEX, message = "Please provide valid BPN") - private String bpn; + private String businessPartnerNumber; @NotBlank(message = "Please provide name") @Size(min = 1, max = 255, message = "Please provide valid name") - private String name; + private String companyName; + + @NotBlank(message = "Please provide url") + @Size(min = 1, max = 2000, message = "Please provide url") + private String didUrl; + + private SigningServiceType signingServiceType = SigningServiceType.LOCAL; } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/IssueMembershipCredentialRequest.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CredentialVerificationRequest.java similarity index 66% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/IssueMembershipCredentialRequest.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CredentialVerificationRequest.java index 301a23229..c4f9dd4a2 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/IssueMembershipCredentialRequest.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CredentialVerificationRequest.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -21,23 +21,19 @@ package org.eclipse.tractusx.managedidentitywallets.dto; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import lombok.*; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; -/** - * The type Issue membership credential request. - */ -@Getter -@Setter -@NoArgsConstructor -@Builder -@AllArgsConstructor -public class IssueMembershipCredentialRequest { +import java.util.LinkedHashMap; +import java.util.Map; + +public class CredentialVerificationRequest extends LinkedHashMap { - @NotBlank(message = "Please provide BPN") - @Pattern(regexp = StringPool.BPN_NUMBER_REGEX, message = "Please provide valid BPN") - private String bpn; -} + public void setJwt(String jwt) { + put(StringPool.VC_JWT_KEY, jwt); + } + + public void setVc(Map vc) { + putAll(vc); + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/IssueDismantlerCredentialRequest.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CredentialsResponse.java similarity index 55% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/IssueDismantlerCredentialRequest.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CredentialsResponse.java index a838d4b6f..d90d7e445 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/IssueDismantlerCredentialRequest.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CredentialsResponse.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -21,32 +21,19 @@ package org.eclipse.tractusx.managedidentitywallets.dto; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; -import lombok.*; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; -import java.util.Set; +import java.util.LinkedHashMap; +import java.util.Map; -/** - * The type Issue dismantler credential request. - */ -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class IssueDismantlerCredentialRequest { +public class CredentialsResponse extends LinkedHashMap { - @NotBlank(message = "Please provide BPN") - @Pattern(regexp = StringPool.BPN_NUMBER_REGEX, message = "Please provide valid BPN") - private String bpn; + public void setJwt(String jwt) { + put(StringPool.VC_JWT_KEY, jwt); + } - @NotBlank(message = "Please provide activity type") - @Size(min = 1, message = "Please provide valid activity type") - private String activityType; + public void setVc(Map vc) { + putAll(vc); + } - @Builder.Default - private Set<@NotBlank String> allowedVehicleBrands = Set.of(); } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/SecureTokenRequest.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/SecureTokenRequest.java new file mode 100644 index 000000000..5eb7c931d --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/SecureTokenRequest.java @@ -0,0 +1,75 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class SecureTokenRequest { + private String audience; + + @JsonProperty("client_id") + private String clientId; + + @JsonProperty("client_secret") + private String clientSecret; + + @JsonProperty("grant_type") + private String grantType; + + @JsonProperty("access_token") + private String accessToken; + + @JsonProperty("bearer_access_alias") + private String bearerAccessAlias; + + @JsonProperty("bearer_access_scope") + private String bearerAccessScope; + + public boolean assertValidWithScopes() { + return bearerAccessScope != null && accessToken == null && !bearerAccessScope.isEmpty(); + } + + public boolean assertValidWithAccessToken() { + return accessToken != null && bearerAccessScope == null; + } + + @Override + public String toString() { + return "SecureTokenRequest{" + + "audience='" + audience + '\'' + + ", clientId='" + clientId + '\'' + + ", grantType='" + grantType + '\'' + + ", bearerAccessAlias='" + bearerAccessAlias + '\'' + + ", bearerAccessScope='" + bearerAccessScope + '\'' + + '}'; + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/ValidationResult.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/ValidationResult.java new file mode 100644 index 000000000..8929fada7 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/ValidationResult.java @@ -0,0 +1,44 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.eclipse.tractusx.managedidentitywallets.constant.TokenValidationErrors; + +import java.util.List; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class ValidationResult { + + private boolean isValid; + + private List errors; + +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/BadDataException.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/BadDataException.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/BadDataException.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/BadDataException.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/CredentialNotFoundProblem.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/CredentialNotFoundProblem.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/CredentialNotFoundProblem.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/CredentialNotFoundProblem.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/DuplicateCredentialProblem.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/DuplicateCredentialProblem.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/DuplicateCredentialProblem.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/DuplicateCredentialProblem.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/DuplicateWalletProblem.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/DuplicateWalletProblem.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/DuplicateWalletProblem.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/DuplicateWalletProblem.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/ForbiddenException.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/ForbiddenException.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/ForbiddenException.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/ForbiddenException.java diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/InvalidIdpTokenResponseException.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/InvalidIdpTokenResponseException.java new file mode 100644 index 000000000..07ae990f2 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/InvalidIdpTokenResponseException.java @@ -0,0 +1,32 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.exception; + +import java.io.Serial; + +public class InvalidIdpTokenResponseException extends RuntimeException { + @Serial + private static final long serialVersionUID = 1L; + public InvalidIdpTokenResponseException(String message) { + super(message); + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/InvalidSecureTokenRequestException.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/InvalidSecureTokenRequestException.java new file mode 100644 index 000000000..930e59590 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/InvalidSecureTokenRequestException.java @@ -0,0 +1,33 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.exception; + +import java.io.Serial; + +public class InvalidSecureTokenRequestException extends RuntimeException { + @Serial + private static final long serialVersionUID = 1L; + + public InvalidSecureTokenRequestException(String message) { + super(message); + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/MissingVcTypesException.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/MissingVcTypesException.java new file mode 100644 index 000000000..c6f150d4a --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/MissingVcTypesException.java @@ -0,0 +1,33 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.exception; + +import java.io.Serial; + +public class MissingVcTypesException extends RuntimeException { + @Serial + private static final long serialVersionUID = 1L; + + public MissingVcTypesException(String message) { + super(message); + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/PermissionViolationException.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/PermissionViolationException.java new file mode 100644 index 000000000..7208d8fb8 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/PermissionViolationException.java @@ -0,0 +1,33 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.exception; + +import java.io.Serial; + +public class PermissionViolationException extends RuntimeException { + @Serial + private static final long serialVersionUID = 1L; + + public PermissionViolationException(String message) { + super(message); + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/SignatureFailureException.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/SignatureFailureException.java new file mode 100644 index 000000000..44399bf06 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/SignatureFailureException.java @@ -0,0 +1,33 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.exception; + +import java.io.Serial; + +public class SignatureFailureException extends RuntimeException { + @Serial + private static final long serialVersionUID = 1L; + + public SignatureFailureException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/DuplicateSummaryCredentialProblem.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/UnknownBusinessPartnerNumberException.java similarity index 52% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/DuplicateSummaryCredentialProblem.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/UnknownBusinessPartnerNumberException.java index 917b7a1ab..3408d598b 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/DuplicateSummaryCredentialProblem.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/UnknownBusinessPartnerNumberException.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -21,43 +21,18 @@ package org.eclipse.tractusx.managedidentitywallets.exception; -/** - * The type Duplicate wallet problem. - */ -public class DuplicateSummaryCredentialProblem extends RuntimeException { +import java.io.Serial; - /** - * Instantiates a new Duplicate wallet problem. - */ - public DuplicateSummaryCredentialProblem() { - } +public class UnknownBusinessPartnerNumberException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; - /** - * Instantiates a new Duplicate wallet problem. - * - * @param message the message - */ - public DuplicateSummaryCredentialProblem(String message) { + public UnknownBusinessPartnerNumberException(String message) { super(message); } - /** - * Instantiates a new Duplicate wallet problem. - * - * @param message the message - * @param cause the cause - */ - public DuplicateSummaryCredentialProblem(String message, Throwable cause) { + public UnknownBusinessPartnerNumberException(String message, Throwable cause) { super(message, cause); } - - /** - * Instantiates a new Duplicate wallet problem. - * - * @param cause the cause - */ - public DuplicateSummaryCredentialProblem(Throwable cause) { - super(cause); - } - } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/UnsupportedAlgorithmException.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/UnsupportedAlgorithmException.java new file mode 100644 index 000000000..d5a54ee8d --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/UnsupportedAlgorithmException.java @@ -0,0 +1,33 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.exception; + +import java.io.Serial; + +public class UnsupportedAlgorithmException extends RuntimeException { + @Serial + private static final long serialVersionUID = 1L; + + public UnsupportedAlgorithmException(String message) { + super(message); + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/UnsupportedGrantTypeException.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/UnsupportedGrantTypeException.java new file mode 100644 index 000000000..6f857b678 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/UnsupportedGrantTypeException.java @@ -0,0 +1,33 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.exception; + +import java.io.Serial; + +public class UnsupportedGrantTypeException extends RuntimeException { + @Serial + private static final long serialVersionUID = 1L; + + public UnsupportedGrantTypeException(String message) { + super(message); + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/WalletCreationProblem.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/WalletCreationProblem.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/WalletCreationProblem.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/WalletCreationProblem.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/WalletNotFoundProblem.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/WalletNotFoundProblem.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/WalletNotFoundProblem.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/exception/WalletNotFoundProblem.java diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/interfaces/SecureTokenIssuer.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/interfaces/SecureTokenIssuer.java new file mode 100644 index 000000000..8a54f5f37 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/interfaces/SecureTokenIssuer.java @@ -0,0 +1,35 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.interfaces; + +import com.nimbusds.jwt.JWT; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyPair; + +import java.time.Instant; +import java.util.Set; + +public interface SecureTokenIssuer { + JWT createAccessToken(KeyPair keyPair, DID self, DID partner, Instant expirationTime, Set scopes); + + JWT createIdToken(KeyPair keyPair, DID self, DID partner, Instant expirationTime, JWT accessToken); +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/interfaces/SecureTokenService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/interfaces/SecureTokenService.java new file mode 100644 index 000000000..af35ed92c --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/interfaces/SecureTokenService.java @@ -0,0 +1,40 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.interfaces; + +import org.eclipse.tractusx.managedidentitywallets.domain.BusinessPartnerNumber; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; + +import com.nimbusds.jwt.JWT; +import org.eclipse.tractusx.managedidentitywallets.signing.KeyProvider; + +import java.util.Set; + +public interface SecureTokenService { + JWT issueToken(DID self, DID partner, Set scopes, KeyProvider keyProvider); + + JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, Set scopes, KeyProvider keyProvider); + + JWT issueToken(DID self, DID partner, JWT accessToken, KeyProvider keyProvider); + + JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, JWT accessToken, KeyProvider keyProvider); +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CommonService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CommonService.java similarity index 85% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CommonService.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CommonService.java index 60d67c13f..1acf32154 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CommonService.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CommonService.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -23,14 +23,12 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.text.StringEscapeUtils; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; import org.eclipse.tractusx.managedidentitywallets.exception.WalletNotFoundProblem; import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils; import org.eclipse.tractusx.managedidentitywallets.utils.Validate; -import org.eclipse.tractusx.ssi.lib.exception.DidParseException; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; import org.springframework.stereotype.Service; @@ -55,12 +53,7 @@ public Wallet getWalletByIdentifier(String identifier) { if (CommonUtils.getIdentifierType(identifier).equals(StringPool.BPN)) { wallet = walletRepository.getByBpn(identifier); } else { - try { - wallet = walletRepository.getByDid(identifier); - } catch (DidParseException e) { - log.error("Error while parsing did {}", StringEscapeUtils.escapeJava(identifier), e); - throw new WalletNotFoundProblem("Error while parsing did " + identifier); - } + wallet = walletRepository.getByDid(identifier); } Validate.isNull(wallet).launch(new WalletNotFoundProblem("Wallet not found for identifier " + identifier)); return wallet; diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/DidDocumentResolverService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/DidDocumentResolverService.java similarity index 76% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/service/DidDocumentResolverService.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/DidDocumentResolverService.java index d84c5a6a3..3ffcccf86 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/DidDocumentResolverService.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/DidDocumentResolverService.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -24,9 +24,6 @@ import lombok.Getter; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; import org.eclipse.tractusx.ssi.lib.did.resolver.CompositeDidResolver; -import org.eclipse.tractusx.ssi.lib.did.resolver.DidDocumentResolverRegistry; -import org.eclipse.tractusx.ssi.lib.did.resolver.DidDocumentResolverRegistryImpl; -import org.eclipse.tractusx.ssi.lib.did.web.DidWebDocumentResolver; import org.eclipse.tractusx.ssi.lib.did.web.DidWebResolver; import org.eclipse.tractusx.ssi.lib.did.web.util.DidWebParser; import org.springframework.stereotype.Service; @@ -39,7 +36,7 @@ public class DidDocumentResolverService { final static HttpClient httpClient = HttpClient.newHttpClient(); @Getter - private final DidDocumentResolverRegistry didDocumentResolverRegistry; + private final DidWebResolver didDocumentResolverRegistry; @Getter private final CompositeDidResolver compositeDidResolver; @@ -49,9 +46,8 @@ public DidDocumentResolverService(MIWSettings miwSettings) { final boolean enforceHttps = miwSettings.enforceHttps(); final DidWebParser didParser = new DidWebParser(); - didDocumentResolverRegistry = new DidDocumentResolverRegistryImpl(); - didDocumentResolverRegistry.register( - new DidWebDocumentResolver(httpClient, didParser, enforceHttps)); + didDocumentResolverRegistry = + new DidWebResolver(httpClient, didParser, enforceHttps); compositeDidResolver = new CompositeDidResolver( new DidWebResolver(httpClient, didParser, enforceHttps) diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/DidDocumentService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/DidDocumentService.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/service/DidDocumentService.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/DidDocumentService.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/HoldersCredentialService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/HoldersCredentialService.java similarity index 57% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/service/HoldersCredentialService.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/HoldersCredentialService.java index 670055051..a69ad4659 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/HoldersCredentialService.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/HoldersCredentialService.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -32,12 +32,20 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.text.StringEscapeUtils; +import org.eclipse.tractusx.managedidentitywallets.command.GetCredentialsCommand; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.CredentialCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.domain.VerifiableEncoding; +import org.eclipse.tractusx.managedidentitywallets.dto.CredentialsResponse; import org.eclipse.tractusx.managedidentitywallets.exception.CredentialNotFoundProblem; import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; +import org.eclipse.tractusx.managedidentitywallets.signing.SignerResult; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils; import org.eclipse.tractusx.managedidentitywallets.utils.Validate; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; @@ -71,7 +79,8 @@ public class HoldersCredentialService extends BaseService credentialSpecificationUtil; - private final WalletKeyService walletKeyService; + private final Map availableSigningServices; + @Override protected BaseRepository getRepository() { @@ -85,52 +94,71 @@ protected SpecificationUtil getSpecificationUtil() { /** - * Gets list of holder's credentials + * Gets credentials. * - * @param credentialId the credentialId - * @param issuerIdentifier the issuer identifier - * @param type the type - * @param sortColumn the sort column - * @param sortType the sort type - * @param callerBPN the caller bpn + * @param command the command * @return the credentials */ - public PageImpl getCredentials(String credentialId, String issuerIdentifier, List type, String sortColumn, String sortType, int pageNumber, int size, String callerBPN) { + public PageImpl getCredentials(GetCredentialsCommand command) { FilterRequest filterRequest = new FilterRequest(); - filterRequest.setPage(pageNumber); - filterRequest.setSize(size); + filterRequest.setPage(command.getPageNumber()); + filterRequest.setSize(command.getSize()); //Holder must be caller of API - Wallet holderWallet = commonService.getWalletByIdentifier(callerBPN); + Wallet holderWallet = commonService.getWalletByIdentifier(command.getCallerBPN()); filterRequest.appendCriteria(StringPool.HOLDER_DID, Operator.EQUALS, holderWallet.getDid()); - if (StringUtils.hasText(issuerIdentifier)) { - Wallet issuerWallet = commonService.getWalletByIdentifier(issuerIdentifier); + if (StringUtils.hasText(command.getIdentifier())) { + Wallet issuerWallet = commonService.getWalletByIdentifier(command.getIdentifier()); filterRequest.appendCriteria(StringPool.ISSUER_DID, Operator.EQUALS, issuerWallet.getDid()); } - if (StringUtils.hasText(credentialId)) { - filterRequest.appendCriteria(StringPool.CREDENTIAL_ID, Operator.EQUALS, credentialId); + if (StringUtils.hasText(command.getCredentialId())) { + filterRequest.appendCriteria(StringPool.CREDENTIAL_ID, Operator.EQUALS, command.getCredentialId()); } FilterRequest request = new FilterRequest(); - if (!CollectionUtils.isEmpty(type)) { + if (!CollectionUtils.isEmpty(command.getType())) { request.setPage(filterRequest.getPage()); request.setSize(filterRequest.getSize()); request.setCriteriaOperator(CriteriaOperator.OR); - for (String str : type) { + for (String str : command.getType()) { request.appendCriteria(StringPool.TYPE, Operator.CONTAIN, str); } } Sort sort = new Sort(); - sort.setColumn(sortColumn); - sort.setSortType(SortType.valueOf(sortType.toUpperCase())); + sort.setColumn(command.getSortColumn()); + sort.setSortType(SortType.valueOf(command.getSortType().toUpperCase())); filterRequest.setSort(sort); Page filter = filter(filterRequest, request, CriteriaOperator.AND); - List list = new ArrayList<>(filter.getContent().size()); + List list = new ArrayList<>(filter.getContent().size()); + + Wallet issuerWallet = command.getIdentifier() != null ? commonService.getWalletByIdentifier(command.getIdentifier()) : holderWallet; + for (HoldersCredential credential : filter.getContent()) { - list.add(credential.getData()); + CredentialsResponse cr = new CredentialsResponse(); + if (command.isAsJwt()) { + + CredentialCreationConfig config = CredentialCreationConfig.builder() + .algorithm(SupportedAlgorithms.ED25519) + .issuerDoc(issuerWallet.getDidDocument()) + .holderDid(holderWallet.getDid()) + .keyName(issuerWallet.getBpn()) + .verifiableCredential(credential.getData()) + .subject(credential.getData().getCredentialSubject().get(0)) + .contexts(credential.getData().getContext()) + .vcId(credential.getData().getId()) + .types(credential.getData().getTypes()) + .encoding(VerifiableEncoding.JWT) + .build(); + + SignerResult signerResult = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(config); + cr.setJwt(signerResult.getJwt()); + } else { + cr.setVc(credential.getData()); + } + list.add(cr); } return new PageImpl<>(list, filter.getPageable(), filter.getTotalElements()); @@ -141,35 +169,60 @@ public PageImpl getCredentials(String credentialId, String * * @param data the data * @param callerBpn the caller bpn + * @param asJwt the as jwt * @return the verifiable credential */ - public VerifiableCredential issueCredential(Map data, String callerBpn) { + public CredentialsResponse issueCredential(Map data, String callerBpn, boolean asJwt) { VerifiableCredential verifiableCredential = new VerifiableCredential(data); Wallet issuerWallet = commonService.getWalletByIdentifier(verifiableCredential.getIssuer().toString()); //validate BPN access, Holder must be caller of API Validate.isFalse(callerBpn.equals(issuerWallet.getBpn())).launch(new ForbiddenException(BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN)); - // get Key - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifierAsBytes(issuerWallet.getId()); - // check if the expiryDate is set Date expiryDate = null; if (verifiableCredential.getExpirationDate() != null) { expiryDate = Date.from(verifiableCredential.getExpirationDate()); } + + CredentialCreationConfig holdersCredentialCreationConfig = CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .subject(verifiableCredential.getCredentialSubject().get(0)) + .types(verifiableCredential.getTypes()) + .issuerDoc(issuerWallet.getDidDocument()) + .holderDid(issuerWallet.getDid()) + .contexts(verifiableCredential.getContext()) + .expiryDate(expiryDate) + .selfIssued(true) + .keyName(issuerWallet.getBpn()) + .algorithm(SupportedAlgorithms.valueOf(issuerWallet.getAlgorithm())) + .build(); + // Create Credential - HoldersCredential credential = CommonUtils.getHoldersCredential(verifiableCredential.getCredentialSubject().get(0), - verifiableCredential.getTypes(), issuerWallet.getDidDocument(), - privateKeyBytes, issuerWallet.getDid(), - verifiableCredential.getContext(), expiryDate, true); + + SignerResult signerResult = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + VerifiableCredential vc = (VerifiableCredential) signerResult.getJsonLd(); + HoldersCredential credential = CommonUtils.convertVerifiableCredential(vc, holdersCredentialCreationConfig); + //Store Credential in holder table credential = create(credential); - log.debug("VC type of {} issued to bpn ->{}", StringEscapeUtils.escapeJava(verifiableCredential.getTypes().toString()), StringEscapeUtils.escapeJava(callerBpn)); + final CredentialsResponse cr = new CredentialsResponse(); + // Return VC - return credential.getData(); + if (asJwt) { + holdersCredentialCreationConfig.setVerifiableCredential(credential.getData()); + holdersCredentialCreationConfig.setEncoding(VerifiableEncoding.JWT); + SignerResult signerJwtResult = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + cr.setJwt(signerJwtResult.getJwt()); + } else { + cr.setVc(credential.getData()); + } + + log.debug("VC type of {} issued to bpn ->{}", StringEscapeUtils.escapeJava(verifiableCredential.getTypes().toString()), StringEscapeUtils.escapeJava(callerBpn)); + + return cr; } private void isCredentialExistWithId(String holderDid, String credentialId) { diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IdpAuthorization.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IdpAuthorization.java new file mode 100644 index 000000000..956a938cb --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IdpAuthorization.java @@ -0,0 +1,86 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.service; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.managedidentitywallets.config.security.SecurityConfigProperties; +import org.eclipse.tractusx.managedidentitywallets.domain.IdpTokenResponse; +import org.eclipse.tractusx.managedidentitywallets.dto.SecureTokenRequest; +import org.eclipse.tractusx.managedidentitywallets.exception.UnsupportedGrantTypeException; +import org.eclipse.tractusx.managedidentitywallets.exception.InvalidIdpTokenResponseException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.CLIENT_CREDENTIALS; +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.CLIENT_ID; +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.CLIENT_SECRET; +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.GRANT_TYPE; +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.SCOPE; +import static org.springframework.security.oauth2.core.oidc.OidcScopes.OPENID; + +@Service +@Slf4j +public class IdpAuthorization { + + private final RestTemplate rest; + + @Autowired + public IdpAuthorization(final SecurityConfigProperties properties, final RestTemplateBuilder restTemplateBuilder) { + String url = UriComponentsBuilder.fromUriString(properties.authServerUrl()) + .pathSegment("realms", properties.realm()) + .build() + .toString(); + this.rest = restTemplateBuilder + .rootUri(url) + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .build(); + } + + public IdpTokenResponse fromSecureTokenRequest(SecureTokenRequest secureTokenRequest) throws UnsupportedGrantTypeException, InvalidIdpTokenResponseException { + // we're ignoring the input, but the protocol requires us to check. + if (!secureTokenRequest.getGrantType().equals(CLIENT_CREDENTIALS)) { + throw new UnsupportedGrantTypeException("The provided 'grant_type' is not valid. Use 'client_credentials'."); + } + MultiValueMap tokenRequest = new LinkedMultiValueMap<>(); + tokenRequest.add(GRANT_TYPE, CLIENT_CREDENTIALS); + tokenRequest.add(SCOPE, OPENID); + tokenRequest.add(CLIENT_ID, secureTokenRequest.getClientId()); + tokenRequest.add(CLIENT_SECRET, secureTokenRequest.getClientSecret()); + log.debug("Doing OAuth token request for '{}' during secure token request flow.", secureTokenRequest.getClientId()); + IdpTokenResponse idpResponse = rest.postForObject( + "/protocol/openid-connect/token", + tokenRequest, + IdpTokenResponse.class + ); + if (idpResponse == null) { + throw new InvalidIdpTokenResponseException("The idp response cannot be null. Possible causes for this are: the 'clientId' is invalid, or the 'client' is not enabled."); + } + return idpResponse; + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialService.java new file mode 100644 index 000000000..8d6fb6d2b --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialService.java @@ -0,0 +1,389 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jwt.SignedJWT; +import com.smartsensesolutions.java.commons.FilterRequest; +import com.smartsensesolutions.java.commons.base.repository.BaseRepository; +import com.smartsensesolutions.java.commons.base.service.BaseService; +import com.smartsensesolutions.java.commons.criteria.CriteriaOperator; +import com.smartsensesolutions.java.commons.operator.Operator; +import com.smartsensesolutions.java.commons.sort.Sort; +import com.smartsensesolutions.java.commons.sort.SortType; +import com.smartsensesolutions.java.commons.specification.SpecificationUtil; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.text.StringEscapeUtils; +import org.eclipse.tractusx.managedidentitywallets.command.GetCredentialsCommand; +import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; +import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.IssuersCredential; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.CredentialCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.domain.VerifiableEncoding; +import org.eclipse.tractusx.managedidentitywallets.dto.CredentialVerificationRequest; +import org.eclipse.tractusx.managedidentitywallets.dto.CredentialsResponse; +import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; +import org.eclipse.tractusx.managedidentitywallets.signing.SignerResult; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; +import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils; +import org.eclipse.tractusx.managedidentitywallets.utils.Validate; +import org.eclipse.tractusx.ssi.lib.did.resolver.DidResolver; +import org.eclipse.tractusx.ssi.lib.did.web.DidWebResolver; +import org.eclipse.tractusx.ssi.lib.did.web.util.DidWebParser; +import org.eclipse.tractusx.ssi.lib.exception.proof.JwtExpiredException; +import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtValidator; +import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtVerifier; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; +import org.eclipse.tractusx.ssi.lib.proof.LinkedDataProofValidation; +import org.eclipse.tractusx.ssi.lib.serialization.SerializeUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.io.IOException; +import java.net.http.HttpClient; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * The type Issuers credential service. + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class IssuersCredentialService extends BaseService { + + /** + * The constant BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN. + */ + public static final String BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN = "Base wallet BPN is not matching with request BPN(from token)"; + + private final IssuersCredentialRepository issuersCredentialRepository; + private final MIWSettings miwSettings; + + private final SpecificationUtil credentialSpecificationUtil; + + private final HoldersCredentialRepository holdersCredentialRepository; + + private final CommonService commonService; + + private final ObjectMapper objectMapper; + + private Map availableSigningServices; + + + @Override + protected BaseRepository getRepository() { + return issuersCredentialRepository; + } + + @Override + protected SpecificationUtil getSpecificationUtil() { + return credentialSpecificationUtil; + } + + + /** + * Gets credentials. + * + * @param command the command + * @return the credentials + */ + public PageImpl getCredentials(GetCredentialsCommand command) { + FilterRequest filterRequest = new FilterRequest(); + filterRequest.setSize(command.getSize()); + filterRequest.setPage(command.getPageNumber()); + + //Issuer must be caller of API + Wallet issuerWallet = commonService.getWalletByIdentifier(command.getCallerBPN()); + filterRequest.appendCriteria(StringPool.ISSUER_DID, Operator.EQUALS, issuerWallet.getDid()); + + if (StringUtils.hasText(command.getIdentifier())) { + Wallet holderWallet = commonService.getWalletByIdentifier(command.getIdentifier()); + filterRequest.appendCriteria(StringPool.HOLDER_DID, Operator.EQUALS, holderWallet.getDid()); + } + + if (StringUtils.hasText(command.getCredentialId())) { + filterRequest.appendCriteria(StringPool.CREDENTIAL_ID, Operator.EQUALS, command.getCredentialId()); + } + FilterRequest request = new FilterRequest(); + if (!CollectionUtils.isEmpty(command.getType())) { + request.setPage(filterRequest.getPage()); + request.setSize(filterRequest.getSize()); + request.setCriteriaOperator(CriteriaOperator.OR); + for (String str : command.getType()) { + request.appendCriteria(StringPool.TYPE, Operator.CONTAIN, str); + } + } + + Sort sort = new Sort(); + sort.setColumn(command.getSortColumn()); + sort.setSortType(SortType.valueOf(command.getSortType().toUpperCase())); + filterRequest.setSort(sort); + Page filter = filter(filterRequest, request, CriteriaOperator.AND); + + List list = new ArrayList<>(filter.getContent().size()); + + Wallet holderWallet = command.getIdentifier() != null ? commonService.getWalletByIdentifier(command.getIdentifier()) : issuerWallet; + + for (IssuersCredential credential : filter.getContent()) { + CredentialsResponse cr = new CredentialsResponse(); + if (command.isAsJwt()) { + CredentialCreationConfig config = CredentialCreationConfig.builder() + .algorithm(SupportedAlgorithms.ED25519) + .issuerDoc(issuerWallet.getDidDocument()) + .holderDid(holderWallet.getDid()) + .keyName(issuerWallet.getBpn()) + .verifiableCredential(credential.getData()) + .subject(credential.getData().getCredentialSubject().get(0)) + .contexts(credential.getData().getContext()) + .vcId(credential.getData().getId()) + .types(credential.getData().getTypes()) + .encoding(VerifiableEncoding.JWT) + .build(); + + SignerResult signerResult = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(config); + cr.setJwt(signerResult.getJwt()); + } else { + cr.setVc(credential.getData()); + } + list.add(cr); + } + return new PageImpl<>(list, filter.getPageable(), filter.getTotalElements()); + } + + + + /** + * Issue credential using base wallet + * + * @param holderDid the holder did + * @param data the data + * @param asJwt the as jwt + * @param callerBpn the caller bpn + * @return the verifiable credential + */ + @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED) + public CredentialsResponse issueCredentialUsingBaseWallet(String holderDid, Map data, boolean asJwt, String callerBpn) { + //Fetch Holder Wallet + Wallet holderWallet = commonService.getWalletByIdentifier(holderDid); + + VerifiableCredential verifiableCredential = new VerifiableCredential(data); + + Wallet issuerWallet = commonService.getWalletByIdentifier(verifiableCredential.getIssuer().toString()); + + validateAccess(callerBpn, issuerWallet); + + boolean isSelfIssued = isSelfIssued(holderWallet.getBpn()); + + CredentialCreationConfig holdersCredentialCreationConfig = CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .subject(verifiableCredential.getCredentialSubject().get(0)) + .types(verifiableCredential.getTypes()) + .issuerDoc(issuerWallet.getDidDocument()) + .keyName(miwSettings.authorityWalletBpn()) + .holderDid(holderWallet.getDid()) + .contexts(verifiableCredential.getContext()) + .expiryDate(Date.from(verifiableCredential.getExpirationDate())) + .selfIssued(isSelfIssued) + .algorithm(SupportedAlgorithms.valueOf(issuerWallet.getAlgorithm())) + .build(); + + + // Create Credential + SignerResult result = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + VerifiableCredential vc = (VerifiableCredential) result.getJsonLd(); + HoldersCredential holdersCredential = CommonUtils.convertVerifiableCredential(vc, holdersCredentialCreationConfig); + + + //save in holder wallet + holdersCredential = holdersCredentialRepository.save(holdersCredential); + + //Store Credential in issuers table + IssuersCredential issuersCredential = IssuersCredential.of(holdersCredential); + issuersCredential = create(issuersCredential); + + final CredentialsResponse cr = new CredentialsResponse(); + + // Return VC + if (asJwt) { + holdersCredentialCreationConfig.setVerifiableCredential(issuersCredential.getData()); + holdersCredentialCreationConfig.setEncoding(VerifiableEncoding.JWT); + SignerResult credential = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + cr.setJwt(credential.getJwt()); + } else { + cr.setVc(issuersCredential.getData()); + } + + log.debug("VC type of {} issued to bpn ->{}", StringEscapeUtils.escapeJava(verifiableCredential.getTypes().toString()), StringEscapeUtils.escapeJava(holderWallet.getBpn())); + + return cr; + } + + + private JWTVerificationResult verifyVCAsJWT(String jwt, DidResolver didResolver, boolean withCredentialsValidation, boolean withCredentialExpiryDate) throws IOException, ParseException { + SignedJWT signedJWT = SignedJWT.parse(jwt); + Map claims = objectMapper.readValue(signedJWT.getPayload().toBytes(), Map.class); + String vcClaim = objectMapper.writeValueAsString(claims.get("vc")); + Map map = SerializeUtil.fromJson(vcClaim); + VerifiableCredential verifiableCredential = new VerifiableCredential(map); + + //took this approach to avoid issues in sonarQube + return new JWTVerificationResult(validateSignature(withCredentialsValidation, signedJWT, didResolver) && validateJWTExpiryDate(withCredentialExpiryDate, signedJWT), verifiableCredential); + + } + + private record JWTVerificationResult(boolean valid, VerifiableCredential verifiableCredential) { + + } + + private boolean validateSignature(boolean withValidateSignature, SignedJWT signedJWT, DidResolver didResolver) { + if (!withValidateSignature) { + return true; + } + //validate jwt signature + try { + SignedJwtVerifier jwtVerifier = new SignedJwtVerifier(didResolver); + return jwtVerifier.verify(signedJWT); + } catch (Exception e) { + log.error("Can not verify signature of jwt", e); + return false; + } + } + + private boolean validateJWTExpiryDate(boolean withExpiryDate, SignedJWT signedJWT) { + if (!withExpiryDate) { + return true; + } + try { + SignedJwtValidator jwtValidator = new SignedJwtValidator(); + jwtValidator.validateDate(signedJWT); + return true; + } catch (Exception e) { + if (!(e instanceof JwtExpiredException)) { + log.error("Can not validate jwt expiry date ", e); + } + return false; + } + } + + + /** + * Credentials validation map. + * + * @param verificationRequest the verification request + * @param withCredentialExpiryDate the with credential expiry date + * @return the map + */ + public Map credentialsValidation(CredentialVerificationRequest verificationRequest, boolean withCredentialExpiryDate) { + return credentialsValidation(verificationRequest, true, withCredentialExpiryDate); + } + + /** + * Credentials validation map. + * + * @param verificationRequest the verification request + * @param withCredentialsValidation the with credentials validation + * @param withCredentialExpiryDate the with credential expiry date + * @return the map + */ + @SneakyThrows + public Map credentialsValidation(CredentialVerificationRequest verificationRequest, boolean withCredentialsValidation, boolean withCredentialExpiryDate) { + HttpClient httpClient = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .build(); + + + DidResolver didResolver = new DidWebResolver(httpClient, new DidWebParser(), miwSettings.enforceHttps()); + Map response = new TreeMap<>(); + boolean valid; + VerifiableCredential verifiableCredential; + boolean dateValidation = true; + + if (verificationRequest.containsKey(StringPool.VC_JWT_KEY)) { + JWTVerificationResult result = verifyVCAsJWT((String) verificationRequest.get(StringPool.VC_JWT_KEY), didResolver, withCredentialsValidation, withCredentialExpiryDate); + valid = result.valid; + } else { + + verifiableCredential = new VerifiableCredential(verificationRequest); + LinkedDataProofValidation proofValidation = LinkedDataProofValidation.newInstance(didResolver); + + + if (withCredentialsValidation) { + valid = proofValidation.verify(verifiableCredential); + } else { + valid = true; + } + + dateValidation = CommonService.validateExpiry(withCredentialExpiryDate, verifiableCredential, + response); + } + + response.put(StringPool.VALID, valid && dateValidation); + response.put(StringPool.VC, verificationRequest); + + return response; + } + + + private void validateAccess(String callerBpn, Wallet issuerWallet) { + //validate BPN access, VC must be issued by base wallet + Validate.isFalse(callerBpn.equals(issuerWallet.getBpn())).launch(new ForbiddenException(BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN)); + + //issuer must be base wallet + Validate.isFalse(issuerWallet.getBpn().equals(miwSettings.authorityWalletBpn())).launch(new ForbiddenException(BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN)); + } + + private boolean isSelfIssued(String holderBpn) { + return holderBpn.equals(miwSettings.authorityWalletBpn()); + } + + + /** + * Sets key service. + * + * @param availableKeyStorage the available key storage + */ + @Autowired + public void setKeyService(Map availableKeyStorage) { + this.availableSigningServices = availableKeyStorage; + } + +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/JwtPresentationES256KService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/JwtPresentationES256KService.java new file mode 100644 index 000000000..1d057d74b --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/JwtPresentationES256KService.java @@ -0,0 +1,177 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.Algorithm; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JOSEObjectType; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.text.StringEscapeUtils; +import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; +import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; +import org.eclipse.tractusx.managedidentitywallets.exception.SignatureFailureException; +import org.eclipse.tractusx.managedidentitywallets.exception.UnsupportedAlgorithmException; +import org.eclipse.tractusx.ssi.lib.model.did.Did; +import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; +import org.eclipse.tractusx.ssi.lib.model.did.DidDocumentBuilder; +import org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod; +import org.eclipse.tractusx.ssi.lib.model.did.VerificationMethod; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; +import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentation; +import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentationBuilder; +import org.eclipse.tractusx.ssi.lib.serialization.jsonld.JsonLdSerializer; +import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedVerifiablePresentation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.net.URI; +import java.security.interfaces.ECPrivateKey; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import static org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod.JWK_CURVE; +import static org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod.JWK_KEK_TYPE; +import static org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod.JWK_X; +import static org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod.PUBLIC_KEY_JWK; +import static org.eclipse.tractusx.ssi.lib.model.did.VerificationMethod.CONTROLLER; +import static org.eclipse.tractusx.ssi.lib.model.did.VerificationMethod.ID; +import static org.eclipse.tractusx.ssi.lib.model.did.VerificationMethod.TYPE; + + +@Service +@Slf4j +public class JwtPresentationES256KService { + + public static final String JWK_Y = "y"; + + private JsonLdSerializer jsonLdSerializer; + private Did agentDid; + private MIWSettings miwSettings; + + @Autowired + public JwtPresentationES256KService(MIWSettings miwSettings) { + this.miwSettings = miwSettings; + } + + public JwtPresentationES256KService(Did agentDid, JsonLdSerializer jsonLdSerializer) { + this.agentDid = agentDid; + this.jsonLdSerializer = jsonLdSerializer; + } + + public SignedJWT createPresentation(Did issuer, List credentials, String audience, ECPrivateKey ecPrivateKey) { + VerifiablePresentationBuilder verifiablePresentationBuilder = new VerifiablePresentationBuilder(); + final VerifiablePresentation verifiablePresentation = + verifiablePresentationBuilder + .id(URI.create(agentDid.toUri() + "#" + UUID.randomUUID())) + .type(List.of("VerifiablePresentation")) + .verifiableCredentials(credentials) + .build(); + final SerializedVerifiablePresentation serializedVerifiablePresentation = + jsonLdSerializer.serializePresentation(verifiablePresentation); + return createSignedJwt(verifiablePresentation.getId(), issuer, audience, serializedVerifiablePresentation, ecPrivateKey); + } + + + public JWKVerificationMethod getJwkVerificationMethod(ECKey ecKey, Did did) { + Map verificationMethodJson = new HashMap<>(); + Map publicKeyJwk = Map.of(JWK_KEK_TYPE, ecKey.getKeyType().toString(), JWK_CURVE, + ecKey.getCurve().getName(), JWK_X, ecKey.getX().toString(), JWK_Y, ecKey.getY().toString()); + verificationMethodJson.put(ID, URI.create(did + "#" + ecKey.getKeyID())); + verificationMethodJson.put(TYPE, JWKVerificationMethod.DEFAULT_TYPE); + verificationMethodJson.put(CONTROLLER, did.toUri()); + verificationMethodJson.put(PUBLIC_KEY_JWK, publicKeyJwk); + return new JWKVerificationMethod(verificationMethodJson); + } + + public DidDocument buildDidDocument(String bpn, Did did, List jwkVerificationMethods) { + DidDocumentBuilder didDocumentBuilder = new DidDocumentBuilder(); + didDocumentBuilder.id(did.toUri()); + didDocumentBuilder.verificationMethods(jwkVerificationMethods); + DidDocument didDocument = didDocumentBuilder.build(); + //modify context URLs + List context = didDocument.getContext(); + List mutableContext = new ArrayList<>(context); + miwSettings.didDocumentContextUrls().forEach(uri -> { + if (!mutableContext.contains(uri)) { + mutableContext.add(uri); + } + }); + didDocument.put("@context", mutableContext); + didDocument = DidDocument.fromJson(didDocument.toJson()); + log.debug("did document created for bpn ->{}", StringEscapeUtils.escapeJava(bpn)); + return didDocument; + } + + public SignedJWT createSignedJwt(URI id, Did didIssuer, String audience, SerializedVerifiablePresentation serializedPresentation, ECPrivateKey ecPrivateKey) { + String issuer = didIssuer.toString(); + String subject = didIssuer.toString(); + try { + Map vp = new ObjectMapper().readValue(serializedPresentation.getJson(), HashMap.class); + JWTClaimsSet claimsSet = (new JWTClaimsSet.Builder()) + .issuer(issuer) + .subject(subject) + .audience(audience) + .claim("vp", vp) + .expirationTime(new Date((new Date()).getTime() + 60000L)) + .jwtID(id.toString()) + .build(); + return createSignedES256KJwt(ecPrivateKey, claimsSet, issuer); + } catch (IOException e) { + throw new BadDataException("Incorrect VP serialization"); + } + } + + private static SignedJWT createSignedES256KJwt(ECPrivateKey ecPrivateKey, JWTClaimsSet claimsSet, String issuer) { + try { + JWSSigner signer = new ECDSASigner(ecPrivateKey); + signer.getJCAContext().setProvider(BouncyCastleProviderSingleton.getInstance()); + if (!signer.supportedJWSAlgorithms().contains(JWSAlgorithm.ES256K)) { + throw new UnsupportedAlgorithmException(String.format("Invalid signing method. Supported signing methods: %s", + signer.supportedJWSAlgorithms().stream().map(Algorithm::getName).collect(Collectors.joining(", ")))); + } else { + JWSAlgorithm algorithm = JWSAlgorithm.ES256K; + JOSEObjectType type = JOSEObjectType.JWT; + JWSHeader header = new JWSHeader.Builder(algorithm).type(type).keyID(issuer).base64URLEncodePayload(true).build(); + SignedJWT vc = new SignedJWT(header, claimsSet); + vc.sign(signer); + return vc; + } + } catch (JOSEException e) { + throw new SignatureFailureException("Creating signature failed", e); + } + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/LocalSecureTokenService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/LocalSecureTokenService.java new file mode 100644 index 000000000..d6fccfb60 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/LocalSecureTokenService.java @@ -0,0 +1,120 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.service; + +import com.nimbusds.jwt.JWT; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.JtiRecord; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.JtiRepository; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.BusinessPartnerNumber; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyPair; +import org.eclipse.tractusx.managedidentitywallets.exception.UnknownBusinessPartnerNumberException; +import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenIssuer; +import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenService; +import org.eclipse.tractusx.managedidentitywallets.signing.KeyProvider; +import org.eclipse.tractusx.managedidentitywallets.sts.SecureTokenConfigurationProperties; + +import java.time.Instant; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +import static org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils.getJtiAccessToken; + +@Slf4j +@RequiredArgsConstructor +public class LocalSecureTokenService implements SecureTokenService { + + private final WalletRepository walletRepository; + + // Autowired by name!!! + private final SecureTokenIssuer localSecureTokenIssuer; + + private final SecureTokenConfigurationProperties properties; + + private final JtiRepository jtiRepository; + + + @Override + public JWT issueToken(final DID self, final DID partner, final Set scopes, KeyProvider keyProvider) { + log.debug("'issueToken' using scopes and DID."); + KeyPair keyPair = keyProvider.getKeyPair(self); + // IMPORTANT: we re-use the expiration time intentionally to mitigate any kind of timing attacks, + // as we're signing two tokens. + Instant expirationTime = Instant.now().plus(properties.tokenDuration()); + JWT accessToken = this.localSecureTokenIssuer.createAccessToken(keyPair, self, partner, expirationTime, scopes); + checkAndStoreJti(accessToken); + return this.localSecureTokenIssuer.createIdToken(keyPair, self, partner, expirationTime, accessToken); + } + + @Override + public JWT issueToken(DID self, DID partner, JWT accessToken, KeyProvider keyProvider) { + log.debug("'issueToken' using an access_token and DID."); + KeyPair keyPair = keyProvider.getKeyPair(self); + Instant expirationTime = Instant.now().plus(properties.tokenDuration()); + checkAndStoreJti(accessToken); + return this.localSecureTokenIssuer.createIdToken(keyPair, self, partner, expirationTime, accessToken); + } + + private void checkAndStoreJti(JWT accessToken) { + String jtiValue = getJtiAccessToken(accessToken); + JtiRecord jti = jtiRepository.getByJti(UUID.fromString(jtiValue)); + if (Objects.isNull(jti)) { + JtiRecord jtiRecord = JtiRecord.builder().jti(UUID.fromString(jtiValue)).isUsedStatus(false).build(); + jtiRepository.save(jtiRecord); + } + } + + @Override + public JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, Set scopes, KeyProvider keyProvider) { + log.debug("'issueToken' using scopes and BPN."); + KeyPair keyPair = keyProvider.getKeyPair(self.toString()); + Wallet wallet = walletRepository.getByBpn(self.toString()); + DID selfDid = new DID(wallet.getDid()); + DID partnerDid = new DID(Optional.ofNullable(walletRepository.getByBpn(partner.toString())) + .orElseThrow(() -> new UnknownBusinessPartnerNumberException(String.format("The provided BPN '%s' is unknown", partner))) + .getDid()); + // IMPORTANT: we re-use the expiration time intentionally to mitigate any kind of timing attacks, + // as we're signing two tokens. + Instant expirationTime = Instant.now().plus(properties.tokenDuration()); + JWT accessToken = this.localSecureTokenIssuer.createAccessToken(keyPair, selfDid, partnerDid, expirationTime, scopes); + return this.localSecureTokenIssuer.createIdToken(keyPair, selfDid, partnerDid, expirationTime, accessToken); + } + + @Override + public JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, JWT accessToken, KeyProvider keyProvider) { + log.debug("'issueToken' using an access_token and BPN."); + KeyPair keyPair = keyProvider.getKeyPair(self.toString()); + Wallet wallet = walletRepository.getByBpn(self.toString()); + DID selfDid = new DID(wallet.getDid()); + DID partnerDid = new DID(Optional.of(walletRepository.getByBpn(partner.toString())) + .orElseThrow(() -> new UnknownBusinessPartnerNumberException(String.format("The provided BPN '%s' is unknown", partner))) + .getDid()); + Instant expirationTime = Instant.now().plus(properties.tokenDuration()); + return this.localSecureTokenIssuer.createIdToken(keyPair, selfDid, partnerDid, expirationTime, accessToken); + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java similarity index 57% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java index 75739bdaf..a121b84a3 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -21,52 +21,63 @@ package org.eclipse.tractusx.managedidentitywallets.service; -import com.ctc.wstx.shaded.msv_core.util.Uri; import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import com.smartsensesolutions.java.commons.base.repository.BaseRepository; import com.smartsensesolutions.java.commons.base.service.BaseService; import com.smartsensesolutions.java.commons.specification.SpecificationUtil; -import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.JtiRecord; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.JtiRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.PresentationCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.domain.VerifiableEncoding; import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; +import org.eclipse.tractusx.managedidentitywallets.exception.MissingVcTypesException; +import org.eclipse.tractusx.managedidentitywallets.exception.PermissionViolationException; +import org.eclipse.tractusx.managedidentitywallets.signing.SignerResult; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; import org.eclipse.tractusx.managedidentitywallets.utils.Validate; -import org.eclipse.tractusx.ssi.lib.crypt.octet.OctetKeyPairFactory; -import org.eclipse.tractusx.ssi.lib.crypt.x21559.x21559PrivateKey; -import org.eclipse.tractusx.ssi.lib.did.resolver.DidDocumentResolver; import org.eclipse.tractusx.ssi.lib.did.resolver.DidResolver; -import org.eclipse.tractusx.ssi.lib.exception.InvalidJsonLdException; -import org.eclipse.tractusx.ssi.lib.exception.InvalidePrivateKeyFormat; -import org.eclipse.tractusx.ssi.lib.exception.JwtExpiredException; -import org.eclipse.tractusx.ssi.lib.exception.UnsupportedSignatureTypeException; -import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtFactory; +import org.eclipse.tractusx.ssi.lib.exception.json.InvalidJsonLdException; +import org.eclipse.tractusx.ssi.lib.exception.proof.JwtExpiredException; import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtValidator; import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtVerifier; import org.eclipse.tractusx.ssi.lib.model.did.Did; -import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; import org.eclipse.tractusx.ssi.lib.model.did.DidParser; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentation; -import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentationBuilder; -import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentationType; import org.eclipse.tractusx.ssi.lib.proof.LinkedDataProofValidation; -import org.eclipse.tractusx.ssi.lib.proof.SignatureType; -import org.eclipse.tractusx.ssi.lib.serialization.jsonLd.JsonLdSerializerImpl; -import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedJwtPresentationFactory; -import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedJwtPresentationFactoryImpl; +import org.eclipse.tractusx.ssi.lib.serialization.jsonld.JsonLdSerializerImpl; import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedVerifiablePresentation; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.net.URI; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.BLANK_SEPARATOR; +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COMA_SEPARATOR; +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.UNDERSCORE; +import static org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils.getClaimsSet; +import static org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils.getScope; +import static org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils.getStringClaim; +import static org.springframework.security.oauth2.jwt.JwtClaimNames.JTI; /** * The type Presentation service. @@ -78,17 +89,21 @@ public class PresentationService extends BaseService { private final HoldersCredentialRepository holdersCredentialRepository; - private final SpecificationUtil credentialSpecificationUtil; private final CommonService commonService; - private final WalletKeyService walletKeyService; private final MIWSettings miwSettings; private final DidDocumentResolverService didDocumentResolverService; + private final Map availableSigningServices; + + + private final JtiRepository jtiRepository; + + @Override protected BaseRepository getRepository() { return holdersCredentialRepository; @@ -108,7 +123,7 @@ protected SpecificationUtil getSpecificationUtil() { * @param callerBpn the caller bpn * @return the map */ - @SneakyThrows({InvalidePrivateKeyFormat.class}) + @SneakyThrows() public Map createPresentation(Map data, boolean asJwt, String audience, String callerBpn) { List> verifiableCredentialList = (List>) data.get(StringPool.VERIFIABLE_CREDENTIALS); @@ -121,43 +136,38 @@ public Map createPresentation(Map data, boolean verifiableCredentials.add(verifiableCredential); }); + SigningService keyStorageService = availableSigningServices.get(callerWallet.getSigningServiceType()); + Map response = new HashMap<>(); + Did vpIssuerDid = DidParser.parse(callerWallet.getDid()); + + + PresentationCreationConfig.PresentationCreationConfigBuilder builder = PresentationCreationConfig.builder() + .verifiableCredentials(verifiableCredentials) + .keyName(callerWallet.getBpn()) + .vpIssuerDid(vpIssuerDid) + .algorithm(SupportedAlgorithms.ED25519); + if (asJwt) { log.debug("Creating VP as JWT for bpn ->{}", callerBpn); Validate.isFalse(StringUtils.hasText(audience)).launch(new BadDataException("Audience needed to create VP as JWT")); - //Issuer of VP is holder of VC - Did vpIssuerDid = DidParser.parse(callerWallet.getDid()); - - //JWT Factory - SerializedJwtPresentationFactory presentationFactory = new SerializedJwtPresentationFactoryImpl( - new SignedJwtFactory(new OctetKeyPairFactory()), new JsonLdSerializerImpl(), vpIssuerDid); - - //Build JWT - x21559PrivateKey ed25519Key = walletKeyService.getPrivateKeyByWalletIdentifier(callerWallet.getId()); - x21559PrivateKey privateKey = new x21559PrivateKey(ed25519Key.asByte()); - SignedJWT presentation = presentationFactory.createPresentation(vpIssuerDid - , verifiableCredentials, audience, privateKey); - - response.put(StringPool.VP, presentation.serialize()); + builder.encoding(VerifiableEncoding.JWT) + .audience(audience); } else { log.debug("Creating VP as JSON-LD for bpn ->{}", callerBpn); - VerifiablePresentationBuilder verifiablePresentationBuilder = - new VerifiablePresentationBuilder(); - - // Build VP - VerifiablePresentation verifiablePresentation = - verifiablePresentationBuilder - .id(URI.create(miwSettings.authorityWalletDid() + "#" + UUID.randomUUID().toString())) - .type(List.of(VerifiablePresentationType.VERIFIABLE_PRESENTATION)) - .verifiableCredentials(verifiableCredentials) - .build(); - response.put(StringPool.VP, verifiablePresentation); + builder.encoding(VerifiableEncoding.JSON_LD) + .verificationMethod(URI.create(miwSettings.authorityWalletDid() + "#" + UUID.randomUUID())); } + + PresentationCreationConfig presentationConfig = builder.build(); + SignerResult signerResult = keyStorageService.createPresentation(presentationConfig); + + response.put(StringPool.VP, asJwt ? signerResult.getJwt() : signerResult.getJsonLd().toJson()); + return response; } - /** * Validate presentation map. * @@ -276,4 +286,102 @@ private boolean validateCredential(VerifiableCredential credential) { } return isValid; } + + @SneakyThrows + public Map createVpWithRequiredScopes(SignedJWT innerJWT, boolean asJwt) { + + JWTClaimsSet jwtClaimsSet = getClaimsSet(innerJWT); + JtiRecord jtiRecord = getJtiRecord(jwtClaimsSet); + + List holdersCredentials = new ArrayList<>(); + List missingVCTypes = new ArrayList<>(); + List verifiableCredentials = new ArrayList<>(); + + String scopeValue = getScope(jwtClaimsSet); + String[] scopes = scopeValue.split(BLANK_SEPARATOR); + + for (String scope : scopes) { + String[] scopeParts = scope.split(COLON_SEPARATOR); + String vcType = scopeParts[1]; + checkReadPermission(scopeParts[2]); + String vcTypeNoVersion = removeVersion(vcType); + + List credentials = + holdersCredentialRepository.getByHolderDidAndType(jwtClaimsSet.getIssuer(), vcTypeNoVersion); + if ((null == credentials) || credentials.isEmpty()) { + missingVCTypes.add(vcTypeNoVersion); + } else { + holdersCredentials.addAll(credentials); + } + } + + checkMissingVcs(missingVCTypes); + + Wallet callerWallet = commonService.getWalletByIdentifier(jwtClaimsSet.getIssuer()); + + holdersCredentials.forEach(c -> verifiableCredentials.add(c.getData())); + + PresentationCreationConfig.PresentationCreationConfigBuilder builder = PresentationCreationConfig.builder() + .verifiableCredentials(verifiableCredentials) + .keyName(callerWallet.getBpn()) + .vpIssuerDid(DidParser.parse(callerWallet.getDid())); + + + if (asJwt) { + + //Issuer of VP is holder of VC + builder.encoding(VerifiableEncoding.JWT) + .audience(jwtClaimsSet.getAudience().get(0)) + .algorithm(SupportedAlgorithms.ES256K); + } else { + builder.encoding(VerifiableEncoding.JSON_LD) + .verificationMethod(URI.create(miwSettings.authorityWalletDid() + "#" + UUID.randomUUID())) + .algorithm(SupportedAlgorithms.valueOf(callerWallet.getAlgorithm())); + } + + PresentationCreationConfig presentationConfig = builder.build(); + SigningService keyStorageService = availableSigningServices.get(callerWallet.getSigningServiceType()); + SignerResult signerResult = keyStorageService.createPresentation(presentationConfig); + + changeJtiStatus(jtiRecord); + + return Map.of(StringPool.VP, asJwt ? signerResult.getJwt() : signerResult.getJsonLd()); + } + + private void checkReadPermission(String permission) { + if (!"read".equals(permission)) { + throw new PermissionViolationException("Scopes must have only READ permission"); + } + } + + private void checkMissingVcs(List missingVCTypes) { + if (!missingVCTypes.isEmpty()) { + throw new MissingVcTypesException(String.format("Missing VC types: %s", + String.join(COMA_SEPARATOR, missingVCTypes))); + } + } + + private String removeVersion(String vcType) { + String[] parts = vcType.split(UNDERSCORE); + return (parts.length > 1) ? parts[0] : vcType; + } + + private JtiRecord getJtiRecord(JWTClaimsSet jwtClaimsSet) { + String jtiValue = getStringClaim(jwtClaimsSet, JTI); + JtiRecord jtiRecord = jtiRepository.getByJti(UUID.fromString(jtiValue)); + if (Objects.isNull(jtiRecord)) { + JtiRecord jtiToAdd = JtiRecord.builder().jti(UUID.fromString(jtiValue)).isUsedStatus(false).build(); + jtiRepository.save(jtiToAdd); + return jtiToAdd; + } else if (jtiRecord.isUsedStatus()) { + throw new BadDataException("The token was already used"); + } else { + return jtiRecord; + } + } + + private void changeJtiStatus(JtiRecord jtiRecord) { + jtiRecord.setUsedStatus(true); + jtiRepository.save(jtiRecord); + } } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/STSTokenValidationService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/STSTokenValidationService.java new file mode 100644 index 000000000..af9917297 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/STSTokenValidationService.java @@ -0,0 +1,118 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.service; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.managedidentitywallets.constant.TokenValidationErrors; +import org.eclipse.tractusx.managedidentitywallets.dto.ValidationResult; +import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; +import org.eclipse.tractusx.managedidentitywallets.utils.CustomSignedJWTVerifier; +import org.eclipse.tractusx.managedidentitywallets.utils.TokenValidationUtils; +import org.springframework.stereotype.Service; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils.getAccessToken; +import static org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils.getClaimsSet; +import static org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils.parseToken; +import static org.springframework.security.oauth2.core.oidc.IdTokenClaimNames.NONCE; + +@Service +@Slf4j +@RequiredArgsConstructor +public class STSTokenValidationService { + + private final DidDocumentResolverService didDocumentResolverService; + private final CustomSignedJWTVerifier customSignedJWTverifier; + private final TokenValidationUtils tokenValidationUtils; + + /** + * Validates SI token and Access token. + * + * @param token token in a String format + * @return boolean result of validation + */ + public ValidationResult validateToken(String token) { + List validationResults = new ArrayList<>(); + + SignedJWT jwtSI = parseToken(token); + JWTClaimsSet claimsSI = getClaimsSet(jwtSI); + + validationResults.add(tokenValidationUtils.checkIfSubjectValidAndEqualsDid(claimsSI)); + validationResults.add(tokenValidationUtils.checkIfIssuerEqualsSubject(claimsSI)); + validationResults.add(tokenValidationUtils.checkTokenExpiry(claimsSI)); + + Optional accessToken = getAccessToken(claimsSI); + if (accessToken.isPresent()) { + SignedJWT jwtAT = parseToken(accessToken.get()); + JWTClaimsSet claimsAT = getClaimsSet(jwtAT); + + validationResults.add(tokenValidationUtils.checkIfAudienceClaimsAreEqual(claimsSI.getAudience(), claimsAT.getAudience())); + try { + validationResults.add(tokenValidationUtils.checkIfNonceClaimsAreEqual(claimsSI.getStringClaim(NONCE), + claimsAT.getStringClaim(NONCE))); + } catch (ParseException e) { + throw new BadDataException("Could not parse 'nonce' claim in token", e); + } + + String didForOuter = claimsAT.getAudience().get(0); + validationResults.add(verifySignature(didForOuter, jwtSI)); + + String didForInner = claimsAT.getIssuer(); + validationResults.add(verifySignature(didForInner, jwtAT)); + } else { + validationResults.add(tokenValidationUtils.getInvalidResult(TokenValidationErrors.ACCESS_TOKEN_MISSING)); + } + return combineValidationResults(validationResults); + } + + private ValidationResult verifySignature(String did, SignedJWT signedJWT) { + try { + customSignedJWTverifier.setDidResolver(didDocumentResolverService.getCompositeDidResolver()); + return customSignedJWTverifier.verify(did, signedJWT) + ? tokenValidationUtils.getValidResult() + : tokenValidationUtils.getInvalidResult(TokenValidationErrors.SIGNATURE_NOT_VERIFIED); + } catch (JOSEException ex) { + throw new BadDataException("Could not verify signature of jwt", ex); + } + } + + private ValidationResult combineValidationResults(List validationResults) { + List errorsList = new ArrayList<>(); + for (ValidationResult result : validationResults) { + List errors = result.getErrors(); + if (null != errors) { + errorsList.add(errors.get(0)); + } + } + ValidationResult finalResult = ValidationResult.builder().errors(errorsList).build(); + finalResult.setValid(errorsList.isEmpty()); + return finalResult; + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java new file mode 100644 index 000000000..ab7a636a6 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java @@ -0,0 +1,138 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.service; + +import com.smartsensesolutions.java.commons.base.repository.BaseRepository; +import com.smartsensesolutions.java.commons.base.service.BaseService; +import com.smartsensesolutions.java.commons.specification.SpecificationUtil; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.bouncycastle.util.io.pem.PemReader; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; +import org.eclipse.tractusx.managedidentitywallets.exception.UnsupportedAlgorithmException; +import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; +import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519PrivateKey; +import org.eclipse.tractusx.ssi.lib.exception.key.InvalidPrivateKeyFormatException; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.StringReader; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; + +/** + * The type Wallet key service. + */ +@Service +@RequiredArgsConstructor +public class WalletKeyService extends BaseService { + + public static final String EC = "EC"; + + private final WalletKeyRepository walletKeyRepository; + + private final SpecificationUtil specificationUtil; + + private final EncryptionUtils encryptionUtils; + + @Override + public BaseRepository getRepository() { + return walletKeyRepository; + } + + @Override + protected SpecificationUtil getSpecificationUtil() { + return specificationUtil; + } + + /** + * Get private key by wallet identifier as bytes byte [ ]. + * + * @param walletId the wallet id + * @param algorithm the algorithm + * @return the byte [ ] + */ + @SneakyThrows + public byte[] getPrivateKeyByWalletIdAsBytes(long walletId, String algorithm) { + Object privateKey = getPrivateKeyByWalletIdAndAlgorithm(walletId, SupportedAlgorithms.valueOf(algorithm)); + if (privateKey instanceof X25519PrivateKey x25519PrivateKey) { + return x25519PrivateKey.asByte(); + } else { + return ((ECPrivateKey) privateKey).getEncoded(); + } + } + + + @SneakyThrows + public Object getPrivateKeyByKeyId(String keyId, SupportedAlgorithms supportedAlgorithms) { + WalletKey walletKey = walletKeyRepository.getByKeyIdAndAlgorithm(keyId, supportedAlgorithms.name()); + Object privateKey = getKeyObject(SupportedAlgorithms.valueOf(walletKey.getAlgorithm()), encryptionUtils.decrypt(walletKey.getPrivateKey())); + if (privateKey instanceof X25519PrivateKey x25519PrivateKey) { + return x25519PrivateKey; + } else { + return privateKey; + } + } + /** + * Gets private key by wallet identifier. + * + * @param walletId the wallet id + * @param algorithm the algorithm + * @return the private key by wallet identifier + */ + @SneakyThrows + public Object getPrivateKeyByWalletIdAndAlgorithm(long walletId, SupportedAlgorithms algorithm) { + WalletKey wallet = walletKeyRepository.getByWalletIdAndAlgorithm(walletId, algorithm.toString()); + String privateKey = encryptionUtils.decrypt(wallet.getPrivateKey()); + return getKeyObject(algorithm, privateKey); + } + + private static Object getKeyObject(SupportedAlgorithms algorithm, String privateKey) throws IOException, InvalidPrivateKeyFormatException, NoSuchAlgorithmException, InvalidKeySpecException { + byte[] content = new PemReader(new StringReader(privateKey)).readPemObject().getContent(); + if (SupportedAlgorithms.ED25519.equals(algorithm)) { + return new X25519PrivateKey(content); + } else if (SupportedAlgorithms.ES256K.equals(algorithm)) { + KeyFactory kf = KeyFactory.getInstance(EC); + return kf.generatePrivate(new PKCS8EncodedKeySpec(content)); + } else { + throw new UnsupportedAlgorithmException("Unsupported algorithm: " + algorithm); + } + } + + /** + * Gets wallet key by wallet id. + * + * @param walletId the wallet id + * @param supportedAlgorithms the algorithm of private key + * @return the wallet key by wallet identifier + */ + @SneakyThrows + public String getWalletKeyIdByWalletId(long walletId, SupportedAlgorithms supportedAlgorithms) { + return walletKeyRepository.getByWalletIdAndAlgorithm(walletId, supportedAlgorithms.name()).getKeyId(); + } + +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java similarity index 61% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java index 3dd1435e9..0ca388ac2 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -21,6 +21,10 @@ package org.eclipse.tractusx.managedidentitywallets.service; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.KeyType; +import com.nimbusds.jose.jwk.KeyUse; import com.smartsensesolutions.java.commons.FilterRequest; import com.smartsensesolutions.java.commons.base.repository.BaseRepository; import com.smartsensesolutions.java.commons.base.service.BaseService; @@ -28,31 +32,37 @@ import com.smartsensesolutions.java.commons.sort.SortType; import com.smartsensesolutions.java.commons.specification.SpecificationUtil; import jakarta.annotation.PostConstruct; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.text.StringEscapeUtils; -import org.bouncycastle.util.io.pem.PemObject; -import org.bouncycastle.util.io.pem.PemWriter; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; import org.eclipse.tractusx.managedidentitywallets.dto.CreateWalletRequest; import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; import org.eclipse.tractusx.managedidentitywallets.exception.DuplicateWalletProblem; import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; +import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils; import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; import org.eclipse.tractusx.managedidentitywallets.utils.Validate; -import org.eclipse.tractusx.ssi.lib.crypt.IKeyGenerator; import org.eclipse.tractusx.ssi.lib.crypt.KeyPair; import org.eclipse.tractusx.ssi.lib.crypt.jwk.JsonWebKey; -import org.eclipse.tractusx.ssi.lib.crypt.x21559.x21559Generator; import org.eclipse.tractusx.ssi.lib.did.web.DidWebFactory; -import org.eclipse.tractusx.ssi.lib.model.did.*; +import org.eclipse.tractusx.ssi.lib.model.did.Did; +import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; +import org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod; +import org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethodBuilder; +import org.eclipse.tractusx.ssi.lib.model.did.VerificationMethod; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; import org.springframework.beans.factory.annotation.Qualifier; @@ -60,19 +70,18 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import java.io.StringWriter; -import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.REFERENCE_KEY; +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.VAULT_ACCESS_TOKEN; + /** * The type Wallet service. */ @@ -92,8 +101,6 @@ public class WalletService extends BaseService { private final EncryptionUtils encryptionUtils; - private final WalletKeyService walletKeyService; - private final HoldersCredentialRepository holdersCredentialRepository; private final SpecificationUtil walletSpecificationUtil; @@ -102,9 +109,13 @@ public class WalletService extends BaseService { private final CommonService commonService; + private final Map availableSigningServices; + @Qualifier("transactionManager") private final PlatformTransactionManager transactionManager; + private final JwtPresentationES256KService jwtPresentationES256KService; + @Override protected BaseRepository getRepository() { @@ -207,9 +218,17 @@ public Page getWallets(int pageNumber, int size, String sortColumn, Stri * @return the wallet */ @SneakyThrows - @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED) + @Transactional public Wallet createWallet(CreateWalletRequest request, String callerBpn) { - return createWallet(request, false, callerBpn); + TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); + final Wallet[] wallets = new Wallet[1]; + transactionTemplate.execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus status) { + wallets[0] = createWallet(request, false, callerBpn); + } + }); + return wallets[0]; } /** @@ -223,78 +242,112 @@ private Wallet createWallet(CreateWalletRequest request, boolean authority, Stri validateCreateWallet(request, callerBpn); //create private key pair - IKeyGenerator keyGenerator = new x21559Generator(); - KeyPair keyPair = keyGenerator.generateKey(); + final SigningServiceType signingServiceType; + if (authority) { + signingServiceType = miwSettings.authoritySigningServiceType(); + } else { + signingServiceType = request.getSigningServiceType(); + } + + KeyCreationConfig keyCreationConfig = KeyCreationConfig.builder() + .keyName(request.getBusinessPartnerNumber()) + .keyTypes(List.of(KeyType.OCT, KeyType.EC)) + .curve(Curve.SECP256K1) + .build(); + SigningService signingService = availableSigningServices.get(signingServiceType); + + Map keyPairs = signingService.getKeys(keyCreationConfig); //create did json - Did did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), request.getBpn()); - - String keyId = UUID.randomUUID().toString(); - - JsonWebKey jwk = new JsonWebKey(keyId, keyPair.getPublicKey(), keyPair.getPrivateKey()); - JWKVerificationMethod jwkVerificationMethod = - new JWKVerificationMethodBuilder().did(did).jwk(jwk).build(); - - DidDocumentBuilder didDocumentBuilder = new DidDocumentBuilder(); - didDocumentBuilder.id(did.toUri()); - didDocumentBuilder.verificationMethods(List.of(jwkVerificationMethod)); - DidDocument didDocument = didDocumentBuilder.build(); - //modify context URLs - List context = didDocument.getContext(); - List mutableContext = new ArrayList<>(context); - miwSettings.didDocumentContextUrls().forEach(uri -> { - if (!mutableContext.contains(uri)) { - mutableContext.add(uri); + Did did = createDidJson(request.getDidUrl()); + List walletKeyInfos = new ArrayList<>(); + keyPairs.forEach((k, keyPair) -> { + String keyId = UUID.randomUUID().toString(); + JWKVerificationMethod jwkVerificationMethod; + SupportedAlgorithms algorithm; + if (k == KeyType.OCT) { + algorithm = SupportedAlgorithms.ED25519; + JsonWebKey jwk = new JsonWebKey(keyId, keyPair.getPublicKey(), keyPair.getPrivateKey()); + jwkVerificationMethod = + new JWKVerificationMethodBuilder().did(did).jwk(jwk).build(); + } else if (k == KeyType.EC) { + algorithm = SupportedAlgorithms.ES256K; + ECKey ecKey = new ECKey.Builder(Curve.SECP256K1, CommonUtils.ecPublicFrom(keyPair.getPublicKey().asByte())) + .privateKey(CommonUtils.ecPrivateFrom(keyPair.getPrivateKey().asByte())) + .keyID(keyId) + .keyUse(KeyUse.SIGNATURE) + .build(); + jwkVerificationMethod = jwtPresentationES256KService.getJwkVerificationMethod(ecKey, did); + } else { + throw new IllegalArgumentException("unsupported keyType %s".formatted(k.getValue())); } + + walletKeyInfos.add(new WalletKeyInfo(keyId, keyPair, algorithm, jwkVerificationMethod)); }); - didDocument.put("@context", mutableContext); - didDocument = DidDocument.fromJson(didDocument.toJson()); - log.debug("did document created for bpn ->{}", StringEscapeUtils.escapeJava(request.getBpn())); + + + DidDocument didDocument = jwtPresentationES256KService.buildDidDocument(request.getBusinessPartnerNumber(), did, walletKeyInfos.stream().map(wki -> wki.verificationMethod).toList()); //Save wallet Wallet wallet = create(Wallet.builder() .didDocument(didDocument) - .bpn(request.getBpn()) - .name(request.getName()) + .bpn(request.getBusinessPartnerNumber()) + .name(request.getCompanyName()) .did(did.toUri().toString()) .algorithm(StringPool.ED_25519) + .signingServiceType(signingServiceType) .build()); + var walletsKeys = walletKeyInfos.stream().map(e -> + WalletKey.builder() + .wallet(wallet) + .keyId(e.keyId) + .referenceKey(REFERENCE_KEY) + .vaultAccessToken(VAULT_ACCESS_TOKEN) + .privateKey(encryptionUtils.encrypt(CommonUtils.getKeyString(e.keyPair.getPrivateKey().asByte(), StringPool.PRIVATE_KEY))) + .publicKey(encryptionUtils.encrypt(CommonUtils.getKeyString(e.keyPair.getPublicKey().asByte(), StringPool.PUBLIC_KEY))) + .algorithm(e.algorithm.name()) + .build() + ).toList(); - //Save key - walletKeyService.getRepository().save(WalletKey.builder() - .walletId(wallet.getId()) - .keyId(keyId) - .referenceKey("dummy ref key, removed once vault setup is ready") - .vaultAccessToken("dummy vault access token, removed once vault setup is ready") - .privateKey(encryptionUtils.encrypt(getPrivateKeyString(keyPair.getPrivateKey().asByte()))) - .publicKey(encryptionUtils.encrypt(getPublicKeyString(keyPair.getPublicKey().asByte()))) - .build()); - log.debug("Wallet created for bpn ->{}", StringEscapeUtils.escapeJava(request.getBpn())); - Wallet issuerWallet = walletRepository.getByBpn(miwSettings.authorityWalletBpn()); + signingService.saveKeys(walletsKeys); - //issue BPN credentials - issuersCredentialService.issueBpnCredential(issuerWallet, wallet, authority); + log.debug("Wallet created for bpn ->{}", StringEscapeUtils.escapeJava(request.getBusinessPartnerNumber())); return wallet; } + private Did createDidJson(String didUrl) { + String[] split = didUrl.split(COLON_SEPARATOR); + if (split.length == 1) { + return DidWebFactory.fromHostname(didUrl); + } else if (split.length == 2) { + return DidWebFactory.fromHostnameAndPath(split[0], split[1]); + } else { + int i = didUrl.lastIndexOf(COLON_SEPARATOR); + String[] splitByLast = { didUrl.substring(0, i), didUrl.substring(i + 1) }; + return DidWebFactory.fromHostnameAndPath(splitByLast[0], splitByLast[1]); + } + } + /** * Create authority wallet on application start up, skip if already created. */ @PostConstruct public void createAuthorityWallet() { TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); + final Wallet[] wallets = new Wallet[1]; transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { if (!walletRepository.existsByBpn(miwSettings.authorityWalletBpn())) { CreateWalletRequest request = CreateWalletRequest.builder() - .name(miwSettings.authorityWalletName()) - .bpn(miwSettings.authorityWalletBpn()) + .companyName(miwSettings.authorityWalletName()) + .businessPartnerNumber(miwSettings.authorityWalletBpn()) + .didUrl(miwSettings.host() + COLON_SEPARATOR + miwSettings.authorityWalletBpn()) .build(); - createWallet(request, true, miwSettings.authorityWalletBpn()); + wallets[0] = createWallet(request, true, miwSettings.authorityWalletBpn()); log.info("Authority wallet created with bpn {}", StringEscapeUtils.escapeJava(miwSettings.authorityWalletBpn())); } else { log.info("Authority wallet exists with bpn {}", StringEscapeUtils.escapeJava(miwSettings.authorityWalletBpn())); @@ -303,35 +356,24 @@ protected void doInTransactionWithoutResult(TransactionStatus status) { }); } + private void validateCreateWallet(CreateWalletRequest request, String callerBpn) { // check base wallet Validate.isFalse(callerBpn.equalsIgnoreCase(miwSettings.authorityWalletBpn())).launch(new ForbiddenException(BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN)); // check wallet already exists - boolean exist = walletRepository.existsByBpn(request.getBpn()); + boolean exist = walletRepository.existsByBpn(request.getBusinessPartnerNumber()); if (exist) { - throw new DuplicateWalletProblem("Wallet is already exists for bpn " + request.getBpn()); + throw new DuplicateWalletProblem("Wallet is already exists for bpn " + request.getBusinessPartnerNumber()); } } - @SneakyThrows - private String getPrivateKeyString(byte[] privateKeyBytes) { - StringWriter stringWriter = new StringWriter(); - PemWriter pemWriter = new PemWriter(stringWriter); - pemWriter.writeObject(new PemObject("PRIVATE KEY", privateKeyBytes)); - pemWriter.flush(); - pemWriter.close(); - return stringWriter.toString(); - } - @SneakyThrows - private String getPublicKeyString(byte[] publicKeyBytes) { - StringWriter stringWriter = new StringWriter(); - PemWriter pemWriter = new PemWriter(stringWriter); - pemWriter.writeObject(new PemObject("PUBLIC KEY", publicKeyBytes)); - pemWriter.flush(); - pemWriter.close(); - return stringWriter.toString(); + @RequiredArgsConstructor + private class WalletKeyInfo { + private final String keyId; + private final KeyPair keyPair; + private final SupportedAlgorithms algorithm; + private final VerificationMethod verificationMethod; } - } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/KeyProvider.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/KeyProvider.java new file mode 100644 index 000000000..e8b925dad --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/KeyProvider.java @@ -0,0 +1,64 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.signing; + +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyPair; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyStorageType; + +import java.util.List; + +/** + * This class will be used by LocalSigningService to retrieve and save keys + * + * @see LocalSigningService + */ +public interface KeyProvider { + + /** + * @param keyName the name of the key that is to be retrieved + * @return the key as a byte-array + * + */ + Object getPrivateKey(String keyName, SupportedAlgorithms algorithm); + + /** + * @param walletKey the key to save + */ + void saveKeys(List walletKey); + + + String getKeyId(String keyName, SupportedAlgorithms algorithm); + + + /** + * @return the type of KeyProvider + * + * @see KeyStorageType + */ + KeyStorageType getKeyStorageType(); + + KeyPair getKeyPair(DID self); + KeyPair getKeyPair(String bpn); +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalKeyProvider.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalKeyProvider.java new file mode 100644 index 000000000..93353361d --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalKeyProvider.java @@ -0,0 +1,90 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.signing; + +import lombok.RequiredArgsConstructor; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyPair; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyStorageType; +import org.eclipse.tractusx.managedidentitywallets.service.WalletKeyService; +import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@RequiredArgsConstructor +public class LocalKeyProvider implements KeyProvider { + + private final WalletKeyService walletKeyService; + + private final WalletKeyRepository walletKeyRepository; + + private final EncryptionUtils encryptionUtils; + + @Override + public Object getPrivateKey(String keyName, SupportedAlgorithms algorithm) { + WalletKey walletKey = walletKeyRepository.getByAlgorithmAndWallet_Bpn(algorithm.name(), keyName); + return walletKeyService.getPrivateKeyByKeyId(walletKey.getKeyId(), algorithm); + } + + @Override + public void saveKeys(List walletKeys) { + walletKeyRepository.saveAllAndFlush(walletKeys); + } + + @Override + public String getKeyId(String keyName, SupportedAlgorithms algorithm) { + WalletKey walletKey = walletKeyRepository.getByAlgorithmAndWallet_Bpn(algorithm.name(), keyName); + return walletKey.getKeyId(); + } + + @Override + public KeyStorageType getKeyStorageType() { + return KeyStorageType.DB; + } + + @Override + public KeyPair getKeyPair(DID self) { + KeyPair dto = walletKeyRepository.findFirstByWallet_Did(self.toString()).toDto(); + + return new KeyPair( + dto.keyId(), + encryptionUtils.decrypt(dto.privateKey()), + encryptionUtils.decrypt(dto.publicKey()) + ); + } + + @Override + public KeyPair getKeyPair(String bpn) { + KeyPair dto = walletKeyRepository.findFirstByWallet_Bpn(bpn).toDto(); + + return new KeyPair( + dto.keyId(), + encryptionUtils.decrypt(dto.privateKey()), + encryptionUtils.decrypt(dto.publicKey()) + ); + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningService.java new file mode 100644 index 000000000..67a205afa --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningService.java @@ -0,0 +1,36 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.signing; + +/** + * Specialized interface for SigningServices that will sign credentials/presentations locally + * (may retrieve the keys from remote via KeyProvider) + * + * @see SigningService + * @see KeyProvider + */ +public interface LocalSigningService extends SigningService { + /** + * @param keyProvider the KeyProvider to be used by the implementation + */ + void setKeyProvider(KeyProvider keyProvider); +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningServiceImpl.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningServiceImpl.java new file mode 100644 index 000000000..1b1198ff3 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningServiceImpl.java @@ -0,0 +1,375 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.signing; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.KeyType; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.SignedJWT; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.apache.commons.lang3.NotImplementedException; +import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.domain.BusinessPartnerNumber; +import org.eclipse.tractusx.managedidentitywallets.domain.CredentialCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.PresentationCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.domain.VerifiableEncoding; +import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; +import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenService; +import org.eclipse.tractusx.managedidentitywallets.service.JwtPresentationES256KService; +import org.eclipse.tractusx.ssi.lib.crypt.IKeyGenerator; +import org.eclipse.tractusx.ssi.lib.crypt.IPrivateKey; +import org.eclipse.tractusx.ssi.lib.crypt.IPublicKey; +import org.eclipse.tractusx.ssi.lib.crypt.KeyPair; +import org.eclipse.tractusx.ssi.lib.crypt.octet.OctetKeyPairFactory; +import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519Generator; +import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519PrivateKey; +import org.eclipse.tractusx.ssi.lib.exception.json.TransformJsonLdException; +import org.eclipse.tractusx.ssi.lib.exception.key.InvalidPrivateKeyFormatException; +import org.eclipse.tractusx.ssi.lib.exception.key.KeyGenerationException; +import org.eclipse.tractusx.ssi.lib.exception.proof.SignatureGenerateFailedException; +import org.eclipse.tractusx.ssi.lib.exception.proof.UnsupportedSignatureTypeException; +import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtFactory; +import org.eclipse.tractusx.ssi.lib.model.JsonLdObject; +import org.eclipse.tractusx.ssi.lib.model.base.EncodeType; +import org.eclipse.tractusx.ssi.lib.model.did.DidParser; +import org.eclipse.tractusx.ssi.lib.model.proof.Proof; +import org.eclipse.tractusx.ssi.lib.model.verifiable.Verifiable; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialBuilder; +import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentation; +import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentationBuilder; +import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentationType; +import org.eclipse.tractusx.ssi.lib.proof.LinkedDataProofGenerator; +import org.eclipse.tractusx.ssi.lib.proof.SignatureType; +import org.eclipse.tractusx.ssi.lib.serialization.jsonld.JsonLdSerializerImpl; +import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedJwtPresentationFactory; +import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedJwtPresentationFactoryImpl; +import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedJwtVCFactoryImpl; +import org.springframework.stereotype.Component; + +import java.net.URI; +import java.security.KeyFactory; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class LocalSigningServiceImpl implements LocalSigningService { + + // this is not autowired by name, and injected by ApplicationConfig, + // since keys could be saved in hashicorp (=> HashicorpKeyProvider, e.g.) and retrieved from there + private KeyProvider keyProvider; + + // Autowired by name!!! + private final SecureTokenService localSecureTokenService; + + @Override + public SignerResult createCredential(CredentialCreationConfig config) { + + byte[] privateKeyBytes = getPrivateKeyBytes(config.getKeyName(), config.getAlgorithm()); + VerifiableEncoding encoding = Objects.requireNonNull(config.getEncoding()); + SignerResult.SignerResultBuilder resultBuilder = SignerResult.builder().encoding(encoding); + switch (encoding) { + case JSON_LD -> { + return resultBuilder.jsonLd(createVerifiableCredential(config, privateKeyBytes)).build(); + } + case JWT -> { + SignedJWT verifiableCredentialAsJwt = createVerifiableCredentialAsJwt(config); + return resultBuilder.jwt(verifiableCredentialAsJwt.serialize()).build(); + } + default -> + throw new IllegalArgumentException("encoding %s is not supported".formatted(config.getEncoding())); + + } + } + + private byte[] getPrivateKeyBytes(String keyName, SupportedAlgorithms supportedAlgorithms) { + byte[] privateKeyBytes; + if (supportedAlgorithms.equals(SupportedAlgorithms.ED25519)) { + privateKeyBytes = ((IPrivateKey) keyProvider.getPrivateKey(keyName, supportedAlgorithms)).asByte(); + } else if (supportedAlgorithms.equals(SupportedAlgorithms.ES256K)) { + ECPrivateKey ecKey = (ECPrivateKey) keyProvider.getPrivateKey(keyName, supportedAlgorithms); + privateKeyBytes = ecKey.getEncoded(); + } else { + throw new IllegalArgumentException("Unknown algorithm " + supportedAlgorithms); + } + return privateKeyBytes; + } + + @SneakyThrows + private SignedJWT createVerifiableCredentialAsJwt(CredentialCreationConfig config) { + if (!config.getAlgorithm().equals(SupportedAlgorithms.ED25519)) { + throw new IllegalArgumentException("VC as JWT is not supported for provided algorithm -> " + config.getAlgorithm()); + } + // JWT Factory + SerializedJwtVCFactoryImpl vcFactory = new SerializedJwtVCFactoryImpl( + new SignedJwtFactory(new OctetKeyPairFactory())); + IPrivateKey iPrivateKey = ((IPrivateKey) keyProvider.getPrivateKey(config.getKeyName(), config.getAlgorithm())); + + return vcFactory.createVCJwt(DidParser.parse(config.getIssuerDoc().getId()), DidParser.parse(config.getHolderDid()), config.getVerifiableCredential(), + iPrivateKey, + keyProvider.getKeyId(config.getKeyName(), config.getAlgorithm())); + } + + @Override + + public Map getKeys(KeyCreationConfig config) throws KeyGenerationException { + List keyTypes = Objects.requireNonNull(config.getKeyTypes()); + Map result = new HashMap<>(); + for (KeyType keyType : keyTypes) { + switch (keyType.getValue().toUpperCase()) { + case "EC" -> { + try { + ECKey ecKey = new ECKeyGenerator(Curve.SECP256K1) + .provider(BouncyCastleProviderSingleton.getInstance()) + .generate(); + ECPrivateKey ecPrivateKey = ecKey.toECPrivateKey(); + ECPublicKey ecPublicKey = ecKey.toECPublicKey(); + + result.put(keyType, new KeyPair(new IPublicKey() { + @Override + public int getKeyLength() { + return 0; + } + + @Override + public String asStringForStoring() { + return null; + } + + @Override + public String asStringForExchange(EncodeType encodeType) { + return null; + } + + @Override + public byte[] asByte() { + return ecPublicKey.getEncoded(); + } + }, new IPrivateKey() { + @Override + public int getKeyLength() { + return 0; + } + + @Override + public String asStringForStoring() { + return null; + } + + @Override + public String asStringForExchange(EncodeType encodeType) { + return null; + } + + @Override + public byte[] asByte() { + return ecPrivateKey.getEncoded(); + } + })); + } catch (JOSEException e) { + throw new BadDataException("Could not generate EC Jwk", e); + } + } + case "RSA" -> + throw new NotImplementedException("%s is not implemented yet".formatted(keyType.toString())); + case "OCT" -> { + IKeyGenerator keyGenerator = new X25519Generator(); + result.put(keyType, keyGenerator.generateKey()); + } + default -> throw new IllegalArgumentException("%s is not supported".formatted(keyType.toString())); + } + } + return result; + } + + @Override + public SigningServiceType getSupportedServiceType() { + return SigningServiceType.LOCAL; + } + + @Override + public SignerResult createPresentation(PresentationCreationConfig config) { + byte[] privateKeyBytes = getPrivateKeyBytes(config.getKeyName(), config.getAlgorithm()); + VerifiableEncoding encoding = Objects.requireNonNull(config.getEncoding()); + SignerResult.SignerResultBuilder resultBuilder = SignerResult.builder().encoding(encoding); + switch (config.getEncoding()) { + case JWT -> { + return resultBuilder.jwt(generateJwtPresentation(config, privateKeyBytes).serialize()).build(); + } + case JSON_LD -> { + try { + return resultBuilder.jsonLd(generateJsonLdPresentation(config, privateKeyBytes)).build(); + } catch (UnsupportedSignatureTypeException | InvalidPrivateKeyFormatException | + SignatureGenerateFailedException | TransformJsonLdException e) { + throw new IllegalStateException(e); + } + } + default -> + throw new IllegalArgumentException("encoding %s is not supported".formatted(config.getEncoding())); + } + } + + @Override + public void setKeyProvider(KeyProvider keyProvider) { + this.keyProvider = Objects.requireNonNull(keyProvider); + } + + @Override + public void saveKeys(List key) { + keyProvider.saveKeys(key); + } + + private SignedJWT generateJwtPresentation(PresentationCreationConfig config, byte[] privateKeyBytes) { + + + if (config.getAlgorithm() == SupportedAlgorithms.ES256K) { + return buildES256K(config, privateKeyBytes); + } else if (config.getAlgorithm() == SupportedAlgorithms.ED25519) { + return buildED25519(config, privateKeyBytes); + } + + throw new IllegalArgumentException("algorithm %s is not supported for VP JWT".formatted(config.getAlgorithm().name())); + } + + private VerifiablePresentation generateJsonLdPresentation(PresentationCreationConfig config, byte[] privateKeyBytes) throws UnsupportedSignatureTypeException, InvalidPrivateKeyFormatException, SignatureGenerateFailedException, TransformJsonLdException { + VerifiablePresentationBuilder verifiablePresentationBuilder = + new VerifiablePresentationBuilder().id(URI.create(config.getVpIssuerDid() + "#" + UUID.randomUUID().toString())) + .type(List.of(VerifiablePresentationType.VERIFIABLE_PRESENTATION)) + .verifiableCredentials(config.getVerifiableCredentials()); + + + VerifiablePresentation verifiablePresentation = verifiablePresentationBuilder.build(); + List contexts = verifiablePresentation.getContext().stream().map(URI::toString).collect(Collectors.toList()); + if (!contexts.contains(StringPool.W3_ID_JWS_2020_V1_CONTEXT_URL)) { + contexts.add(StringPool.W3_ID_JWS_2020_V1_CONTEXT_URL); + } + verifiablePresentation.put(JsonLdObject.CONTEXT, contexts); + LinkedDataProofGenerator generator = LinkedDataProofGenerator.newInstance(SignatureType.JWS); + + X25519PrivateKey privateKey = new X25519PrivateKey(privateKeyBytes); + + Proof proof = generator.createProof(verifiablePresentation, config.getVerificationMethod(), + privateKey); + verifiablePresentation.put(Verifiable.PROOF, proof); + return verifiablePresentation; + } + + @SneakyThrows + private static VerifiableCredential createVerifiableCredential(CredentialCreationConfig config, byte[] privateKeyBytes) { + //VC Builder + + // if the credential does not contain the JWS proof-context add it + URI jwsUri = URI.create("https://w3id.org/security/suites/jws-2020/v1"); + if (!config.getContexts().contains(jwsUri)) { + config.getContexts().add(jwsUri); + } + + + URI id = URI.create(UUID.randomUUID().toString()); + VerifiableCredentialBuilder builder = new VerifiableCredentialBuilder() + .context(config.getContexts()) + .id(URI.create(config.getIssuerDoc().getId() + "#" + id)) + .type(config.getTypes()) + .issuer(config.getIssuerDoc().getId()) + .expirationDate(config.getExpiryDate() != null ? config.getExpiryDate().toInstant() : null) + .issuanceDate(Instant.now()) + .credentialSubject(config.getSubject()); + + + LinkedDataProofGenerator generator = LinkedDataProofGenerator.newInstance(SignatureType.JWS); + URI verificationMethod = config.getIssuerDoc().getVerificationMethods().get(0).getId(); + + Proof proof = generator.createProof(builder.build(), verificationMethod, new X25519PrivateKey(privateKeyBytes)); + + //Adding Proof to VC + builder.proof(proof); + + //Create Credential + return builder.build(); + } + + + private SignedJWT buildED25519(PresentationCreationConfig config, byte[] privateKeyBytes) { + SerializedJwtPresentationFactory presentationFactory = new SerializedJwtPresentationFactoryImpl( + new SignedJwtFactory(new OctetKeyPairFactory()), new JsonLdSerializerImpl(), config.getVpIssuerDid()); + + X25519PrivateKey privateKey = null; + try { + privateKey = new X25519PrivateKey(privateKeyBytes); + } catch (InvalidPrivateKeyFormatException e) { + throw new IllegalArgumentException(e); + } + return presentationFactory.createPresentation(config.getVpIssuerDid(), config.getVerifiableCredentials(), config.getAudience(), privateKey, config.getKeyName()); + } + + @SneakyThrows + private SignedJWT buildES256K(PresentationCreationConfig config, byte[] privateKeyBytes) { + var kf = KeyFactory.getInstance("EC"); + var privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + ECPrivateKey ecPrivateKey = (ECPrivateKey) kf.generatePrivate(privateKeySpec); + + JwtPresentationES256KService presentationFactory = new JwtPresentationES256KService(config.getVpIssuerDid(), new JsonLdSerializerImpl()); + return presentationFactory.createPresentation(config.getVpIssuerDid() + , config.getVerifiableCredentials(), config.getAudience(), ecPrivateKey); + } + + + @Override + public JWT issueToken(DID self, DID partner, Set scopes) { + return localSecureTokenService.issueToken(self, partner, scopes, keyProvider); + } + + @Override + public JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, Set scopes) { + return localSecureTokenService.issueToken(self, partner, scopes, keyProvider); + } + + @Override + public JWT issueToken(DID self, DID partner, JWT accessToken) { + return localSecureTokenService.issueToken(self, partner, accessToken, keyProvider); + } + + @Override + public JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, JWT accessToken) { + return localSecureTokenService.issueToken(self, partner, accessToken, keyProvider); + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SignerResult.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SignerResult.java new file mode 100644 index 000000000..4db8e99ca --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SignerResult.java @@ -0,0 +1,37 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.signing; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import org.eclipse.tractusx.managedidentitywallets.domain.VerifiableEncoding; +import org.eclipse.tractusx.ssi.lib.model.verifiable.Verifiable; + +@Getter +@Setter +@Builder +public class SignerResult { + private VerifiableEncoding encoding; + private Verifiable jsonLd; + private String jwt; +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SigningService.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SigningService.java new file mode 100644 index 000000000..579af4a51 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SigningService.java @@ -0,0 +1,89 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.signing; + +import com.nimbusds.jose.jwk.KeyType; +import com.nimbusds.jwt.JWT; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.domain.BusinessPartnerNumber; +import org.eclipse.tractusx.managedidentitywallets.domain.CredentialCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.PresentationCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.ssi.lib.crypt.KeyPair; +import org.eclipse.tractusx.ssi.lib.exception.key.KeyGenerationException; + + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Service used to sign the verifiable credentials and verifiable presentations + */ +public interface SigningService { + /** + * @param config the configuration for creating the credential + * @return SignerResult containing the signed credential + * @see CredentialCreationConfig + * @see SignerResult + */ + SignerResult createCredential(CredentialCreationConfig config); + + + /** + * @param config the config for creating/retrieving the key + * @return KeyPair containing the public and private key (private key depends on the type of signing service used) + * @throws KeyGenerationException when something goes wrong + * @see KeyPair + * @see KeyCreationConfig + */ + Map getKeys(KeyCreationConfig config) throws KeyGenerationException; + + /** + * @param key the key to be saved, LocalSigningService implementations may call KeyProvider.saveKey + * @see KeyProvider + */ + void saveKeys(List key); + + /** + * @return the implementation's supported type + */ + SigningServiceType getSupportedServiceType(); + + /** + * @param config the configuration for creating the presentation + * @return SignerResult containing the signed presentation + * @see PresentationCreationConfig + * @see SignerResult + */ + SignerResult createPresentation(PresentationCreationConfig config); + + JWT issueToken(DID self, DID partner, Set scopes); + + JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, Set scopes); + + JWT issueToken(DID self, DID partner, JWT accessToken); + + JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, JWT accessToken); +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/LocalSecureTokenIssuer.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/LocalSecureTokenIssuer.java new file mode 100644 index 000000000..32709faa7 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/LocalSecureTokenIssuer.java @@ -0,0 +1,111 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.sts; + +import com.nimbusds.jose.JOSEObjectType; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.Ed25519Signer; +import com.nimbusds.jose.jwk.OctetKeyPair; +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyPair; +import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenIssuer; +import org.eclipse.tractusx.ssi.lib.crypt.octet.OctetKeyPairFactory; +import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519PrivateKey; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.Date; +import java.util.Set; +import java.util.UUID; + +import static org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils.getNonceAccessToken; +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.ACCESS_TOKEN; +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.SCOPE; +import static org.springframework.security.oauth2.core.oidc.IdTokenClaimNames.NONCE; + +@Slf4j +@Component +@RequiredArgsConstructor +public class LocalSecureTokenIssuer implements SecureTokenIssuer { + + @Override + public JWT createIdToken(KeyPair keyPair, DID self, DID partner, Instant expirationTime, JWT accessToken) { + log.debug("'createIdToken' using a provided access_token."); + return createSignedJWT(keyPair, new JWTClaimsSet.Builder() + .issuer(self.toString()) + .audience(partner.toString()) + .subject(self.toString()) + .expirationTime(Date.from(expirationTime)) + .claim(NONCE, getNonceAccessToken(accessToken)) + .claim(ACCESS_TOKEN, accessToken.serialize())); + } + + @Override + public JWT createAccessToken(KeyPair keyPair, DID self, DID partner, Instant expirationTime, Set scopes) { + log.debug("'createAccessToken' using scopes."); + return createSignedJWT(keyPair, new JWTClaimsSet.Builder() + .issuer(self.toString()) + .audience(self.toString()) + .subject(partner.toString()) + .expirationTime(Date.from(expirationTime)) + .claim(NONCE, UUID.randomUUID().toString()) + .claim(SCOPE, String.join(" ", scopes))); + } + + @SneakyThrows + private JWT createSignedJWT(KeyPair keyPair, JWTClaimsSet.Builder builder) { + log.debug("Creating JWS header for issuer '{}' and holder '{}'", builder.getClaims().get("iss"), + builder.getClaims().get("sub")); + // todo bri: we're hard-coding the algorithm for now. Should become dynamic in the future. + JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.EdDSA) + .type(JOSEObjectType.JWT) + .keyID(keyPair.keyId()) + .build(); + + log.debug("Creating JWS body for issuer '{}' and holder '{}'", builder.getClaims().get("iss"), + builder.getClaims().get("sub")); + JWTClaimsSet body = builder + .issueTime(Date.from(Instant.now())) + .jwtID(UUID.randomUUID().toString()) + .build(); + + log.debug("Creating JWS signature for issuer '{}' and holder '{}'", builder.getClaims().get("iss"), + builder.getClaims().get("sub")); + SignedJWT signedJWT = new SignedJWT(header, body); + String privateKey = keyPair.privateKey(); + // todo bri: this should become dynamic in the future, as we want to support more key algos. + OctetKeyPair jwk = new OctetKeyPairFactory().fromPrivateKey(new X25519PrivateKey(privateKey, true)); + Ed25519Signer signer = new Ed25519Signer(jwk); + signedJWT.sign(signer); + log.debug("JWT signed for issuer '{}' and holder '{}'", builder.getClaims().get("iss"), + builder.getClaims().get("sub")); + + return signedJWT; + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenBeanConfig.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenBeanConfig.java new file mode 100644 index 000000000..273f06ebc --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenBeanConfig.java @@ -0,0 +1,45 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.sts; + +import org.eclipse.tractusx.managedidentitywallets.dao.repository.JtiRepository; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; +import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenIssuer; +import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenService; +import org.eclipse.tractusx.managedidentitywallets.service.LocalSecureTokenService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SecureTokenBeanConfig { + + @Bean + public SecureTokenService secureTokenService( + WalletRepository walletRepository, + SecureTokenIssuer issuer, + SecureTokenConfigurationProperties properties, + JtiRepository jtiRepository + ) { + return new LocalSecureTokenService(walletRepository, issuer, properties, jtiRepository); + } + +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenConfigurationProperties.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenConfigurationProperties.java new file mode 100644 index 000000000..916f829c9 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenConfigurationProperties.java @@ -0,0 +1,30 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.sts; + +import java.time.Duration; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "sts") +public record SecureTokenConfigurationProperties(Duration tokenDuration) { +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java new file mode 100644 index 000000000..c38a50d64 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java @@ -0,0 +1,120 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemWriter; +import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; +import org.eclipse.tractusx.managedidentitywallets.domain.CredentialCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.dto.SecureTokenRequest; +import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; +import org.springframework.util.MultiValueMap; + +import java.io.StringWriter; +import java.security.KeyFactory; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * The type Common utils. + */ +@UtilityClass +public class CommonUtils { + + public static final Pattern BPN_NUMBER_PATTERN = Pattern.compile(StringPool.BPN_NUMBER_REGEX); + + /** + * Gets identifier type. + * + * @param identifier the identifier + * @return the identifier type + */ + public static String getIdentifierType(String identifier) { + if (identifier.startsWith("did:web")) { + return StringPool.DID; + } else { + Validate.isFalse(BPN_NUMBER_PATTERN.matcher(identifier).matches()) + .launch(new BadDataException("Invalid BPN number - " + identifier)); + return StringPool.BPN; + } + } + + + public static HoldersCredential convertVerifiableCredential(VerifiableCredential verifiableCredential, CredentialCreationConfig config) { + List cloneTypes = new ArrayList<>(config.getTypes()); + cloneTypes.remove(VerifiableCredentialType.VERIFIABLE_CREDENTIAL); + + // Create Credential + return HoldersCredential.builder() + .holderDid(config.getHolderDid()) + .issuerDid(config.getIssuerDoc().getId().toString()) + .type(String.join(",", cloneTypes)) + .credentialId(verifiableCredential.getId().toString()) + .data(verifiableCredential) + .selfIssued(config.isSelfIssued()) + .build(); + } + + @SneakyThrows + public static String getKeyString(byte[] privateKeyBytes, String keyType) { + StringWriter stringWriter = new StringWriter(); + PemWriter pemWriter = new PemWriter(stringWriter); + pemWriter.writeObject(new PemObject(keyType, privateKeyBytes)); + pemWriter.flush(); + pemWriter.close(); + return stringWriter.toString(); + } + + + public static SecureTokenRequest getSecureTokenRequest(MultiValueMap map) { + final ObjectMapper objectMapper = new ObjectMapper(); + Map singleValueMap = map.toSingleValueMap(); + return objectMapper.convertValue(singleValueMap, SecureTokenRequest.class); + } + + @SneakyThrows + public static ECPrivateKey ecPrivateFrom(byte[] encoded) { + var kf = KeyFactory.getInstance("EC"); + var privateKeySpec = new PKCS8EncodedKeySpec(encoded); + return (ECPrivateKey) kf.generatePrivate(privateKeySpec); + } + + @SneakyThrows + public static ECPublicKey ecPublicFrom(byte[] encoded) { + var kf = KeyFactory.getInstance("EC"); + var publicKeySpec = new X509EncodedKeySpec(encoded); + return (ECPublicKey) kf.generatePublic(publicKeySpec); + } + +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CustomSignedJWTVerifier.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CustomSignedJWTVerifier.java new file mode 100644 index 000000000..903facebd --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CustomSignedJWTVerifier.java @@ -0,0 +1,91 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.utils; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.crypto.Ed25519Verifier; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.OctetKeyPair; +import com.nimbusds.jose.util.Base64URL; +import com.nimbusds.jwt.SignedJWT; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; +import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; +import org.eclipse.tractusx.managedidentitywallets.service.DidDocumentService; +import org.eclipse.tractusx.ssi.lib.did.resolver.DidResolver; +import org.eclipse.tractusx.ssi.lib.exception.proof.UnsupportedVerificationMethodException; +import org.eclipse.tractusx.ssi.lib.model.MultibaseString; +import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; +import org.eclipse.tractusx.ssi.lib.model.did.Ed25519VerificationMethod; +import org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod; +import org.eclipse.tractusx.ssi.lib.model.did.VerificationMethod; +import org.springframework.stereotype.Service; + +import java.util.Map; + +@Service +@RequiredArgsConstructor +@Data +public class CustomSignedJWTVerifier { + private DidResolver didResolver; + private final DidDocumentService didDocumentService; + public static final String KID = "kid"; + + @SneakyThrows({UnsupportedVerificationMethodException.class}) + public boolean verify(String did, SignedJWT jwt) throws JOSEException { + VerificationMethod verificationMethod = checkVerificationMethod(did, jwt); + if (JWKVerificationMethod.isInstance(verificationMethod)) { + JWKVerificationMethod method = new JWKVerificationMethod(verificationMethod); + String kty = method.getPublicKeyJwk().getKty(); + String crv = method.getPublicKeyJwk().getCrv(); + String x = method.getPublicKeyJwk().getX(); + if (!kty.equals("OKP") || !crv.equals("Ed25519")) { + throw new UnsupportedVerificationMethodException(method, "Only kty:OKP with crv:Ed25519 is supported"); + } + + OctetKeyPair keyPair = (new OctetKeyPair.Builder(Curve.Ed25519, Base64URL.from(x))).build(); + return jwt.verify(new Ed25519Verifier(keyPair)); + + } else if (Ed25519VerificationMethod.isInstance(verificationMethod)) { + Ed25519VerificationMethod method = new Ed25519VerificationMethod(verificationMethod); + MultibaseString multibase = method.getPublicKeyBase58(); + Ed25519PublicKeyParameters publicKeyParameters = new Ed25519PublicKeyParameters(multibase.getDecoded(), 0); + OctetKeyPair keyPair = (new OctetKeyPair.Builder(Curve.Ed25519, Base64URL.encode(publicKeyParameters.getEncoded()))).build(); + return jwt.verify(new Ed25519Verifier(keyPair)); + } + return false; + } + + public VerificationMethod checkVerificationMethod(String did, SignedJWT jwt) { + Map headers = jwt.getHeader().toJSONObject(); + String kid = String.valueOf(headers.get(KID)); + DidDocument didDocument = didDocumentService.getDidDocument(did); + for (VerificationMethod method : didDocument.getVerificationMethods()) { + if (method.getId().toString().contains(kid)) { + return method; + } + } + throw new BadDataException("Verification method doesn't match 'kid' parameter"); + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/EncryptionUtils.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/EncryptionUtils.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/EncryptionUtils.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/EncryptionUtils.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/StringToCredentialConverter.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/StringToCredentialConverter.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/StringToCredentialConverter.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/StringToCredentialConverter.java diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/StringToDidDocumentConverter.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/StringToDidDocumentConverter.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/StringToDidDocumentConverter.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/StringToDidDocumentConverter.java diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/TokenParsingUtils.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/TokenParsingUtils.java new file mode 100644 index 000000000..e5101f118 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/TokenParsingUtils.java @@ -0,0 +1,112 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.utils; + +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import lombok.experimental.UtilityClass; +import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; + +import java.text.ParseException; +import java.util.Optional; + +import static org.springframework.security.oauth2.core.oidc.IdTokenClaimNames.NONCE; +import static org.springframework.security.oauth2.jwt.JwtClaimNames.JTI; +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.ACCESS_TOKEN; +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.SCOPE; + +@UtilityClass +public class TokenParsingUtils { + + public static final String PARSING_TOKEN_ERROR = "Could not parse jwt token"; + public static final String BEARER_ACCESS_SCOPE = "bearer_access_scope"; + public static final String ACCESS_TOKEN_ERROR = "Access token not present"; + + public static JWTClaimsSet getClaimsSet(SignedJWT tokenParsed) { + try { + return tokenParsed.getJWTClaimsSet(); + } catch (ParseException e) { + throw new BadDataException(PARSING_TOKEN_ERROR, e); + } + } + + public static SignedJWT parseToken(String token) { + try { + return SignedJWT.parse(token); + } catch (ParseException e) { + throw new BadDataException(PARSING_TOKEN_ERROR, e); + } + } + + public static String getStringClaim(JWTClaimsSet claimsSet, String name) { + try { + return claimsSet.getStringClaim(name); + } catch (ParseException e) { + throw new BadDataException(PARSING_TOKEN_ERROR, e); + } + } + + public static Optional getAccessToken(JWTClaimsSet claims) { + try { + String accessTokenValue = claims.getStringClaim(ACCESS_TOKEN); + return accessTokenValue == null ? Optional.empty() : Optional.of(accessTokenValue); + } catch (ParseException e) { + throw new BadDataException(PARSING_TOKEN_ERROR, e); + } + } + + public static SignedJWT getAccessToken(String outerToken) { + SignedJWT jwtOuter = parseToken(outerToken); + JWTClaimsSet claimsSet = getClaimsSet(jwtOuter); + Optional accessToken = getAccessToken(claimsSet); + return accessToken.map(TokenParsingUtils::parseToken).orElseThrow(() -> new BadDataException(ACCESS_TOKEN_ERROR)); + } + + public static String getScope(JWTClaimsSet jwtClaimsSet) { + try { + String scopes = jwtClaimsSet.getStringClaim(SCOPE); + if (scopes == null) { + scopes = jwtClaimsSet.getStringClaim(BEARER_ACCESS_SCOPE); + } + return scopes; + } catch (ParseException e) { + throw new BadDataException("Token does not contain scope claim"); + } + } + + public static String getJtiAccessToken(JWT accessToken) { + try { + return getStringClaim(accessToken.getJWTClaimsSet(), JTI); + } catch (ParseException e) { + throw new BadDataException(PARSING_TOKEN_ERROR, e); + } + } + + public static String getNonceAccessToken(JWT accessToken) { + try { + return accessToken.getJWTClaimsSet().getStringClaim(NONCE); + } catch (ParseException e) { + throw new BadDataException(PARSING_TOKEN_ERROR, e); + } + } +} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/TokenValidationUtils.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/TokenValidationUtils.java new file mode 100644 index 000000000..cea94cb5c --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/TokenValidationUtils.java @@ -0,0 +1,164 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.utils; + +import com.nimbusds.jwt.JWTClaimsSet; +import lombok.RequiredArgsConstructor; +import org.eclipse.tractusx.managedidentitywallets.constant.TokenValidationErrors; +import org.eclipse.tractusx.managedidentitywallets.dto.ValidationResult; +import org.eclipse.tractusx.managedidentitywallets.service.DidDocumentService; +import org.springframework.stereotype.Component; + +import java.net.URI; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +import static java.time.ZoneOffset.UTC; + +/** + * Contains methods for validating token claims. + */ +@Component +@RequiredArgsConstructor +public class TokenValidationUtils { + + private final DidDocumentService service; + + public static final String DID_FORMAT = "did:"; + private static final int IAT_LEEWAY = 5; + + public ValidationResult checkIfIssuerEqualsSubject(JWTClaimsSet claims) { + String iss = claims.getIssuer(); + String sub = claims.getSubject(); + return (iss != null && Objects.equals(iss, sub)) ? + getValidResult() : getInvalidResult(TokenValidationErrors.ISS_AND_SUB_NOT_EQUAL); + } + + public ValidationResult getValidResult() { + return ValidationResult.builder().isValid(true).build(); + } + + public ValidationResult getInvalidResult(TokenValidationErrors error) { + return ValidationResult.builder().isValid(false).errors(List.of(error)).build(); + } + + public ValidationResult checkIfSubjectValidAndEqualsDid(JWTClaimsSet claims) { + String sub = claims.getSubject(); + return checkIfSubPresent(sub) + ? checkIfDidPresent(sub) + ? getValidResult() + : getInvalidResult(TokenValidationErrors.SUB_NOT_MATCH_ANY_DID) + : getInvalidResult(TokenValidationErrors.SUB_NOT_DID); + } + + private boolean checkIfSubPresent(String sub) { + return sub != null && sub.startsWith(DID_FORMAT); + } + + private boolean checkIfDidPresent(String sub) { + URI id = service.getDidDocument(sub).getId(); + return id != null && Objects.equals(id.toString(), sub); + } + + public ValidationResult checkTokenExpiry(JWTClaimsSet claims) { + return !checkIfExpirationIsPresent(claims.getExpirationTime()) + ? getInvalidResult(TokenValidationErrors.EXP_MISSING) + : checkIfTokenIsExpired(claims.getExpirationTime()) + ? getInvalidResult(TokenValidationErrors.TOKEN_ALREADY_EXPIRED) + : checkIssAt(claims); + } + + private boolean checkIfExpirationIsPresent(Date expirationTime) { + return null != expirationTime; + } + + private boolean checkIfTokenIsExpired(Date expirationTime) { + return Instant.now().isAfter(convertDateToUtcTime(expirationTime)); + } + + private Instant convertDateToUtcTime(Date date) { + return date.toInstant().atOffset(UTC).toInstant(); + } + + private ValidationResult checkIssAt(JWTClaimsSet claimsSet) { + return !checkIfIssuedAtIsPresent(claimsSet.getIssueTime()) + ? getInvalidResult(TokenValidationErrors.IAT_MISSING) + : checkIfIssuedAtIsAfterExpires(claimsSet) + ? getInvalidResult(TokenValidationErrors.IAT_AFTER_EXPIRATION) + : checkIssuedAtIsAfterCurrentDateTime(claimsSet.getIssueTime()) + ? getInvalidResult(TokenValidationErrors.CURRENT_TIME_BEFORE_IAT) + : getValidResult(); + } + + private boolean checkIfIssuedAtIsPresent(Date issueTime) { + return null != issueTime; + } + + private boolean checkIfIssuedAtIsAfterExpires(JWTClaimsSet claims) { + Date expires = claims.getExpirationTime(); + Date issuedAt = claims.getIssueTime(); + Instant issuedAtInst = convertDateToUtcTime(issuedAt); + return issuedAtInst.isAfter(convertDateToUtcTime(expires)); + } + + private boolean checkIssuedAtIsAfterCurrentDateTime(Date issuedAt) { + Instant issuedAtInst = convertDateToUtcTime(issuedAt); + Instant now = Instant.now(); + return now.plusSeconds(IAT_LEEWAY).isBefore(issuedAtInst); + } + + public ValidationResult checkIfAudienceClaimsAreEqual(List audienceSI, List audienceAccess) { + return checkIfAudsAreMissing(audienceSI, audienceAccess) + ? getInvalidResult(TokenValidationErrors.AUD_MISSING) + : checkAudEquality(audienceSI, audienceAccess) + ? checkAudFormat(audienceAccess) + ? getValidResult() + : getInvalidResult(TokenValidationErrors.AUD_NOT_DID) + : getInvalidResult(TokenValidationErrors.AUD_CLAIMS_NOT_EQUAL); + } + + private boolean checkAudFormat(List audienceAccess) { + return audienceAccess.get(0).startsWith(DID_FORMAT); + } + + private boolean checkAudEquality(List audienceSI, List audienceAccess) { + return audienceSI.contains(audienceAccess.get(0)); + } + + private boolean checkIfAudsAreMissing(List audienceSI, List audienceAccess) { + return audienceSI.isEmpty() || audienceAccess.isEmpty(); + } + + public ValidationResult checkIfNonceClaimsAreEqual(String nonceSI, String nonceAccess) { + return checkIfNoncesAreMissing(nonceSI, nonceAccess) + ? getInvalidResult(TokenValidationErrors.NONCE_MISSING) + : nonceSI.equals(nonceAccess) + ? getValidResult() + : getInvalidResult(TokenValidationErrors.NONCE_CLAIMS_NOT_EQUAL); + } + + private boolean checkIfNoncesAreMissing(String nonceSI, String nonceAccess) { + return nonceSI == null || nonceAccess == null; + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/Validate.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/Validate.java similarity index 100% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/Validate.java rename to miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/Validate.java diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/validator/SecureTokenRequestValidator.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/validator/SecureTokenRequestValidator.java new file mode 100644 index 000000000..1f6dd6e48 --- /dev/null +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/validator/SecureTokenRequestValidator.java @@ -0,0 +1,70 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.validator; + +import org.eclipse.tractusx.managedidentitywallets.dto.SecureTokenRequest; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.validation.BeanPropertyBindingResult; +import org.springframework.validation.Errors; +import org.springframework.validation.ValidationUtils; +import org.springframework.validation.Validator; + +import static org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils.getSecureTokenRequest; + +public class SecureTokenRequestValidator implements Validator { + + public static final String LINKED_MULTI_VALUE_MAP_CLASS_NAME = "org.springframework.util.LinkedMultiValueMap"; + + @Override + public boolean supports(Class clazz) { + return SecureTokenRequest.class.equals(clazz) || clazz.getCanonicalName().equals(LINKED_MULTI_VALUE_MAP_CLASS_NAME); + } + + @Override + public void validate(Object target, Errors errors) { + LinkedMultiValueMap requestParams = null; + if (target instanceof LinkedMultiValueMap) { + requestParams = (LinkedMultiValueMap) target; + } + SecureTokenRequest secureTokenRequest = requestParams != null ? getSecureTokenRequest(requestParams) : (SecureTokenRequest) target; + Errors errorsHandled = new BeanPropertyBindingResult(secureTokenRequest, errors.getObjectName()); + ValidationUtils.rejectIfEmptyOrWhitespace(errorsHandled, "audience", "audience.empty", "The 'audience' cannot be empty or missing."); + ValidationUtils.rejectIfEmptyOrWhitespace(errorsHandled, "clientId", "client_id.empty", "The 'client_id' cannot be empty or missing."); + ValidationUtils.rejectIfEmptyOrWhitespace(errorsHandled, "clientSecret", "client_secret.empty", "The 'client_secret' cannot be empty or missing."); + ValidationUtils.rejectIfEmptyOrWhitespace(errorsHandled, "grantType", "grant_type.empty", "The 'grant_type' cannot be empty or missing."); + + if (secureTokenRequest.getAccessToken() != null && secureTokenRequest.getBearerAccessScope() != null) { + errorsHandled.rejectValue("accessToken", "access_token.incompatible", "The 'access_token' and the 'bearer_access_token' cannot be set together."); + errorsHandled.rejectValue("bearerAccessScope", "bearer_access_scope.incompatible", "The 'access_token' and the 'bearer_access_token' cannot be set together."); + } + if (secureTokenRequest.getAccessToken() == null && secureTokenRequest.getBearerAccessScope() == null) { + errorsHandled.rejectValue("accessToken", "access_token.incompatible", "Both the 'access_token' and the 'bearer_access_scope' are missing. At least one must be set."); + errorsHandled.rejectValue("bearerAccessScope", "bearer_access_scope.incompatible", "Both the 'access_token' and the 'bearer_access_scope' are missing. At least one must be set."); + } + if (secureTokenRequest.getAccessToken() != null) { + ValidationUtils.rejectIfEmptyOrWhitespace(errorsHandled, "accessToken", "access_token.empty", "The 'access_token' cannot be empty or missing."); + } + if (secureTokenRequest.getBearerAccessScope() != null) { + ValidationUtils.rejectIfEmptyOrWhitespace(errorsHandled, "bearerAccessScope", "bearer_access_scope.empty", "The 'bearer_access_scope' cannot be empty or missing."); + } + } +} diff --git a/src/main/resources/application.yaml b/miw/src/main/resources/application.yaml similarity index 70% rename from src/main/resources/application.yaml rename to miw/src/main/resources/application.yaml index 269aab7ba..aa638730d 100644 --- a/src/main/resources/application.yaml +++ b/miw/src/main/resources/application.yaml @@ -1,3 +1,22 @@ +################################################################################ +# Copyright (c) 2021,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 +################################################################################ + server: port: ${APPLICATION_PORT:8087} shutdown: graceful @@ -75,12 +94,11 @@ miw: authorityWalletBpn: ${AUTHORITY_WALLET_BPN:BPNL000000000000} authorityWalletName: ${AUTHORITY_WALLET_NAME:Catena-X} authorityWalletDid: ${AUTHORITY_WALLET_DID:did:web:localhost:BPNL000000000000} + authoritySigningServiceType: ${AUTHORITY_SIGNING_SERVICE_TYPE:LOCAL} + localSigningKeyStorageType: ${LOCAL_SIGNING_KEY_STORAGE_TYPE:DB} vcContexts: ${VC_SCHEMA_LINK:https://www.w3.org/2018/credentials/v1, https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json} - summaryVcContexts: ${SUMMARY_VC_SCHEMA_LINK:https://www.w3.org/2018/credentials/v1, https://catenax-ng.github.io/product-core-schemas/SummaryVC.json} vcExpiryDate: ${VC_EXPIRY_DATE:01-10-2023} #dd-MM-yyyy ie. 01-01-2025 expiry date will be 2024-12-31T18:30:00Z in VC - supportedFrameworkVCTypes: ${SUPPORTED_FRAMEWORK_VC_TYPES:PcfCredential, SustainabilityCredential, QualityCredential, TraceabilityCredential, BehaviorTwinCredential, ResiliencyCredential} enforceHttps: ${ENFORCE_HTTPS_IN_DID_RESOLUTION:true} - contractTemplatesUrl: ${CONTRACT_TEMPLATES_URL:https://public.catena-x.org/contracts/} didDocumentContextUrls: ${DID_DOCUMENT_CONTEXT_URL:https://www.w3.org/ns/did/v1,https://w3c.github.io/vc-jws-2020/contexts/v1} security: enabled: true @@ -89,4 +107,10 @@ miw: auth-server-url: ${AUTH_SERVER_URL:http://localhost:8081} auth-url: ${miw.security.auth-server-url}/realms/${miw.security.realm}/protocol/openid-connect/auth token-url: ${miw.security.auth-server-url}/realms/${miw.security.realm}/protocol/openid-connect/token - refresh-token-url: ${miw.security.token-url} \ No newline at end of file + refresh-token-url: ${miw.security.token-url} + + +sts: + token-duration: 60000 + token-service: org.eclipse.tractusx.managedidentitywallets.service. + diff --git a/src/main/resources/db/changelog/changelog-master.xml b/miw/src/main/resources/db/changelog/changelog-master.xml similarity index 91% rename from src/main/resources/db/changelog/changelog-master.xml rename to miw/src/main/resources/db/changelog/changelog-master.xml index b115eddcc..6219c1cd1 100644 --- a/src/main/resources/db/changelog/changelog-master.xml +++ b/miw/src/main/resources/db/changelog/changelog-master.xml @@ -25,4 +25,6 @@ xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd"> + + diff --git a/miw/src/main/resources/db/changelog/changes/create_jti_table.sql b/miw/src/main/resources/db/changelog/changes/create_jti_table.sql new file mode 100644 index 000000000..7751fc797 --- /dev/null +++ b/miw/src/main/resources/db/changelog/changes/create_jti_table.sql @@ -0,0 +1,36 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +-- liquibase formatted sql +-- changeset andreibogus:create-jti-table + +CREATE TABLE IF NOT EXISTS public.jti +( + id bigserial NOT NULL, + jti uuid NOT NULL, + is_used_status bool NOT NULL, + created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at timestamp(6) NULL, + modified_from varchar(255) NULL, + CONSTRAINT jti_pkey PRIMARY KEY (id), + CONSTRAINT uk_jti UNIQUE (jti) +); +COMMENT ON TABLE public.jti IS 'This table will store jti field statuses'; diff --git a/src/main/resources/db/changelog/changes/init.sql b/miw/src/main/resources/db/changelog/changes/init.sql similarity index 95% rename from src/main/resources/db/changelog/changes/init.sql rename to miw/src/main/resources/db/changelog/changes/init.sql index d6b396fc6..83823c4e4 100644 --- a/src/main/resources/db/changelog/changes/init.sql +++ b/miw/src/main/resources/db/changelog/changes/init.sql @@ -77,4 +77,9 @@ COMMENT ON TABLE public.holders_credential IS 'This table will store holders cre COMMENT ON COLUMN public.holders_credential.is_stored IS 'true is VC is stored using store VC api(Not issued by MIW)'; --changeset nitin:2 -ALTER TABLE public.wallet_key ADD key_id varchar(255) NULL; \ No newline at end of file +ALTER TABLE public.wallet_key ADD key_id varchar(255) NULL; + +--changeset pmanaras:3 +ALTER TABLE public.wallet ADD signing_service_type VARCHAR(255) NOT NULL DEFAULT 'LOCAL'; + + diff --git a/miw/src/main/resources/db/changelog/changes/update_wallet_table.sql b/miw/src/main/resources/db/changelog/changes/update_wallet_table.sql new file mode 100644 index 000000000..e9e0af615 --- /dev/null +++ b/miw/src/main/resources/db/changelog/changes/update_wallet_table.sql @@ -0,0 +1,25 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +-- liquibase formatted sql +-- changeset andreibogus: add column algorithm to wallet_key table + +ALTER TABLE public.wallet_key ADD algorithm varchar(255) NOT NULL DEFAULT 'ED25519'; diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/command/GetCredentialsCommandTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/command/GetCredentialsCommandTest.java new file mode 100644 index 000000000..bc0c3ec4a --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/command/GetCredentialsCommandTest.java @@ -0,0 +1,84 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.command; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +class GetCredentialsCommandTest { + + @Test + void testGettersAndSetters() { + // Test data + String credentialId = "cred123"; + String identifier = "user123"; + List type = new ArrayList<>(); + String sortColumn = "name"; + String sortType = "asc"; + int pageNumber = 1; + int size = 10; + boolean asJwt = true; + String callerBPN = "callerBPN123"; + + // Create a GetCredentialsCommand object using the builder + GetCredentialsCommand command = GetCredentialsCommand.builder() + .credentialId(credentialId) + .identifier(identifier) + .type(type) + .sortColumn(sortColumn) + .sortType(sortType) + .pageNumber(pageNumber) + .size(size) + .asJwt(asJwt) + .callerBPN(callerBPN) + .build(); + + // Test getter and setter methods + assertEquals(credentialId, command.getCredentialId()); + assertEquals(identifier, command.getIdentifier()); + assertEquals(type, command.getType()); + assertEquals(sortColumn, command.getSortColumn()); + assertEquals(sortType, command.getSortType()); + assertEquals(pageNumber, command.getPageNumber()); + assertEquals(size, command.getSize()); + assertEquals(asJwt, command.isAsJwt()); + assertEquals(callerBPN, command.getCallerBPN()); + + // Modify some fields using setter methods + String newCredentialId = "newCred123"; + String updatedSortColumn = "updatedName"; + int updatedPageNumber = 2; + + command.setCredentialId(newCredentialId); + command.setSortColumn(updatedSortColumn); + command.setPageNumber(updatedPageNumber); + + // Test modified values + assertEquals(newCredentialId, command.getCredentialId()); + assertEquals(updatedSortColumn, command.getSortColumn()); + assertEquals(updatedPageNumber, command.getPageNumber()); + } +} diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/config/TestConfig.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/config/TestConfig.java new file mode 100644 index 000000000..a41c0ddee --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/config/TestConfig.java @@ -0,0 +1,36 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.config; + +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Configuration +@Profile("test") +public class TestConfig { + @Bean + public TestRestTemplate testRestTemplate() { + return new TestRestTemplate(); + } +} diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/config/TestContextInitializer.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/config/TestContextInitializer.java similarity index 87% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/config/TestContextInitializer.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/config/TestContextInitializer.java index b9a492b90..6c07d12f4 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/config/TestContextInitializer.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/config/TestContextInitializer.java @@ -26,6 +26,7 @@ import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; +import org.testcontainers.containers.PostgreSQLContainer; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; @@ -36,6 +37,10 @@ public class TestContextInitializer implements ApplicationContextInitializer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer<>("postgres:14.5") + .withDatabaseName("integration-tests-db") + .withUsername("sa") + .withPassword("sa"); @SneakyThrows @Override @@ -55,11 +60,11 @@ public void initialize(ConfigurableApplicationContext applicationContext) { "miw.authorityWalletBpn: BPNL000000000000", "miw.authorityWalletName: Test-X", "miw.authorityWalletDid: did:web:localhost%3A${server.port}:BPNL000000000000", - "spring.datasource.url=jdbc:h2:mem:testdb", - "spring.datasource.driverClassName=org.h2.Driver", - "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", + "spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver", + "spring.datasource.url=jdbc:tc:postgresql:14.5:///integration-tests-db", + "spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect", "spring.datasource.username=sa", - "spring.datasource.password=password", + "spring.datasource.password=sa", "miw.security.auth-server-url=" + authServerUrl, "miw.security.clientId=miw_private_client ", "miw.security.auth-url=${miw.security.auth-server-url}realms/${miw.security.realm}/protocol/openid-connect/auth", diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationIatpFilterTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationIatpFilterTest.java new file mode 100644 index 000000000..ed2555bdd --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationIatpFilterTest.java @@ -0,0 +1,108 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.controller; + +import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; +import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; +import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; +import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.dto.ValidationResult; +import org.eclipse.tractusx.managedidentitywallets.service.STSTokenValidationService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; + +import java.util.List; +import java.util.Map; + +import static org.eclipse.tractusx.managedidentitywallets.constant.TokenValidationErrors.NONCE_MISSING; +import static org.eclipse.tractusx.managedidentitywallets.constant.TokenValidationErrors.TOKEN_ALREADY_EXPIRED; + +@DirtiesContext +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { ManagedIdentityWalletsApplication.class }) +@ContextConfiguration(initializers = { TestContextInitializer.class }) +class PresentationIatpFilterTest { + + private static final String TOKEN = "eyJraWQiOiI1OGNiNGIzMi1jMmU0LTQ2ZjAtYTNhZC0zMjg2ZTM0NzY1ZWQiLCJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9.eyJhY2Nlc3NfdG9rZW4iOiJleUpyYVdRaU9pSTFPR05pTkdJek1pMWpNbVUwTFRRMlpqQXRZVE5oWkMwek1qZzJaVE0wTnpZMVpXUWlMQ0owZVhBaU9pSktWMVFpTENKaGJHY2lPaUpGWkVSVFFTSjkuZXlKaGRXUWlPaUprYVdRNmQyVmlPbXh2WTJGc2FHOXpkRHBDVUU1TU1EQXdNREF3TURBd01EQXdJaXdpYzNWaUlqb2laR2xrT25kbFlqcHNiMk5oYkdodmMzUTZRbEJPVERBd01EQXdNREF3TURBd01DSXNJbk5qYjNCbElqb2liM0puTG1WamJHbHdjMlV1ZEhKaFkzUjFjM2d1ZG1NdWRIbHdaVHBXWVd4cFpFTnlaV1JsYm5ScFlXeFVlWEJsT25KbFlXUWlMQ0pwYzNNaU9pSmthV1E2ZDJWaU9teHZZMkZzYUc5emREcENVRTVNTURBd01EQXdNREF3TURBd0lpd2laWGh3SWpveE56QTNNak13TkRVNUxDSnBZWFFpT2pFM01EY3lNekF6T1Rrc0ltcDBhU0k2SW1FNU16YzJNakk0TFRreVpUSXROR1pqT0MwNVpUZ3pMVGMxWlRneFpEVm1OR1V3TXlKOS40WHBKVTl0VlQ5QU4zT2JYdHZOX2hGcTNqY2Z0QjMwR2tJOXJHUWhBbFA2MnB5eFNZeDZTRENEVkJTbmpQTUE0MVB3cXIzaC1OVVVtcmFVU2dvUXNBZyIsImF1ZCI6ImRpZDp3ZWI6bG9jYWxob3N0OkJQTkwwMDAwMDAwMDAwMDAiLCJzdWIiOiJkaWQ6d2ViOmxvY2FsaG9zdDpCUE5MMDAwMDAwMDAwMDAwIiwiaXNzIjoiZGlkOndlYjpsb2NhbGhvc3Q6QlBOTDAwMDAwMDAwMDAwMCIsImV4cCI6MTcwNzIzMDQ1OSwiaWF0IjoxNzA3MjMwMzk5LCJqdGkiOiJhYWQ4OTUzMS04YjE4LTQzN2EtOGZmNS1lZDc2OThjMmFlYTAifQ.HXPtWRDh6rIlYdhQq40zLmeLhWgQnj_EwHYZ014AuTJhSgEmTep756nNyTcMXqa-cloNxoKrA323VLcaOAezBQ"; + + @MockBean + private STSTokenValidationService validationService; + + @Autowired + private TestRestTemplate testTemplate; + + @Test + void createPresentationFailure401Test() { + HttpHeaders headers = new HttpHeaders(); + headers.put(HttpHeaders.CONTENT_TYPE, List.of(MediaType.APPLICATION_JSON_VALUE)); + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity> response = testTemplate.exchange( + RestURI.API_PRESENTATIONS_IATP, + HttpMethod.GET, + entity, + new ParameterizedTypeReference<>() { + } + ); + + Assertions.assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); + } + + @Test + void createPresentationFailure401WithErrorsTest() { + HttpHeaders headers = new HttpHeaders(); + headers.put(HttpHeaders.CONTENT_TYPE, List.of(MediaType.APPLICATION_JSON_VALUE)); + headers.put(HttpHeaders.AUTHORIZATION, List.of(TOKEN)); + HttpEntity entity = new HttpEntity<>(headers); + ValidationResult validationResult = ValidationResult.builder() + .isValid(false) + .errors(List.of(TOKEN_ALREADY_EXPIRED, NONCE_MISSING)) + .build(); + + Mockito.when(validationService.validateToken(TOKEN)).thenReturn(validationResult); + + ResponseEntity response = testTemplate.exchange( + RestURI.API_PRESENTATIONS_IATP, + HttpMethod.GET, + entity, + new ParameterizedTypeReference<>() { + } + ); + + String expectedBody = TOKEN_ALREADY_EXPIRED.name() + StringPool.COMA_SEPARATOR + NONCE_MISSING.name(); + + Assertions.assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); + Assertions.assertEquals(expectedBody, response.getBody()); + } +} diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/controller/SecureTokenControllerTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/controller/SecureTokenControllerTest.java new file mode 100644 index 000000000..58dc11653 --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/controller/SecureTokenControllerTest.java @@ -0,0 +1,136 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.controller; + +import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; +import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; +import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; +import org.eclipse.tractusx.managedidentitywallets.utils.AuthenticationUtils; +import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; +import org.eclipse.tractusx.ssi.lib.did.web.DidWebFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.ContextConfiguration; + +import java.util.List; +import java.util.Map; + +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { ManagedIdentityWalletsApplication.class }) +@ContextConfiguration(initializers = { TestContextInitializer.class }) +class SecureTokenControllerTest { + + @Autowired + private MIWSettings miwSettings; + + @Autowired + private TestRestTemplate testTemplate; + + private String bpn; + + private String clientId; + + private String clientSecret; + + @BeforeEach + public void initWallets() { + // given + bpn = TestUtils.getRandomBpmNumber(); + String partnerBpn = TestUtils.getRandomBpmNumber(); + clientId = "main"; + clientSecret = "main"; + AuthenticationUtils.setupKeycloakClient(clientId, clientSecret, bpn); + AuthenticationUtils.setupKeycloakClient("partner", "partner", partnerBpn); + String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); + String didPartner = DidWebFactory.fromHostnameAndPath(miwSettings.host(), partnerBpn).toString(); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + TestUtils.createWallet(bpn, did, testTemplate, miwSettings.authorityWalletBpn(), defaultLocation); + String defaultLocationPartner = miwSettings.host() + COLON_SEPARATOR + partnerBpn; + TestUtils.createWallet(partnerBpn, didPartner, testTemplate, miwSettings.authorityWalletBpn(), defaultLocationPartner); + } + + @Test + void tokenJSON() { + // when + String body = """ + { + "audience": "%s", + "client_id": "%s", + "client_secret": "%s", + "grant_type": "client_credentials", + "bearer_access_scope": "org.eclipse.tractusx.vc.type:BpnCredential:read" + } + """; + String requestBody = String.format(body, bpn, clientId, clientSecret); + // then + HttpHeaders headers = new HttpHeaders(); + headers.put(HttpHeaders.CONTENT_TYPE, List.of(MediaType.APPLICATION_JSON_VALUE)); + HttpEntity entity = new HttpEntity<>(requestBody, headers); + ResponseEntity> response = testTemplate.exchange( + "/api/token", + HttpMethod.POST, + entity, + new ParameterizedTypeReference<>() { + } + ); + Assertions.assertEquals(response.getStatusCode(), HttpStatus.OK); + Assertions.assertEquals(response.getHeaders().getContentType(), MediaType.APPLICATION_JSON); + Assertions.assertNotNull(response.getBody()); + Assertions.assertNotNull(response.getBody().getOrDefault("access_token", null)); + Assertions.assertNotNull(response.getBody().getOrDefault("expiresAt", null)); + } + + @Test + void tokenFormUrlencoded() { + // when + String body = "audience=%s&client_id=%s&client_secret=%s&grant_type=client_credentials&bearer_access_scope=org.eclipse.tractusx.vc.type:BpnCredential:read"; + String requestBody = String.format(body, bpn, clientId, clientSecret); + // then + HttpHeaders headers = new HttpHeaders(); + headers.put(HttpHeaders.CONTENT_TYPE, List.of(MediaType.APPLICATION_FORM_URLENCODED_VALUE)); + HttpEntity entity = new HttpEntity<>(requestBody, headers); + ResponseEntity> response = testTemplate.exchange( + "/api/token", + HttpMethod.POST, + entity, + new ParameterizedTypeReference<>() { + } + ); + Assertions.assertEquals(response.getStatusCode(), HttpStatus.OK); + Assertions.assertEquals(response.getHeaders().getContentType(), MediaType.APPLICATION_JSON); + Assertions.assertNotNull(response.getBody()); + Assertions.assertNotNull(response.getBody().getOrDefault("access_token", null)); + Assertions.assertNotNull(response.getBody().getOrDefault("expiresAt", null)); + } +} diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/did/DidDocumentsTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/did/DidDocumentsTest.java similarity index 93% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/did/DidDocumentsTest.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/did/DidDocumentsTest.java index 72ccc1e19..4e902df7e 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/did/DidDocumentsTest.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/did/DidDocumentsTest.java @@ -38,6 +38,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; + @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {ManagedIdentityWalletsApplication.class}) @ContextConfiguration(initializers = {TestContextInitializer.class}) class DidDocumentsTest { @@ -86,8 +88,9 @@ void getDidResolveWithBpn200() { private Wallet createWallet(String bpn) { CreateWalletRequest createWalletRequest = new CreateWalletRequest(); - createWalletRequest.setBpn(bpn); - createWalletRequest.setName("wallet_" + bpn); + createWalletRequest.setBusinessPartnerNumber(bpn); + createWalletRequest.setCompanyName("wallet_" + bpn); + createWalletRequest.setDidUrl(miwSettings.host() + COLON_SEPARATOR + bpn); return walletService.createWallet(createWalletRequest, miwSettings.authorityWalletBpn()); } } diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfigTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfigTest.java new file mode 100644 index 000000000..07ccba5ad --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfigTest.java @@ -0,0 +1,157 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.domain; + +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.ssi.lib.model.did.Did; +import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialStatus; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; + +import java.net.URI; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class CredentialCreationConfigTest { + + @Test + void shouldBuildWithRequiredAttributes() { + CredentialCreationConfig build = assertDoesNotThrow(() -> CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .expiryDate(new Date()) + .subject(mockCredentialSubject()) + .types(Collections.emptyList()) + .issuerDoc(Mockito.mock(DidDocument.class)) + .holderDid(Mockito.mock(Did.class).toString()) + .contexts(Collections.emptyList()) + .verifiableCredentialStatus(Mockito.mock(VerifiableCredentialStatus.class)) + .vcId(URI.create("yada://test.com")) + .keyName("keyName") + .algorithm(SupportedAlgorithms.ED25519) + .build()); + assertNotNull(build); + assertNotNull(build.getExpiryDate()); + assertNotNull(build.getSubject()); + assertNotNull(build.getTypes()); + assertNotNull(build.getIssuerDoc()); + assertNotNull(build.getHolderDid()); + assertNotNull(build.getContexts()); + assertNotNull(build.getVerifiableCredentialStatus()); + assertNotNull(build.getVcId()); + assertNotNull(build.getKeyName()); + assertNotNull(build.getEncoding()); + assertFalse(build.isSelfIssued()); + } + + @Test + void shouldBuildWhenVcIdIsString() { + + CredentialCreationConfig build = assertDoesNotThrow(() -> CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .expiryDate(new Date()) + .subject(mockCredentialSubject()) + .types(Collections.emptyList()) + .issuerDoc(Mockito.mock(DidDocument.class)) + .holderDid(Mockito.mock(Did.class).toString()) + .contexts(Collections.emptyList()) + .verifiableCredentialStatus(Mockito.mock(VerifiableCredentialStatus.class)) + .keyName("keyName") + .algorithm(SupportedAlgorithms.ED25519) + .build()); + } + + @ParameterizedTest + @MethodSource("testConfigs") + void shouldThrowIfRequiredAttributesMissing(TestConfig conf) { + assertThrows(NullPointerException.class, () -> CredentialCreationConfig + .builder().expiryDate(conf.expiryDate) + .subject(conf.subject) + .types(conf.types) + .issuerDoc(conf.issuerDoc) + .holderDid(conf.holderDid) + .contexts(conf.contexts) + .encoding(conf.encoding) + .keyName(conf.keyName)); + + + } + + @Test + void shouldThrowWhenSettingIllegalVcId() { + CredentialCreationConfig.CredentialCreationConfigBuilder builder = CredentialCreationConfig.builder(); + assertThrows(IllegalArgumentException.class, () -> builder.vcId(42)); + } + + @Test + void shouldNotThrowWhenVcIdValid(){ + CredentialCreationConfig.CredentialCreationConfigBuilder builder = CredentialCreationConfig.builder(); + assertDoesNotThrow(() -> builder.vcId("https://test.com")); + assertDoesNotThrow(() -> builder.vcId(URI.create("https://test.com"))); + } + + private static Stream testConfigs() { + return Stream.of( + Arguments.of(new TestConfig(null, null, null, null, null, null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), null, null, null, null, null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), Collections.emptyList(), null, null, null, null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), Collections.emptyList(), new byte[]{}, null, null, null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), Collections.emptyList(), new byte[]{}, Mockito.mock(DidDocument.class), null, null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), Collections.emptyList(), new byte[]{}, Mockito.mock(DidDocument.class), Mockito.mock(Did.class).toString(), null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), Collections.emptyList(), new byte[]{}, Mockito.mock(DidDocument.class), Mockito.mock(Did.class).toString(), Collections.emptyList(), null, null, null)) + ); + } + + private record TestConfig( + VerifiableCredentialSubject subject, + List types, + byte[] privateKey, + DidDocument issuerDoc, + String holderDid, + List contexts, + Date expiryDate, + + String keyName, + + VerifiableEncoding encoding + + ) { + } + + + private static VerifiableCredentialSubject mockCredentialSubject() { + return new VerifiableCredentialSubject(Map.of("id", "42")); + } + +} diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/migration/SetSigningServiceTypeToExistingWalletsTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/migration/SetSigningServiceTypeToExistingWalletsTest.java new file mode 100644 index 000000000..ec7fed508 --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/migration/SetSigningServiceTypeToExistingWalletsTest.java @@ -0,0 +1,132 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.migration; + +import liquibase.Liquibase; +import liquibase.database.Database; +import liquibase.database.DatabaseFactory; +import liquibase.database.jvm.JdbcConnection; +import liquibase.resource.ClassLoaderResourceAccessor; +import lombok.SneakyThrows; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class SetSigningServiceTypeToExistingWalletsTest { + public static final int EXPECTED_WALLET_COUNT = 5; + private static Database database; + + @BeforeAll + @SneakyThrows + public static void beforeAll() { + Connection conn = DriverManager.getConnection( + "jdbc:h2:mem:framework_test;INIT=CREATE SCHEMA IF NOT EXISTS migration", + "admin", + "password"); + + database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(conn)); + } + + @SneakyThrows + @Test + void migrateWallets() { + var connection = (JdbcConnection) database.getConnection(); + + // 1. apply changelog without signing service-type + Liquibase liquibase = new Liquibase("db/signing-service-migration-test/without-changes.xml", new ClassLoaderResourceAccessor(), database); + liquibase.update((String) null); + + // assert that column for signing_service_type does not exist + + List columns = getColumnList(connection); + assertFalse(columns.contains("signing_service_type".toUpperCase())); + + // insert Wallets + insertWallets(connection); + + // assert that there are 5 Wallets created + String sqlCount = "SELECT count(*) as count FROM public.wallet"; + Statement st = connection.createStatement(); + ResultSet count = st.executeQuery(sqlCount); + assertTrue(count.next()); + assertEquals(EXPECTED_WALLET_COUNT, count.getInt("count")); + + // run migration + Liquibase migrate = new Liquibase("db/signing-service-migration-test/signing-service-changelog.xml", new ClassLoaderResourceAccessor(), database); + migrate.update((String) null); + + // assert that column was created + List newColumns = getColumnList(connection); + assertTrue(newColumns.contains("signing_service_type".toUpperCase())); + + // assert that every wallet now has signing_service_type = LOCAL by default + String walletSql = "SELECT * FROM public.wallet"; + Statement walletStatement = connection.createStatement(); + ResultSet walletResult = walletStatement.executeQuery(walletSql); + while(walletResult.next()){ + assertEquals("LOCAL", walletResult.getString("signing_service_type".toUpperCase())); + } + } + + @SneakyThrows + private List getColumnList(JdbcConnection connection){ + String selectColumns = "SHOW COLUMNS FROM public.wallet"; + // COLUMN_NAME | TYPE | IS_NULLABLE | KEY | DEFAULT + ResultSet resultSet = connection.createStatement().executeQuery(selectColumns); + List columns = new ArrayList<>(); + while (resultSet.next()) { + columns.add(resultSet.getString("COLUMN_NAME")); + } + return columns; + } + + @SneakyThrows + private void insertWallets(JdbcConnection connection) { + String insert = "INSERT INTO wallet(id,name,did,bpn,algorithm,did_document,created_at) VALUES(?,?,?,?,?,?,?)"; + PreparedStatement pst = connection.prepareStatement(insert); + + for (int i = 1; i <= EXPECTED_WALLET_COUNT; i++) { + pst.setInt(1, i); + pst.setString(2, "name"+i); + pst.setString(3, "did"+i); + pst.setString(4, "bpn"+i); + pst.setString(5, "algorithm"+i); + pst.setString(6, "document"+i); + pst.setTimestamp(7, Timestamp.from(Instant.now())); + pst.execute(); + } + } + +} diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialServiceTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialServiceTest.java new file mode 100644 index 000000000..6be2a0c31 --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialServiceTest.java @@ -0,0 +1,366 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; +import com.nimbusds.jose.JWSObject; +import com.nimbusds.jwt.SignedJWT; +import com.smartsensesolutions.java.commons.specification.SpecificationUtil; +import lombok.SneakyThrows; +import org.apache.commons.lang3.time.DateUtils; +import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; +import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.IssuersCredential; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.dto.CredentialVerificationRequest; +import org.eclipse.tractusx.managedidentitywallets.dto.CredentialsResponse; +import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenService; +import org.eclipse.tractusx.managedidentitywallets.signing.LocalKeyProvider; +import org.eclipse.tractusx.managedidentitywallets.signing.LocalSigningServiceImpl; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; +import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; +import org.eclipse.tractusx.managedidentitywallets.utils.MockUtil; +import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; +import org.eclipse.tractusx.ssi.lib.crypt.KeyPair; +import org.eclipse.tractusx.ssi.lib.crypt.octet.OctetKeyPairFactory; +import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519PrivateKey; +import org.eclipse.tractusx.ssi.lib.did.resolver.DidResolver; +import org.eclipse.tractusx.ssi.lib.exception.did.DidParseException; +import org.eclipse.tractusx.ssi.lib.exception.key.InvalidPrivateKeyFormatException; +import org.eclipse.tractusx.ssi.lib.exception.key.KeyTransformationException; +import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtFactory; +import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtVerifier; +import org.eclipse.tractusx.ssi.lib.model.did.Did; +import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; +import org.eclipse.tractusx.ssi.lib.model.did.DidMethod; +import org.eclipse.tractusx.ssi.lib.model.did.DidMethodIdentifier; +import org.eclipse.tractusx.ssi.lib.model.did.DidParser; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; +import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedJwtVCFactoryImpl; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.security.oauth2.jwt.JwtException; + +import java.sql.Connection; +import java.sql.SQLException; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.sql.DataSource; + +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class IssuersCredentialServiceTest { + public static final String DID_WEB_LOCALHOST = "did:web:localhost"; + + public static final Did ISSUER = MockUtil.generateDid("caller"); + public static final String KEY_ID = "key-1"; + + private static MIWSettings miwSettings; + + private static WalletKeyService walletKeyService; + + private static WalletKeyRepository walletKeyRepository; + + private static HoldersCredentialRepository holdersCredentialRepository; + + private static CommonService commonService; + + private static IssuersCredentialRepository issuersCredentialRepository; + + private static IssuersCredentialService issuersCredentialService; + + private static SecureTokenService secureTokenService; + + private static EncryptionUtils encryptionUtils; + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeAll + public static void beforeAll() throws SQLException { + + miwSettings = Mockito.mock(MIWSettings.class); + walletKeyService = Mockito.mock(WalletKeyService.class); + holdersCredentialRepository = Mockito.mock(HoldersCredentialRepository.class); + commonService = Mockito.mock(CommonService.class); + issuersCredentialRepository = mock(IssuersCredentialRepository.class); + secureTokenService = mock(SecureTokenService.class); + walletKeyRepository = mock(WalletKeyRepository.class); + + Connection connection = mock(Connection.class); + + DataSource dataSource = mock(DataSource.class); + when(dataSource.getConnection()).thenReturn(connection); + + when(miwSettings.encryptionKey()).thenReturn("26FlcjRKOEML8YW699CXlg=="); + when(miwSettings.vcExpiryDate()).thenReturn(DateUtils.addMonths(new Date(), 10)); + encryptionUtils = new EncryptionUtils(miwSettings); + + issuersCredentialService = new IssuersCredentialService( + issuersCredentialRepository, + miwSettings, + new SpecificationUtil(), + holdersCredentialRepository, commonService, objectMapper); + } + + @BeforeEach + public void beforeEach() { + Mockito.reset( + miwSettings, + walletKeyService, + holdersCredentialRepository, + commonService, + issuersCredentialRepository); + } + + @Nested + class issueCredentialUsingBaseWallet { + + @Test + void shouldIssueCredentialAsJwt() throws InvalidPrivateKeyFormatException, + KeyTransformationException, JwtException { + Map wallets = mockBaseAndHolderWallet(); + Wallet baseWallet = (Wallet) wallets.get("base"); + String baseWalletBpn = baseWallet.getBpn(); + Wallet holderWallet = (Wallet) wallets.get("holder"); + String holderWalletBpn = holderWallet.getBpn(); + String walletKeyId = "key-1"; + + KeyPair keyPair = MockUtil.generateEDKeys(); + VerifiableCredential verifiableCredential = MockUtil.getCredentialBuilder( + List.of("TypeA,TypeB"), + List.of(MockUtil.mockCredentialSubject(), MockUtil.mockCredentialSubject2()), + Instant.now().plus(Duration.ofDays(5)), + MockUtil.generateDid("basewallet")).build(); + + MockUtil.makeCreateWorkForIssuer(issuersCredentialRepository); + when(walletKeyService.getPrivateKeyByWalletIdAsBytes(baseWallet.getId(), "ED25519")).thenReturn(keyPair.getPrivateKey() + .asByte()); + when(commonService.getWalletByIdentifier(holderWalletBpn)).thenReturn(holderWallet); + when(commonService.getWalletByIdentifier(verifiableCredential.getIssuer() + .toString())).thenReturn(baseWallet); + when(miwSettings.authorityWalletBpn()).thenReturn(baseWalletBpn); + when(holdersCredentialRepository.save(any(HoldersCredential.class))) + .thenAnswer(new Answer() { + @Override + public HoldersCredential answer(InvocationOnMock invocation) { + HoldersCredential argument = invocation.getArgument(0, HoldersCredential.class); + argument.setId(42L); + return argument; + } + }); + + WalletKey walletKey = mock(WalletKey.class); + when(walletKey.getKeyId()).thenReturn(KEY_ID); + when(baseWallet.getAlgorithm()).thenReturn("ED25519"); + when(walletKey.getId()).thenReturn(42L); + when(walletKeyService.getPrivateKeyByWalletIdAndAlgorithm(baseWallet.getId(), SupportedAlgorithms.valueOf(baseWallet.getAlgorithm()))) + .thenReturn(new X25519PrivateKey(keyPair.getPrivateKey().asStringForStoring(), true)); + when(walletKeyService.getWalletKeyIdByWalletId(baseWallet.getId(), SupportedAlgorithms.ED25519)).thenReturn(walletKeyId); + + when(walletKeyRepository.getByKeyIdAndAlgorithm(anyString(), anyString())).thenReturn(walletKey); + when(baseWallet.getSigningServiceType()).thenReturn(SigningServiceType.LOCAL); + when(walletKeyService.getPrivateKeyByKeyId(anyString(), any())).thenReturn(keyPair.getPrivateKey()); + when(walletKeyRepository.getByAlgorithmAndWallet_Bpn(anyString(), anyString())).thenReturn(walletKey); + + LocalSigningServiceImpl localSigningService = new LocalSigningServiceImpl(secureTokenService); + localSigningService.setKeyProvider(new LocalKeyProvider(walletKeyService, walletKeyRepository, encryptionUtils)); + + Map map = new HashMap<>(); + map.put(SigningServiceType.LOCAL, localSigningService); + + issuersCredentialService.setKeyService(map); + + CredentialsResponse credentialsResponse = assertDoesNotThrow( + () -> issuersCredentialService.issueCredentialUsingBaseWallet( + holderWalletBpn, + verifiableCredential, + true, + baseWalletBpn)); + + validateCredentialResponse(credentialsResponse, MockUtil.buildDidDocument(new Did(new DidMethod("web"), + new DidMethodIdentifier("basewallet"), + null), keyPair)); + } + } + + @Nested + class jwtValidationTest { + + @RegisterExtension + static WireMockExtension issuer = WireMockExtension.newInstance() + .options(wireMockConfig().dynamicPort() + // .notifier(new ConsoleNotifier(true)) + ) + .build(); + + @Test + void shouldValidateAsJWT() throws DidParseException { + Map wallets = mockBaseAndHolderWallet("localhost%3A" + issuer.getPort()); + Wallet baseWallet = (Wallet) wallets.get("base"); + String baseWalletDid = baseWallet.getDid(); + + DidDocument issuerDidDocument = MockUtil.buildDidDocument( + DidParser.parse(baseWalletDid), + (KeyPair) wallets.get("baseKeys")); + issuer.stubFor( + get("/.well-known/did.json").willReturn(ok(issuerDidDocument.toPrettyJson()))); + + Wallet holderWallet = (Wallet) wallets.get("holder"); + String holderWalletDid = holderWallet.getDid(); + + VerifiableCredential verifiableCredential = MockUtil.getCredentialBuilder( + List.of("TypeA,TypeB"), + List.of(MockUtil.mockCredentialSubject(), MockUtil.mockCredentialSubject2()), + Instant.now().plus(Duration.ofDays(5)), + MockUtil.generateDid("basewallet")).build(); + + SerializedJwtVCFactoryImpl vcFactory = new SerializedJwtVCFactoryImpl( + new SignedJwtFactory(new OctetKeyPairFactory())); + + SignedJWT vcJWT = vcFactory.createVCJwt(DidParser.parse(baseWalletDid), DidParser.parse(holderWalletDid), + verifiableCredential, + ((KeyPair) wallets.get("baseKeys")).getPrivateKey(), + "key-1"); + + String serialized = vcJWT.serialize(); + + CredentialVerificationRequest credentialVerificationRequest = new CredentialVerificationRequest(); + credentialVerificationRequest.setJwt(serialized); + + Map stringObjectMap = assertDoesNotThrow( + () -> issuersCredentialService.credentialsValidation(credentialVerificationRequest, true)); + assertTrue((Boolean) stringObjectMap.get(StringPool.VALID)); + } + } + + private Map mockBaseAndHolderWallet() { + KeyPair baseKeys = MockUtil.generateEDKeys(); + KeyPair holderKeys = MockUtil.generateEDKeys(); + String baseWalletBpn = TestUtils.getRandomBpmNumber(); + + Wallet baseWallet = MockUtil.mockWallet( + baseWalletBpn, + MockUtil.generateDid("basewallet"), + baseKeys); + String holderWalletBpn = TestUtils.getRandomBpmNumber(); + Wallet holderWallet = MockUtil.mockWallet( + holderWalletBpn, + MockUtil.generateDid("holderwallet"), + holderKeys); + + return Map.of("base", baseWallet, "holder", holderWallet, "baseKeys", baseKeys, "holderKeys", holderKeys); + } + + private Map mockBaseAndHolderWallet(String baseHost) { + KeyPair baseKeys = MockUtil.generateEDKeys(); + KeyPair holderKeys = MockUtil.generateEDKeys(); + String baseWalletBpn = TestUtils.getRandomBpmNumber(); + + Wallet baseWallet = MockUtil.mockWallet( + baseWalletBpn, + MockUtil.generateDid(baseHost), + baseKeys); + String holderWalletBpn = TestUtils.getRandomBpmNumber(); + Wallet holderWallet = MockUtil.mockWallet( + holderWalletBpn, + MockUtil.generateDid("holderwallet"), + holderKeys); + + return Map.of("base", baseWallet, "holder", holderWallet, "baseKeys", baseKeys, "holderKeys", holderKeys); + } + + private void mockCommon( + String baseWalletBpn, + String holderWalletBpn, + KeyPair keyPair, + Wallet baseWallet, + Wallet holderWallet) { + when(miwSettings.authorityWalletBpn()).thenReturn(baseWalletBpn); + when(commonService.getWalletByIdentifier(baseWalletBpn)).thenReturn(baseWallet); + when(commonService.getWalletByIdentifier(holderWalletBpn)).thenReturn(holderWallet); + when(walletKeyService.getPrivateKeyByWalletIdAsBytes(baseWallet.getId(), baseWallet.getAlgorithm())) + .thenReturn(keyPair.getPrivateKey().asByte()); + when(holdersCredentialRepository.save(any(HoldersCredential.class))) + .thenAnswer(new Answer() { + @Override + public HoldersCredential answer(InvocationOnMock invocation) { + HoldersCredential argument = invocation.getArgument(0, HoldersCredential.class); + argument.setId(42L); + return argument; + } + }); + } + + @SneakyThrows + private void validateCredentialResponse(CredentialsResponse credentialsResponse, DidDocument didDocument) { + assertTrue(credentialsResponse.containsKey("jwt")); + JWSObject parsed = JWSObject.parse((String) credentialsResponse.get("jwt")); + assertEquals("did:web:basewallet#" + KEY_ID, parsed.getHeader().getKeyID()); + assertEquals("JWT", parsed.getHeader().getType().getType()); + assertEquals("EdDSA", parsed.getHeader().getAlgorithm().getName()); + + Map payload = parsed.getPayload().toJSONObject(); + assertTrue(payload.containsKey("vc")); + + SignedJwtVerifier jwtVerifier = new SignedJwtVerifier(new DidResolver() { + @Override + public DidDocument resolve(Did did) { + return didDocument; + } + + @Override + public boolean isResolvable(Did did) { + return false; + } + }); + + SignedJWT signedJwt = SignedJWT.parse((String) credentialsResponse.get("jwt")); + assertTrue(jwtVerifier.verify(signedJwt)); + } +} diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/service/STSTokenValidationServiceTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/service/STSTokenValidationServiceTest.java new file mode 100644 index 000000000..4614ea023 --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/service/STSTokenValidationServiceTest.java @@ -0,0 +1,165 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.service; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.OctetKeyPair; +import com.nimbusds.jose.jwk.gen.OctetKeyPairGenerator; +import com.nimbusds.jwt.JWTClaimsSet; +import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; +import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; +import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; +import org.eclipse.tractusx.managedidentitywallets.constant.TokenValidationErrors; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; +import org.eclipse.tractusx.managedidentitywallets.dto.ValidationResult; +import org.eclipse.tractusx.managedidentitywallets.utils.TokenValidationUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +import java.util.Date; + +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.BPN_1; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.BPN_2; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.BPN_CREDENTIAL_READ; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.DID_BPN_1; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.DID_BPN_2; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.DID_JSON_STRING_1; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.DID_JSON_STRING_2; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.EXP_VALID_DATE; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.IAT_VALID_DATE; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.JWK_INNER; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.JWK_OUTER; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.NONCE; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestUtils.addAccessTokenToClaimsSet; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestUtils.buildClaimsSet; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestUtils.buildJWTToken; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestUtils.buildWallet; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestUtils.generateUuid; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { ManagedIdentityWalletsApplication.class }) +@ContextConfiguration(initializers = { TestContextInitializer.class }) +class STSTokenValidationServiceTest { + + private static final Date ALREADY_EXP_DATE = new Date(Long.parseLong("1707582883000")); + + @Autowired + private STSTokenValidationService stsTokenValidationService; + + @Autowired + private TokenValidationUtils tokenValidationUtils; + + @Autowired + private DidDocumentService didDocumentService; + + @Autowired + private CommonService commonService; + + @Autowired + private WalletRepository walletRepository; + + @Autowired + private MIWSettings miwSettings; + + private final Wallet wallet1 = buildWallet(BPN_1, DID_BPN_1, DID_JSON_STRING_1); + + private final Wallet wallet2 = buildWallet(BPN_2, DID_BPN_2, DID_JSON_STRING_2); + + @BeforeEach + public void initWallets() { + walletRepository.save(wallet1); + walletRepository.save(wallet2); + } + + @AfterEach + public void cleanWallets() { + walletRepository.deleteById(wallet1.getId()); + walletRepository.deleteById(wallet2.getId()); + } + + @Test + void validateTokenFailureAccessTokenMissingTest() throws JOSEException { + JWTClaimsSet outerSet = buildClaimsSet(DID_BPN_1, DID_BPN_1, DID_BPN_1, NONCE, BPN_CREDENTIAL_READ, EXP_VALID_DATE, IAT_VALID_DATE, generateUuid()); + String siToken = buildJWTToken(JWK_OUTER, outerSet); + + ValidationResult result = stsTokenValidationService.validateToken(siToken); + + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.ACCESS_TOKEN_MISSING, result.getErrors().get(0)); + } + + @Test + void validateTokenFailureWrongSignatureInnerTokenTest() throws JOSEException { + + OctetKeyPair jwkRandom = new OctetKeyPairGenerator(Curve.Ed25519) + .keyID("58cb4b32-c2e4-46f0-a3ad-3286e34765ty") + .generate(); + + JWTClaimsSet innerSet = buildClaimsSet(DID_BPN_2, DID_BPN_1, DID_BPN_1, NONCE, BPN_CREDENTIAL_READ, EXP_VALID_DATE, IAT_VALID_DATE, generateUuid()); + String accessToken = buildJWTToken(jwkRandom, innerSet); + + JWTClaimsSet outerSet = buildClaimsSet(DID_BPN_1, DID_BPN_1, DID_BPN_1, NONCE, BPN_CREDENTIAL_READ, EXP_VALID_DATE, ALREADY_EXP_DATE, generateUuid()); + JWTClaimsSet outerSetFull = addAccessTokenToClaimsSet(accessToken, outerSet); + String siToken = buildJWTToken(JWK_OUTER, outerSetFull); + + ValidationResult result = stsTokenValidationService.validateToken(siToken); + + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.SIGNATURE_NOT_VERIFIED, result.getErrors().get(0)); + } + + @Test + void validateTokenFailureExpiredTokenIssNotEqualsSubTest() throws JOSEException { + JWTClaimsSet innerSet = buildClaimsSet(DID_BPN_2, DID_BPN_1, DID_BPN_1, NONCE, BPN_CREDENTIAL_READ, EXP_VALID_DATE, IAT_VALID_DATE, generateUuid()); + String accessToken = buildJWTToken(JWK_INNER, innerSet); + + JWTClaimsSet outerSet = buildClaimsSet(DID_BPN_1, DID_BPN_2, DID_BPN_1, NONCE, BPN_CREDENTIAL_READ, ALREADY_EXP_DATE, IAT_VALID_DATE, generateUuid()); + JWTClaimsSet outerSetFull = addAccessTokenToClaimsSet(accessToken, outerSet); + String siToken = buildJWTToken(JWK_OUTER, outerSetFull); + + ValidationResult result = stsTokenValidationService.validateToken(siToken); + + Assertions.assertFalse(result.isValid()); + Assertions.assertTrue(result.getErrors().contains(TokenValidationErrors.ISS_AND_SUB_NOT_EQUAL)); + Assertions.assertTrue(result.getErrors().contains(TokenValidationErrors.TOKEN_ALREADY_EXPIRED)); + } + + @Test + void validateTokenSuccessTest() throws JOSEException { + JWTClaimsSet innerSet = buildClaimsSet(DID_BPN_2, DID_BPN_1, DID_BPN_1, NONCE, BPN_CREDENTIAL_READ, EXP_VALID_DATE, IAT_VALID_DATE, generateUuid()); + String accessToken = buildJWTToken(JWK_INNER, innerSet); + + JWTClaimsSet outerSet = buildClaimsSet(DID_BPN_1, DID_BPN_1, DID_BPN_1, NONCE, BPN_CREDENTIAL_READ, EXP_VALID_DATE, IAT_VALID_DATE, generateUuid()); + JWTClaimsSet outerSetFull = addAccessTokenToClaimsSet(accessToken, outerSet); + String siToken = buildJWTToken(JWK_OUTER, outerSetFull); + + ValidationResult result = stsTokenValidationService.validateToken(siToken); + + Assertions.assertTrue(result.isValid()); + } +} diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/AuthenticationUtils.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/AuthenticationUtils.java similarity index 71% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/AuthenticationUtils.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/AuthenticationUtils.java index dd99e720d..1f4be3d36 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/AuthenticationUtils.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/AuthenticationUtils.java @@ -28,9 +28,12 @@ import org.keycloak.admin.client.KeycloakBuilder; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.springframework.http.HttpHeaders; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; @@ -107,6 +110,45 @@ private static String getJwtToken(String username, String bpn) { return getJwtToken(username); } + public static void setupKeycloakClient(String clientId, String clientSecret, String bpn) { + Keycloak keycloakAdmin = KeycloakBuilder.builder() + .serverUrl(TestContextInitializer.getAuthServerUrl()) + .realm("master") // Use the master realm for admin operations + .clientId("admin-cli") + .username("admin") + .password("admin") + .build(); + + Map attributes = new HashMap<>(); + attributes.put("BPN", bpn); + + ClientRepresentation clientRepresentation = new ClientRepresentation(); + clientRepresentation.setEnabled(true); + clientRepresentation.setServiceAccountsEnabled(true); + clientRepresentation.setClientId(clientId); + clientRepresentation.setSecret(clientSecret); + clientRepresentation.setConsentRequired(false); + clientRepresentation.setAttributes(attributes); + + ProtocolMapperRepresentation propertyMapper = new ProtocolMapperRepresentation(); + propertyMapper.setName("BPN mapper"); + propertyMapper.setProtocol("openid-connect"); + propertyMapper.setProtocolMapper("oidc-hardcoded-claim-mapper"); + propertyMapper.setConfig(Map.of( + "claim.name", "BPN", + "user.attribute", "BPN", + "claim.value", bpn, + "id.token.claim", "true", + "access.token.claim", "true", + "jsonType.label", "String", + "userinfo.token.claim", "true" + )); + + // Set the updated list of protocol mappers back to the client representation + clientRepresentation.setProtocolMappers(List.of(propertyMapper)); + keycloakAdmin.realm(StringPool.REALM).clients().create(clientRepresentation); + } + private static String getJwtToken(String username) { Keycloak keycloakAdminClient = KeycloakBuilder.builder() diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/EncryptionTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/EncryptionTest.java similarity index 100% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/EncryptionTest.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/EncryptionTest.java diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/MockUtil.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/MockUtil.java new file mode 100644 index 000000000..652c975c8 --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/MockUtil.java @@ -0,0 +1,307 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ +package org.eclipse.tractusx.managedidentitywallets.utils; + +import com.nimbusds.jose.util.JSONObjectUtils; +import lombok.SneakyThrows; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.IssuersCredential; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; +import org.eclipse.tractusx.ssi.lib.crypt.IPublicKey; +import org.eclipse.tractusx.ssi.lib.crypt.KeyPair; +import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519Generator; +import org.eclipse.tractusx.ssi.lib.exception.key.InvalidPrivateKeyFormatException; +import org.eclipse.tractusx.ssi.lib.exception.key.KeyGenerationException; +import org.eclipse.tractusx.ssi.lib.exception.proof.UnsupportedSignatureTypeException; +import org.eclipse.tractusx.ssi.lib.model.MultibaseString; +import org.eclipse.tractusx.ssi.lib.model.base.MultibaseFactory; +import org.eclipse.tractusx.ssi.lib.model.did.Did; +import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; +import org.eclipse.tractusx.ssi.lib.model.did.DidDocumentBuilder; +import org.eclipse.tractusx.ssi.lib.model.did.DidMethod; +import org.eclipse.tractusx.ssi.lib.model.did.DidMethodIdentifier; +import org.eclipse.tractusx.ssi.lib.model.did.Ed25519VerificationMethod; +import org.eclipse.tractusx.ssi.lib.model.did.Ed25519VerificationMethodBuilder; +import org.eclipse.tractusx.ssi.lib.model.did.VerificationMethod; +import org.eclipse.tractusx.ssi.lib.model.proof.Proof; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialBuilder; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; +import org.eclipse.tractusx.ssi.lib.proof.LinkedDataProofGenerator; +import org.eclipse.tractusx.ssi.lib.proof.SignatureType; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; +import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonProcessingException; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MockUtil { + + public static VerifiableCredential mockCredential( + List types, + List credentialSubjects, + KeyPair keyPair, + String host, + Instant expirationDate + ) { + return mockCredential(types, credentialSubjects, keyPair, host, expirationDate, false); + } + + @SneakyThrows + public static VerifiableCredential mockCredential( + List types, + List credentialSubjects, + KeyPair keyPair, + String host, + Instant expirationDate, + boolean jws + ) { + Did issuer = new Did(new DidMethod("web"), new DidMethodIdentifier(host), null); + VerifiableCredentialBuilder builder = MockUtil.getCredentialBuilder( + types, + credentialSubjects, + expirationDate, + issuer + ); + + // Ed25519 Proof Builder + LinkedDataProofGenerator generator; + try { + generator = LinkedDataProofGenerator.newInstance(jws ? SignatureType.JWS : SignatureType.ED25519); + } catch (UnsupportedSignatureTypeException e) { + throw new AssertionError(e); + } + + Proof proof; + try { + proof = + generator.createProof(builder.build(), URI.create(issuer + "#key-1"), keyPair.getPrivateKey()); + } catch (InvalidPrivateKeyFormatException e) { + throw new AssertionError(e); + } + + // Adding Proof to VC + builder.proof(proof); + + return builder.build(); + } + + public static VerifiableCredentialBuilder getCredentialBuilder( + List types, + List credentialSubjects, + Instant expirationDate, + Did issuer + ) { + + VerifiableCredentialBuilder builder = + new VerifiableCredentialBuilder() + .context(List.of( + URI.create("https://www.w3.org/2018/credentials/v1"), + URI.create("https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json"), + URI.create("https://w3id.org/security/suites/jws-2020/v1"), + URI.create("https://catenax-ng.github.io/product-core-schemas/SummaryVC.json"), + URI.create("https://w3id.org/security/suites/ed25519-2020/v1"), + URI.create("https://w3id.org/vc/status-list/2021/v1") + ) + ) + .id(URI.create(issuer + "#key-1")) + .issuer(issuer.toUri()) + .issuanceDate(Instant.now().minus(Duration.ofDays(5))) + .credentialSubject(credentialSubjects) + .expirationDate(expirationDate) + .type(types); + + try { + System.out.println(new ObjectMapper().writeValueAsString(builder.build())); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + return builder; + } + + public static Did generateDid(String host) { + return new Did( + new DidMethod("web"), + new DidMethodIdentifier(host), + null + ); + } + + @SneakyThrows + public static DidDocument buildDidDocument(Did did, KeyPair keyPair) { + IPublicKey publicKey = keyPair.getPublicKey(); + MultibaseString publicKeyBase = MultibaseFactory.create(publicKey.asByte()); + + // Building Verification Methods: + List verificationMethods = new ArrayList<>(); + Ed25519VerificationMethodBuilder builder = new Ed25519VerificationMethodBuilder(); + Ed25519VerificationMethod key = builder.id(URI.create(did.toUri() + "#key-" + 1)).controller(did.toUri()).publicKeyMultiBase(publicKeyBase).build(); + verificationMethods.add(key); + DidDocumentBuilder didDocumentBuilder = new DidDocumentBuilder(); + didDocumentBuilder.id(did.toUri()); + didDocumentBuilder.verificationMethods(verificationMethods); + + return didDocumentBuilder.build(); + } + + public static Wallet mockWallet(String bpn, Did did, KeyPair keyPair) { + Wallet wallet = mock(Wallet.class); + when(wallet.getId()).thenReturn(new Random().nextLong()); + when(wallet.getName()).thenReturn("WalletName"); + when(wallet.getBpn()).thenReturn(bpn); + when(wallet.getDid()).thenReturn(did.toUri().toString()); + when(wallet.getDidDocument()).thenReturn(buildDidDocument(did, keyPair)); + when(wallet.getAlgorithm()).thenReturn("Ed25519"); + when(wallet.getCreatedAt()).thenReturn(new Date()); + when(wallet.getModifiedAt()).thenReturn(new Date()); + when(wallet.getModifiedFrom()).thenReturn(null); + return wallet; + } + + public static void makeFilterWorkForHolder(HoldersCredentialRepository holdersCredentialRepository) { + KeyPair keyPair = generateEDKeys(); + VerifiableCredential verifiableCredential = mockCredential( + List.of("VerifiableCredential", "SummaryCredential"), + List.of(mockCredentialSubject()), + keyPair, + "localhost", + Instant.now().plus(Duration.ofDays(5)) + ); + HoldersCredential holdersCredential = mockHolderCredential(verifiableCredential); + when(holdersCredentialRepository.findAll(any(Specification.class), any(PageRequest.class))).thenReturn( + new PageImpl<>(List.of(holdersCredential)) + ); + } + + public static void makeFilterWorkForIssuer(IssuersCredentialRepository holdersCredentialRepository) { + KeyPair keyPair = generateEDKeys(); + VerifiableCredential verifiableCredential = mockCredential( + List.of("VerifiableCredential", "SummaryCredential"), + List.of(mockCredentialSubject()), + keyPair, + "localhost", + Instant.now().plus(Duration.ofDays(5)) + ); + IssuersCredential holdersCredential = mockIssuerCredential(verifiableCredential); + when(holdersCredentialRepository.findAll(any(Specification.class), any(PageRequest.class))).thenReturn( + new PageImpl<>(List.of(holdersCredential)) + ); + } + + public static void makeCreateWorkForHolder(HoldersCredentialRepository holdersCredentialRepository) { + when(holdersCredentialRepository.save(any(HoldersCredential.class))) + .thenAnswer(new Answer() { + @Override + public HoldersCredential answer(InvocationOnMock invocation) { + HoldersCredential argument = invocation.getArgument(0, HoldersCredential.class); + argument.setId(42L); + return argument; + } + } + ); + } + + public static void makeCreateWorkForIssuer(IssuersCredentialRepository issuersCredentialRepository) { + when(issuersCredentialRepository.save(any(IssuersCredential.class))) + .thenAnswer(new Answer() { + @Override + public IssuersCredential answer(InvocationOnMock invocation) { + IssuersCredential argument = invocation.getArgument(0, IssuersCredential.class); + argument.setId(42L); + return argument; + } + } + ); + } + + public static KeyPair generateEDKeys() { + X25519Generator gen = new X25519Generator(); + KeyPair baseWalletKeys; + try { + baseWalletKeys = gen.generateKey(); + } catch (KeyGenerationException e) { + throw new AssertionError(e); + } + return baseWalletKeys; + } + + public static HoldersCredential mockHolderCredential(VerifiableCredential verifiableCredential) { + + + HoldersCredential cred = mock(HoldersCredential.class); + when(cred.getCredentialId()).thenReturn("credentialId"); + when(cred.getData()).thenReturn(verifiableCredential); + return cred; + } + + public static IssuersCredential mockIssuerCredential(VerifiableCredential verifiableCredential) { + IssuersCredential cred = mock(IssuersCredential.class); + when(cred.getCredentialId()).thenReturn("credentialId"); + when(cred.getData()).thenReturn(verifiableCredential); + return cred; + } + + public static VerifiableCredentialSubject mockCredentialSubject() { + Map subj; + try (InputStream in = MockUtil.class.getResourceAsStream("/credential-subject.json")) { + subj = JSONObjectUtils.parse(new String(in.readAllBytes(), StandardCharsets.UTF_8)); + } catch (IOException | ParseException e) { + throw new RuntimeException(e); + } + + + return new VerifiableCredentialSubject(subj); + } + + public static VerifiableCredentialSubject mockCredentialSubject2() { + Map subj; + try (InputStream in = MockUtil.class.getResourceAsStream("/credential-subject-2.json")) { + subj = JSONObjectUtils.parse(new String(in.readAllBytes(), StandardCharsets.UTF_8)); + } catch (IOException | ParseException e) { + throw new RuntimeException(e); + } + + + return new VerifiableCredentialSubject(subj); + } +} diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestConstants.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestConstants.java new file mode 100644 index 000000000..59380f2d2 --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestConstants.java @@ -0,0 +1,97 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.utils; + +import com.nimbusds.jose.jwk.OctetKeyPair; +import com.nimbusds.jose.util.Base64URL; + +import java.util.Date; + +import static com.nimbusds.jose.jwk.Curve.Ed25519; + +public class TestConstants { + + public static final String DID_BPN_1 = "did:web:localhost:BPNL000000000001"; + public static final String DID_BPN_2 = "did:web:localhost:BPNL000000000002"; + public static final String BPN_1 = "BPNL000000000001"; + public static final String BPN_2 = "BPNL000000000002"; + public static final String BPN_CREDENTIAL_READ = "org.eclipse.tractusx.vc.type:BpnCredential:read"; + public static final String INVALID_CREDENTIAL_READ = "org.eclipse.tractusx.vc.type:InvalidCredential:read"; + public static final String BPN_CREDENTIAL_WRITE = "org.eclipse.tractusx.vc.type:BpnCredential:write"; + public static final String VERIFIABLE_PRESENTATION = "vp"; + public static final String NONCE = "123456"; + public static final OctetKeyPair JWK_OUTER = new OctetKeyPair + .Builder(Ed25519, new Base64URL("4Q5HCXPyutfcj7gLmbAKlYttlJPkykIkRjh7DH2NtZ0")) + .d(new Base64URL("Ktp0sv9dKr_gnzRxpH5V9qpiTgZ1WbkMSv8WtWodewg")) + .keyID("58cb4b32-c2e4-46f0-a3ad-3286e34765ed") + .build(); + + public static final OctetKeyPair JWK_INNER = new OctetKeyPair + .Builder(Ed25519, new Base64URL("Z-8DEkN6pw2E01niDWqrp1kROLF-syIPIpFgmyrVUOU")) + .d(new Base64URL("MLYxSai_oFzuqEfnB2diA3oDuixLg3kQzZKMyW31-2o")) + .keyID("58cb4b32-c2e4-46f0-a3ad-3286e34765ty") + .build(); + public static final Date EXP_VALID_DATE = new Date(Long.parseLong("2559397136000")); + public static final Date IAT_VALID_DATE = new Date(Long.parseLong("1707496483000")); + public static final String DID_JSON_STRING_1 = """ + { + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3c.github.io/vc-jws-2020/contexts/v1" + ], + "id": "did:web:localhost:BPNL000000000001", + "verificationMethod": [ + { + "publicKeyJwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "4Q5HCXPyutfcj7gLmbAKlYttlJPkykIkRjh7DH2NtZ0" + }, + "controller": "did:web:localhost:BPNL000000000001", + "id": "did:web:localhost:BPNL000000000001#58cb4b32-c2e4-46f0-a3ad-3286e34765ed", + "type": "JsonWebKey2020" + } + ] + } + """; + public static final String DID_JSON_STRING_2 = """ + { + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3c.github.io/vc-jws-2020/contexts/v1" + ], + "id": "did:web:localhost:BPNL000000000002", + "verificationMethod": [ + { + "publicKeyJwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "Z-8DEkN6pw2E01niDWqrp1kROLF-syIPIpFgmyrVUOU" + }, + "controller": "did:web:localhost:BPNL000000000002", + "id": "did:web:localhost:BPNL000000000001#58cb4b32-c2e4-46f0-a3ad-3286e34765ty", + "type": "JsonWebKey2020" + } + ] + } + """; +} diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestUtils.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestUtils.java similarity index 51% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestUtils.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestUtils.java index d078b4938..0304ed185 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestUtils.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestUtils.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -24,43 +24,65 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.curiousoddman.rgxgen.RgxGen; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.crypto.Ed25519Signer; +import com.nimbusds.jose.jwk.OctetKeyPair; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import lombok.SneakyThrows; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; -import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType; import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.IssuersCredential; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; import org.eclipse.tractusx.managedidentitywallets.dto.CreateWalletRequest; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueFrameworkCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueMembershipCredentialRequest; +import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; +import org.eclipse.tractusx.ssi.lib.model.verifiable.Verifiable; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialBuilder; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; import org.jetbrains.annotations.NotNull; import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; import org.junit.jupiter.api.Assertions; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import java.net.URI; +import java.time.Instant; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.ACCESS_TOKEN; +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.SCOPE; +import static org.springframework.security.oauth2.core.oidc.IdTokenClaimNames.NONCE; +import static org.springframework.security.oauth2.jwt.JwtClaimNames.JTI; public class TestUtils { - public static ResponseEntity createWallet(String bpn, String name, TestRestTemplate testTemplate, String baseBPN) { + public static ResponseEntity createWallet(String bpn, String name, TestRestTemplate testTemplate, String baseBPN, String didUrl) { HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(baseBPN); - CreateWalletRequest request = CreateWalletRequest.builder().bpn(bpn).name(name).build(); + CreateWalletRequest request = CreateWalletRequest.builder() + .businessPartnerNumber(bpn) + .companyName(name) + .didUrl(didUrl) + .build(); HttpEntity entity = new HttpEntity<>(request, headers); @@ -91,41 +113,25 @@ public static Wallet createWallet(String bpn, String did, WalletRepository walle .didDocument(DidDocument.fromJson(didDocument)) .algorithm(StringPool.ED_25519) .name(bpn) + .signingServiceType(SigningServiceType.LOCAL) .build(); return walletRepository.save(wallet); } public static void checkVC(VerifiableCredential verifiableCredential, MIWSettings miwSettings) { //text context URL - Assertions.assertEquals(verifiableCredential.getContext().size(), miwSettings.vcContexts().size()); - for (URI link : verifiableCredential.getContext()) { - Assertions.assertTrue(miwSettings.vcContexts().contains(link)); + Assertions.assertEquals(verifiableCredential.getContext().size(), miwSettings.vcContexts().size() + 1); + + for (URI link : miwSettings.vcContexts()) { + Assertions.assertTrue(verifiableCredential.getContext().contains(link)); } //check expiry date Assertions.assertEquals(0, verifiableCredential.getExpirationDate().compareTo(miwSettings.vcExpiryDate().toInstant())); } - public static ResponseEntity issueMembershipVC(TestRestTemplate restTemplate, String bpn, String baseWalletBpn) { - HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(baseWalletBpn); - IssueMembershipCredentialRequest request = IssueMembershipCredentialRequest.builder().bpn(bpn).build(); - HttpEntity entity = new HttpEntity<>(request, headers); - - return restTemplate.exchange(RestURI.CREDENTIALS_ISSUER_MEMBERSHIP, HttpMethod.POST, entity, String.class); - } - - public static IssueFrameworkCredentialRequest getIssueFrameworkCredentialRequest(String bpn, String type) { - IssueFrameworkCredentialRequest twinRequest = IssueFrameworkCredentialRequest.builder() - .contractTemplate("http://localhost") - .contractVersion("v1") - .type(type) - .holderIdentifier(bpn) - .build(); - return twinRequest; - } - - public static Wallet getWalletFromString(String body) throws JsonProcessingException { + public static Wallet getWalletFromString(String body) throws JsonProcessingException, JSONException { JSONObject jsonObject = new JSONObject(body); //convert DidDocument JSONObject didDocument = jsonObject.getJSONObject(StringPool.DID_DOCUMENT); @@ -150,42 +156,10 @@ public static Wallet getWalletFromString(String body) throws JsonProcessingExcep } wallet1.setVerifiableCredentials(verifiableCredentials); } - System.out.println("wallet -- >" + wallet1.getBpn()); return wallet1; } - public static String getSummaryCredentialId(String holderDID, HoldersCredentialRepository holdersCredentialRepository) { - List holderVCs = holdersCredentialRepository.getByHolderDidAndType(holderDID, MIWVerifiableCredentialType.SUMMARY_CREDENTIAL); - Assertions.assertEquals(1, holderVCs.size()); - return holderVCs.get(0).getData().getId().toString(); - } - - public static void checkSummaryCredential(String issuerDID, String holderDID, HoldersCredentialRepository holdersCredentialRepository, - IssuersCredentialRepository issuersCredentialRepository, String type, String previousSummaryCredentialId) { - - //get VC from holder of Summary type - List holderVCs = holdersCredentialRepository.getByHolderDidAndType(holderDID, MIWVerifiableCredentialType.SUMMARY_CREDENTIAL); - Assertions.assertEquals(1, holderVCs.size()); - VerifiableCredential vc = holderVCs.get(0).getData(); - VerifiableCredentialSubject subject = vc.getCredentialSubject().get(0); - - //check if type is in items - List list = (List) subject.get(StringPool.ITEMS); - Assertions.assertTrue(list.contains(type)); - - //check in issuer table - List issuerVCs = issuersCredentialRepository.getByIssuerDidAndHolderDidAndType(issuerDID, holderDID, - MIWVerifiableCredentialType.SUMMARY_CREDENTIAL); - IssuersCredential issuersCredential = issuerVCs.stream() - .filter(issuerVC -> issuerVC.getCredentialId().equalsIgnoreCase(vc.getId().toString())).findFirst() - .orElse(null); - Assertions.assertNotNull(issuersCredential); - IssuersCredential previousIssuersCredential = issuerVCs.stream() - .filter(issuerVC -> issuerVC.getCredentialId().equalsIgnoreCase(previousSummaryCredentialId)).findFirst() - .orElse(null); - Assertions.assertNotNull(previousIssuersCredential); - } @NotNull @@ -205,4 +179,92 @@ public static String getRandomBpmNumber() { RgxGen rgxGen = new RgxGen(StringPool.BPN_NUMBER_REGEX); return rgxGen.generate(); } + + public static String buildJWTToken(OctetKeyPair jwk, JWTClaimsSet claimsSet) throws JOSEException { + JWSSigner signer = new Ed25519Signer(jwk); + SignedJWT signedJWT = new SignedJWT( + new JWSHeader.Builder(JWSAlgorithm.EdDSA).keyID(jwk.getKeyID()).build(), + claimsSet); + + signedJWT.sign(signer); + + return signedJWT.serialize(); + } + + public static JWTClaimsSet buildClaimsSet(String issuer, String subject, String audience, String nonce, String scope, Date expiration, Date issuance, String jti) { + return new JWTClaimsSet.Builder() + .issuer(issuer) + .subject(subject) + .audience(audience) + .expirationTime(expiration) + .issueTime(issuance) + .claim(NONCE, nonce) + .claim(SCOPE, scope) + .claim(JTI, jti) + .build(); + } + + public static String generateUuid() { + return UUID.randomUUID().toString(); + } + + public static JWTClaimsSet addAccessTokenToClaimsSet(String accessToken, JWTClaimsSet initialSet) { + return new JWTClaimsSet.Builder(initialSet).claim(ACCESS_TOKEN, accessToken).build(); + } + + public static Wallet buildWallet(String bpn, String did, String didJson) { + return Wallet.builder() + .bpn(bpn) + .did(did) + .didDocument(DidDocument.fromJson(didJson)) + .algorithm(StringPool.ED_25519) + .name(bpn) + .signingServiceType(SigningServiceType.LOCAL) + .build(); + } + + @SneakyThrows + public static VerifiableCredential issueCustomVCUsingBaseWallet(String holderBPn, String holderDid, String issuerDid, String type, HttpHeaders headers, + MIWSettings miwSettings, ObjectMapper objectMapper, TestRestTemplate restTemplate) { + + Map map = getCredentialAsMap(holderBPn, holderDid, issuerDid, type, miwSettings, objectMapper); + HttpEntity entity = new HttpEntity<>(map, headers); + ResponseEntity response = restTemplate.exchange(RestURI.ISSUERS_CREDENTIALS + "?holderDid={did}", HttpMethod.POST, entity, String.class, holderDid); + if (response.getStatusCode().value() == HttpStatus.FORBIDDEN.value()) { + throw new ForbiddenException(); + } + Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); + return new VerifiableCredential(new ObjectMapper().readValue(response.getBody(), Map.class)); + } + + public static Map getCredentialAsMap(String holderBpn, String holderDid, String issuerDid, String type, MIWSettings miwSettings, ObjectMapper objectMapper) throws JsonProcessingException { + // Create VC without proof + //VC Builder + VerifiableCredentialBuilder verifiableCredentialBuilder = + new VerifiableCredentialBuilder(); + + Map subjectData; + if (Objects.equals(type, StringPool.BPN_CREDENTIAL)) { + subjectData = Map.of(Verifiable.ID, holderDid, StringPool.BPN, holderBpn); + } else { + subjectData = Map.of(Verifiable.ID, "test"); + } + //VC Subject + VerifiableCredentialSubject verifiableCredentialSubject = + new VerifiableCredentialSubject(subjectData); + + //Using Builder + VerifiableCredential credentialWithoutProof = + verifiableCredentialBuilder + .id(URI.create(issuerDid + "#" + UUID.randomUUID())) + .context(miwSettings.vcContexts()) + .type(List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, type)) + .issuer(URI.create(issuerDid)) //issuer must be base wallet + .expirationDate(miwSettings.vcExpiryDate().toInstant()) + .issuanceDate(Instant.now()) + .credentialSubject(verifiableCredentialSubject) + .build(); + + return objectMapper.readValue(credentialWithoutProof.toJson(), Map.class); + } } diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TokenValidationUtilsTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TokenValidationUtilsTest.java new file mode 100644 index 000000000..6f9c0091b --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TokenValidationUtilsTest.java @@ -0,0 +1,222 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.utils; + +import com.nimbusds.jwt.JWTClaimsSet; +import lombok.SneakyThrows; +import org.eclipse.tractusx.managedidentitywallets.constant.TokenValidationErrors; +import org.eclipse.tractusx.managedidentitywallets.dto.ValidationResult; +import org.eclipse.tractusx.managedidentitywallets.service.DidDocumentService; +import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Date; +import java.util.List; + +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.DID_BPN_1; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.DID_BPN_2; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.DID_JSON_STRING_1; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.DID_JSON_STRING_2; +import static org.springframework.security.oauth2.core.oidc.IdTokenClaimNames.NONCE; + +@ExtendWith(MockitoExtension.class) +class TokenValidationUtilsTest { + + @Mock + DidDocumentService didDocumentService; + + @InjectMocks + private TokenValidationUtils tokenValidationUtils; + + //checkIfIssuerEqualsSubject + @Test + void checkIfIssuerEqualsSubjectSuccessTest() { + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issuer(DID_BPN_1).subject(DID_BPN_1).build(); + ValidationResult result = tokenValidationUtils.checkIfIssuerEqualsSubject(claimsSet); + Assertions.assertTrue(result.isValid()); + } + + @Test + void checkIfIssuerEqualsSubjectFailureTest() { + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issuer(DID_BPN_1).subject(DID_BPN_2).build(); + ValidationResult result = tokenValidationUtils.checkIfIssuerEqualsSubject(claimsSet); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.ISS_AND_SUB_NOT_EQUAL, result.getErrors().get(0)); + } + + //checkIfSubjectValidAndEqualsDid + @Test + void checkIfSubjectValidAndEqualsDidSuccessTest() { + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(DID_BPN_1).build(); + DidDocument doc = DidDocument.fromJson(DID_JSON_STRING_1); + Mockito.when(didDocumentService.getDidDocument(DID_BPN_1)).thenReturn(doc); + ValidationResult result = tokenValidationUtils.checkIfSubjectValidAndEqualsDid(claimsSet); + Assertions.assertTrue(result.isValid()); + } + + @Test + void checkIfSubjectValidAndEqualsDidFailureWrongDidTest() { + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(DID_BPN_1).build(); + DidDocument doc = DidDocument.fromJson(DID_JSON_STRING_2); + Mockito.when(didDocumentService.getDidDocument(DID_BPN_1)).thenReturn(doc); + ValidationResult result = tokenValidationUtils.checkIfSubjectValidAndEqualsDid(claimsSet); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.SUB_NOT_MATCH_ANY_DID, result.getErrors().get(0)); + } + + @Test + void checkIfSubjectValidAndEqualsDidFailureWrongFormatTest() { + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject("BPNL001").build(); + ValidationResult result = tokenValidationUtils.checkIfSubjectValidAndEqualsDid(claimsSet); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.SUB_NOT_DID, result.getErrors().get(0)); + } + + //checkTokenExpiry + @Test + void checkTokenExpirySuccessTest() { + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(DID_BPN_1) + .expirationTime(new Date(Long.parseLong("2559397136000"))) + .issueTime(new Date(Long.parseLong("1707317488000"))) + .build(); + ValidationResult result = tokenValidationUtils.checkTokenExpiry(claimsSet); + Assertions.assertTrue(result.isValid()); + } + + @Test + void checkTokenExpiryFailureNoExpClaimTest() { + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(DID_BPN_1).build(); + ValidationResult result = tokenValidationUtils.checkTokenExpiry(claimsSet); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.EXP_MISSING, result.getErrors().get(0)); + } + + @Test + void checkTokenExpiryFailureAlreadyExpiredTest() { + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(DID_BPN_1) + .expirationTime(new Date(Long.parseLong("1707320002664"))).build(); + ValidationResult result = tokenValidationUtils.checkTokenExpiry(claimsSet); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.TOKEN_ALREADY_EXPIRED, result.getErrors().get(0)); + } + + @Test + void checkTokenExpiryFailureIatIsAfterExpTest() { + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(DID_BPN_1) + .expirationTime(new Date(Long.parseLong("2527861136000"))) + .issueTime(new Date(Long.parseLong("2559397136000"))) + .build(); + ValidationResult result = tokenValidationUtils.checkTokenExpiry(claimsSet); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.IAT_AFTER_EXPIRATION, result.getErrors().get(0)); + } + + @Test + void checkTokenExpiryFailureIatInTheFutureTest() { + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(DID_BPN_1) + .expirationTime(new Date(Long.parseLong("2559397136000"))) + .issueTime(new Date(Long.parseLong("2527861136000"))) + .build(); + ValidationResult result = tokenValidationUtils.checkTokenExpiry(claimsSet); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.CURRENT_TIME_BEFORE_IAT, result.getErrors().get(0)); + } + + //checkIfAudienceClaimsAreEqual + @Test + void checkIfAudienceClaimsAreEqualSuccessTest() { + JWTClaimsSet claimsSetSI = new JWTClaimsSet.Builder().audience(List.of(DID_BPN_1, DID_BPN_2)).build(); + JWTClaimsSet claimsSetAccess = new JWTClaimsSet.Builder().audience(DID_BPN_1).build(); + ValidationResult result = tokenValidationUtils.checkIfAudienceClaimsAreEqual(claimsSetSI.getAudience(), + claimsSetAccess.getAudience()); + Assertions.assertTrue(result.isValid()); + } + + @Test + void checkIfAudienceClaimsAreEqualFailureNoAudClaimTest() { + JWTClaimsSet claimsSetSI = new JWTClaimsSet.Builder().subject(DID_BPN_1).build(); + JWTClaimsSet claimsSetAccess = new JWTClaimsSet.Builder().audience(DID_BPN_1).build(); + ValidationResult result = tokenValidationUtils.checkIfAudienceClaimsAreEqual(claimsSetSI.getAudience(), + claimsSetAccess.getAudience()); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.AUD_MISSING, result.getErrors().get(0)); + } + + @Test + void checkIfAudienceClaimsAreEqualFailureMismatchTest() { + JWTClaimsSet claimsSetSI = new JWTClaimsSet.Builder().audience(DID_BPN_1).build(); + JWTClaimsSet claimsSetAccess = new JWTClaimsSet.Builder().audience(DID_BPN_2).build(); + ValidationResult result = tokenValidationUtils.checkIfAudienceClaimsAreEqual(claimsSetSI.getAudience(), + claimsSetAccess.getAudience()); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.AUD_CLAIMS_NOT_EQUAL, result.getErrors().get(0)); + } + + @Test + void checkIfAudienceClaimsAreEqualFailureWrongFormatTest() { + JWTClaimsSet claimsSetSI = new JWTClaimsSet.Builder().audience("localhost:BPNL001").build(); + JWTClaimsSet claimsSetAccess = new JWTClaimsSet.Builder().audience("localhost:BPNL001").build(); + ValidationResult result = tokenValidationUtils.checkIfAudienceClaimsAreEqual(claimsSetSI.getAudience(), + claimsSetAccess.getAudience()); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.AUD_NOT_DID, result.getErrors().get(0)); + } + + //checkIfNonceClaimsAreEqual + @SneakyThrows + @Test + void checkIfNonceClaimsAreEqualSuccessTest() { + JWTClaimsSet claimsSetSI = new JWTClaimsSet.Builder().claim("nonce", "123456").build(); + JWTClaimsSet claimsSetAccess = new JWTClaimsSet.Builder().claim("nonce", "123456").build(); + ValidationResult result = tokenValidationUtils.checkIfNonceClaimsAreEqual(claimsSetSI.getStringClaim(NONCE), + claimsSetAccess.getStringClaim(NONCE)); + Assertions.assertTrue(result.isValid()); + } + + @SneakyThrows + @Test + void checkIfNonceClaimsAreEqualFailureNoNonceClaimTest() { + JWTClaimsSet claimsSetSI = new JWTClaimsSet.Builder().audience(DID_BPN_1).build(); + JWTClaimsSet claimsSetAccess = new JWTClaimsSet.Builder().audience(DID_BPN_1).build(); + ValidationResult result = tokenValidationUtils.checkIfNonceClaimsAreEqual(claimsSetSI.getStringClaim(NONCE), + claimsSetAccess.getStringClaim(NONCE)); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.NONCE_MISSING, result.getErrors().get(0)); + } + + @SneakyThrows + @Test + void checkIfNonceClaimsAreEqualFailureMismatchTest() { + JWTClaimsSet claimsSetSI = new JWTClaimsSet.Builder().claim("nonce", "123456").build(); + JWTClaimsSet claimsSetAccess = new JWTClaimsSet.Builder().claim("nonce", "123456789").build(); + ValidationResult result = tokenValidationUtils.checkIfNonceClaimsAreEqual(claimsSetSI.getStringClaim(NONCE), + claimsSetAccess.getStringClaim(NONCE)); + Assertions.assertFalse(result.isValid()); + Assertions.assertEquals(TokenValidationErrors.NONCE_CLAIMS_NOT_EQUAL, result.getErrors().get(0)); + } +} diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/ValidateTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/ValidateTest.java similarity index 100% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/ValidateTest.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/ValidateTest.java diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/HoldersCredentialTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/HoldersCredentialTest.java similarity index 67% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/HoldersCredentialTest.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/HoldersCredentialTest.java index d7f586b16..dcd0344e0 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/HoldersCredentialTest.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/HoldersCredentialTest.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -23,29 +23,37 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; -import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType; import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; import org.eclipse.tractusx.managedidentitywallets.controller.IssuersCredentialController; import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; import org.eclipse.tractusx.managedidentitywallets.dto.CreateWalletRequest; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueFrameworkCredentialRequest; +import org.eclipse.tractusx.managedidentitywallets.dto.CredentialVerificationRequest; import org.eclipse.tractusx.managedidentitywallets.utils.AuthenticationUtils; import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; import org.eclipse.tractusx.ssi.lib.did.resolver.DidResolver; import org.eclipse.tractusx.ssi.lib.did.web.DidWebFactory; +import org.eclipse.tractusx.ssi.lib.exception.did.DidParseException; +import org.eclipse.tractusx.ssi.lib.exception.json.TransformJsonLdException; +import org.eclipse.tractusx.ssi.lib.exception.key.InvalidPublicKeyFormatException; +import org.eclipse.tractusx.ssi.lib.exception.proof.NoVerificationKeyFoundException; +import org.eclipse.tractusx.ssi.lib.exception.proof.SignatureParseException; +import org.eclipse.tractusx.ssi.lib.exception.proof.SignatureVerificationFailedException; +import org.eclipse.tractusx.ssi.lib.exception.proof.UnsupportedSignatureTypeException; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialBuilder; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; import org.eclipse.tractusx.ssi.lib.proof.LinkedDataProofValidation; -import org.json.JSONArray; -import org.json.JSONObject; +import org.eclipse.tractusx.ssi.lib.serialization.SerializeUtil; +import org.json.JSONException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -56,15 +64,25 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.*; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; import java.net.URI; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {ManagedIdentityWalletsApplication.class}) -@ContextConfiguration(initializers = {TestContextInitializer.class}) +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { ManagedIdentityWalletsApplication.class }) +@ContextConfiguration(initializers = { TestContextInitializer.class }) @ExtendWith(MockitoExtension.class) class HoldersCredentialTest { @@ -132,7 +150,7 @@ void getCredentialsTest403() { } @Test - void getCredentials200() throws com.fasterxml.jackson.core.JsonProcessingException { + void getCredentials200() throws com.fasterxml.jackson.core.JsonProcessingException, JSONException { String baseDID = miwSettings.authorityWalletDid(); @@ -141,26 +159,14 @@ void getCredentials200() throws com.fasterxml.jackson.core.JsonProcessingExcepti HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); //save wallet TestUtils.createWallet(bpn, did, walletRepository); - TestUtils.issueMembershipVC(restTemplate, bpn, miwSettings.authorityWalletBpn()); - String vcList = """ - [ - {"type":"TraceabilityCredential"}, - {"type":"SustainabilityCredential"}, - {"type":"ResiliencyCredential"}, - {"type":"QualityCredential"}, - {"type":"PcfCredential"} - ] - """; - JSONArray jsonArray = new JSONArray(vcList); - - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject jsonObject = jsonArray.getJSONObject(i); - IssueFrameworkCredentialRequest request = TestUtils.getIssueFrameworkCredentialRequest(bpn, jsonObject.get(StringPool.TYPE).toString()); - HttpEntity entity = new HttpEntity<>(request, AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn())); //ony base wallet can issue VC - ResponseEntity exchange = restTemplate.exchange(RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, HttpMethod.POST, entity, String.class); - Assertions.assertEquals(exchange.getStatusCode().value(), HttpStatus.CREATED.value()); - } + List vcs = new ArrayList<>(); + List typesOfVcs = List.of("Type1", "Type2", "Type3"); + + typesOfVcs.forEach(type -> { + VerifiableCredential verifiableCredential = TestUtils.issueCustomVCUsingBaseWallet(bpn, did, miwSettings.authorityWalletDid(), type, AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()), miwSettings, objectMapper, restTemplate); + vcs.add(verifiableCredential); + }); HttpEntity entity = new HttpEntity<>(headers); @@ -168,7 +174,7 @@ void getCredentials200() throws com.fasterxml.jackson.core.JsonProcessingExcepti , HttpMethod.GET, entity, String.class, baseDID); List credentialList = TestUtils.getVerifiableCredentials(response, objectMapper); Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); - Assertions.assertEquals(7, Objects.requireNonNull(credentialList).size()); //5 framework + 1 BPN + 1 Summary + Assertions.assertEquals(typesOfVcs.size(), Objects.requireNonNull(credentialList).size()); response = restTemplate.exchange(RestURI.CREDENTIALS + "?credentialId={id}" , HttpMethod.GET, entity, String.class, credentialList.get(0).getId()); @@ -176,36 +182,65 @@ void getCredentials200() throws com.fasterxml.jackson.core.JsonProcessingExcepti Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); Assertions.assertEquals(1, Objects.requireNonNull(credentialList).size()); - List list = new ArrayList<>(); - list.add(MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL); response = restTemplate.exchange(RestURI.CREDENTIALS + "?type={list}" - , HttpMethod.GET, entity, String.class, String.join(",", list)); + , HttpMethod.GET, entity, String.class, String.join(",", typesOfVcs)); credentialList = TestUtils.getVerifiableCredentials(response, objectMapper); Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); - Assertions.assertEquals(1, Objects.requireNonNull(credentialList).size()); + Assertions.assertEquals(typesOfVcs.size(), Objects.requireNonNull(credentialList).size()); - list = new ArrayList<>(); - list.add(MIWVerifiableCredentialType.SUMMARY_CREDENTIAL); + + //test get by type + String type = typesOfVcs.get(0); response = restTemplate.exchange(RestURI.CREDENTIALS + "?type={list}" - , HttpMethod.GET, entity, String.class, String.join(",", list)); + , HttpMethod.GET, entity, String.class, type); credentialList = TestUtils.getVerifiableCredentials(response, objectMapper); Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); - Assertions.assertEquals(1, credentialList.size()); - VerifiableCredentialSubject subject = credentialList.get(0).getCredentialSubject().get(0); - List itemList = (List) subject.get(StringPool.ITEMS); - Assertions.assertTrue(itemList.contains(MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL)); - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject jsonObject = jsonArray.getJSONObject(i); - Assertions.assertTrue(itemList.contains(jsonObject.get(StringPool.TYPE).toString())); - } + Assertions.assertEquals(1, Objects.requireNonNull(credentialList).size()); + } + + @Test + @DisplayName("Get Credentials as JWT") + void getCredentialsAsJWT200() throws JSONException { + + String baseDID = miwSettings.authorityWalletDid(); + String bpn = TestUtils.getRandomBpmNumber(); + String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); + HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); + // save wallet + TestUtils.createWallet(bpn, did, walletRepository); + + //issue VC : test data + List vcs = new ArrayList<>(); + List typesOfVcs = List.of("Type1", "Type2", "Type3"); + + typesOfVcs.forEach(type -> { + VerifiableCredential verifiableCredential = TestUtils.issueCustomVCUsingBaseWallet(bpn, did, miwSettings.authorityWalletDid(), type, AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()), miwSettings, objectMapper, restTemplate); + vcs.add(verifiableCredential); + }); + + + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange(RestURI.CREDENTIALS + "?issuerIdentifier={did}&asJwt=true", + HttpMethod.GET, entity, String.class, baseDID); + + Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); + Map responseMap = SerializeUtil.fromJson(response.getBody()); + List> vcsAsJwt = (ArrayList>) responseMap.get("content"); + + Assertions.assertEquals(vcs.size(), vcsAsJwt.size()); + vcsAsJwt.forEach(vc -> { + Assertions.assertNotNull(vc.get(StringPool.VC_JWT_KEY)); + }); } @Test - void validateCredentialsWithInvalidVC() throws com.fasterxml.jackson.core.JsonProcessingException { + void validateCredentialsWithInvalidVC() throws com.fasterxml.jackson.core.JsonProcessingException, UnsupportedSignatureTypeException, InvalidPublicKeyFormatException, NoVerificationKeyFoundException, SignatureParseException, DidParseException, SignatureVerificationFailedException, TransformJsonLdException { //data setup - Map map = issueVC(); + CredentialVerificationRequest request = new CredentialVerificationRequest(); + request.setVc(issueVC()); //service call try (MockedStatic utils = Mockito.mockStatic(LinkedDataProofValidation.class)) { @@ -217,18 +252,18 @@ void validateCredentialsWithInvalidVC() throws com.fasterxml.jackson.core.JsonPr }).thenReturn(mock); Mockito.when(mock.verify(Mockito.any(VerifiableCredential.class))).thenReturn(false); - Map stringObjectMap = credentialController.credentialsValidation(map, false).getBody(); + Map stringObjectMap = credentialController.credentialsValidation(request, false).getBody(); Assertions.assertFalse(Boolean.parseBoolean(stringObjectMap.get(StringPool.VALID).toString())); } } + @SneakyThrows @Test @DisplayName("validate VC with date check true, it should return true") - void validateCredentialsWithExpiryCheckTrue() throws com.fasterxml.jackson.core.JsonProcessingException { - - //data setup - Map map = issueVC(); + void validateCredentialsWithExpiryCheckTrue() { + CredentialVerificationRequest request = new CredentialVerificationRequest(); + request.setVc(issueVC()); //service call try (MockedStatic utils = Mockito.mockStatic(LinkedDataProofValidation.class)) { @@ -240,7 +275,7 @@ void validateCredentialsWithExpiryCheckTrue() throws com.fasterxml.jackson.core. }).thenReturn(mock); Mockito.when(mock.verify(Mockito.any(VerifiableCredential.class))).thenReturn(true); - Map stringObjectMap = credentialController.credentialsValidation(map, true).getBody(); + Map stringObjectMap = credentialController.credentialsValidation(request, true).getBody(); Assertions.assertTrue(Boolean.parseBoolean(stringObjectMap.get(StringPool.VALID).toString())); Assertions.assertTrue(Boolean.parseBoolean(stringObjectMap.get(StringPool.VALIDATE_EXPIRY_DATE).toString())); } @@ -248,13 +283,13 @@ void validateCredentialsWithExpiryCheckTrue() throws com.fasterxml.jackson.core. @Test @DisplayName("validate expired VC with date check false, it should return true") - void validateCredentialsWithExpiryCheckFalse() throws com.fasterxml.jackson.core.JsonProcessingException { + void validateCredentialsWithExpiryCheckFalse() throws com.fasterxml.jackson.core.JsonProcessingException, UnsupportedSignatureTypeException, InvalidPublicKeyFormatException, NoVerificationKeyFoundException, SignatureParseException, DidParseException, SignatureVerificationFailedException, TransformJsonLdException { + CredentialVerificationRequest request = new CredentialVerificationRequest(); + request.setVc(issueVC()); - //data setup - Map map = issueVC(); //modify expiry date Instant instant = Instant.now().minusSeconds(60); - map.put("expirationDate", instant.toString()); + request.put("expirationDate", instant.toString()); //service call @@ -267,7 +302,7 @@ void validateCredentialsWithExpiryCheckFalse() throws com.fasterxml.jackson.core }).thenReturn(mock); Mockito.when(mock.verify(Mockito.any(VerifiableCredential.class))).thenReturn(true); - Map stringObjectMap = credentialController.credentialsValidation(map, false).getBody(); + Map stringObjectMap = credentialController.credentialsValidation(request, false).getBody(); Assertions.assertTrue(Boolean.parseBoolean(stringObjectMap.get(StringPool.VALID).toString())); } } @@ -275,13 +310,15 @@ void validateCredentialsWithExpiryCheckFalse() throws com.fasterxml.jackson.core @Test @DisplayName("validate expired VC with date check true, it should return false") - void validateExpiredCredentialsWithExpiryCheckTrue() throws com.fasterxml.jackson.core.JsonProcessingException { + void validateExpiredCredentialsWithExpiryCheckTrue() throws com.fasterxml.jackson.core.JsonProcessingException, UnsupportedSignatureTypeException, InvalidPublicKeyFormatException, NoVerificationKeyFoundException, SignatureParseException, DidParseException, SignatureVerificationFailedException, TransformJsonLdException { //data setup - Map map = issueVC(); + CredentialVerificationRequest request = new CredentialVerificationRequest(); + request.setVc(issueVC()); + //modify expiry date Instant instant = Instant.now().minusSeconds(60); - map.put("expirationDate", instant.toString()); + request.put("expirationDate", instant.toString()); //service call try (MockedStatic utils = Mockito.mockStatic(LinkedDataProofValidation.class)) { @@ -293,7 +330,7 @@ void validateExpiredCredentialsWithExpiryCheckTrue() throws com.fasterxml.jackso }).thenReturn(mock); Mockito.when(mock.verify(Mockito.any(VerifiableCredential.class))).thenReturn(true); - Map stringObjectMap = credentialController.credentialsValidation(map, true).getBody(); + Map stringObjectMap = credentialController.credentialsValidation(request, true).getBody(); Assertions.assertFalse(Boolean.parseBoolean(stringObjectMap.get(StringPool.VALID).toString())); Assertions.assertFalse(Boolean.parseBoolean(stringObjectMap.get(StringPool.VALIDATE_EXPIRY_DATE).toString())); @@ -304,9 +341,10 @@ void validateExpiredCredentialsWithExpiryCheckTrue() throws com.fasterxml.jackso private Map issueVC() throws JsonProcessingException { String bpn = TestUtils.getRandomBpmNumber(); String baseBpn = miwSettings.authorityWalletBpn(); - TestUtils.createWallet(bpn, "Test", restTemplate, baseBpn); - ResponseEntity vc = TestUtils.issueMembershipVC(restTemplate, bpn, miwSettings.authorityWalletBpn()); - VerifiableCredential verifiableCredential = new VerifiableCredential(new ObjectMapper().readValue(vc.getBody(), Map.class)); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + ResponseEntity response = TestUtils.createWallet(bpn, "Test Wallet", restTemplate, baseBpn, defaultLocation); + Wallet wallet = TestUtils.getWalletFromString(response.getBody()); + VerifiableCredential verifiableCredential = TestUtils.issueCustomVCUsingBaseWallet(bpn, wallet.getDid(), miwSettings.authorityWalletDid(), "Type1", AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()), miwSettings, objectMapper, restTemplate); Map map = objectMapper.readValue(verifiableCredential.toJson(), Map.class); return map; } @@ -315,7 +353,8 @@ private Map issueVC() throws JsonProcessingException { private ResponseEntity issueVC(String bpn, String did, String type, HttpHeaders headers) throws JsonProcessingException { String baseBpn = miwSettings.authorityWalletBpn(); //save wallet - TestUtils.createWallet(bpn, did, restTemplate, baseBpn); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + TestUtils.createWallet(bpn, did, restTemplate, baseBpn, defaultLocation); // Create VC without proof //VC Bulider diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/IssuersCredentialTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/IssuersCredentialTest.java similarity index 56% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/IssuersCredentialTest.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/IssuersCredentialTest.java index e67bee8b0..46f602d29 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/IssuersCredentialTest.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/IssuersCredentialTest.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -19,6 +19,7 @@ * ****************************************************************************** */ + package org.eclipse.tractusx.managedidentitywallets.vc; import com.fasterxml.jackson.core.JsonProcessingException; @@ -26,39 +27,44 @@ import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; -import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType; import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; import org.eclipse.tractusx.managedidentitywallets.dao.entity.IssuersCredential; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; import org.eclipse.tractusx.managedidentitywallets.dto.CreateWalletRequest; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueFrameworkCredentialRequest; +import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; import org.eclipse.tractusx.managedidentitywallets.utils.AuthenticationUtils; import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; import org.eclipse.tractusx.ssi.lib.did.web.DidWebFactory; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialBuilder; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; -import org.json.JSONArray; -import org.json.JSONObject; +import org.eclipse.tractusx.ssi.lib.serialization.SerializeUtil; +import org.json.JSONException; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.*; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; -import java.net.URI; -import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {ManagedIdentityWalletsApplication.class}) -@ContextConfiguration(initializers = {TestContextInitializer.class}) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { ManagedIdentityWalletsApplication.class }) +@ContextConfiguration(initializers = { TestContextInitializer.class }) class IssuersCredentialTest { @Autowired @@ -77,32 +83,20 @@ class IssuersCredentialTest { @Test - void getCredentials200() throws com.fasterxml.jackson.core.JsonProcessingException { + void getCredentials200() throws com.fasterxml.jackson.core.JsonProcessingException, JSONException { String baseBPN = miwSettings.authorityWalletBpn(); String holderBpn = TestUtils.getRandomBpmNumber(); String holderDID = "did:web:localhost:" + holderBpn; HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(baseBPN); //save wallet - TestUtils.createWallet(holderBpn, holderDID, walletRepository); - TestUtils.issueMembershipVC(restTemplate, holderBpn, baseBPN); - String vcList = """ - [ - {"type":"TraceabilityCredential"}, - {"type":"SustainabilityCredential"}, - {"type":"ResiliencyCredential"}, - {"type":"QualityCredential"}, - {"type":"PcfCredential"} - ] - """; - JSONArray jsonArray = new JSONArray(vcList); - - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject jsonObject = jsonArray.getJSONObject(i); - IssueFrameworkCredentialRequest request = TestUtils.getIssueFrameworkCredentialRequest(holderBpn, jsonObject.get(StringPool.TYPE).toString()); - HttpEntity entity = new HttpEntity<>(request, AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn())); //ony base wallet can issue VC - ResponseEntity exchange = restTemplate.exchange(RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, HttpMethod.POST, entity, String.class); - Assertions.assertEquals(exchange.getStatusCode().value(), HttpStatus.CREATED.value()); - } + Wallet wallet = TestUtils.createWallet(holderBpn, holderDID, walletRepository); + + //issue some VCs + List typesOfVcs = List.of("Type1", "Type2", "Type3"); + typesOfVcs.forEach(type -> { + TestUtils.issueCustomVCUsingBaseWallet(holderBpn, wallet.getDid(), miwSettings.authorityWalletDid(), type, AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()), miwSettings, objectMapper, restTemplate); + }); + HttpEntity entity = new HttpEntity<>(headers); @@ -112,7 +106,7 @@ void getCredentials200() throws com.fasterxml.jackson.core.JsonProcessingExcepti List credentialList = TestUtils.getVerifiableCredentials(response, objectMapper); Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); - Assertions.assertEquals(12, Objects.requireNonNull(credentialList).size()); //5 framework CV + 1 membership + 6 Summary VC + Assertions.assertEquals(typesOfVcs.size(), Objects.requireNonNull(credentialList).size()); response = restTemplate.exchange(RestURI.ISSUERS_CREDENTIALS + "?credentialId={id}" @@ -121,29 +115,53 @@ void getCredentials200() throws com.fasterxml.jackson.core.JsonProcessingExcepti Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); Assertions.assertEquals(1, Objects.requireNonNull(credentialList).size()); - List list = new ArrayList<>(); - list.add(MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL); - response = restTemplate.exchange(RestURI.ISSUERS_CREDENTIALS + "?type={list}" - , HttpMethod.GET, entity, String.class, String.join(",", list)); + + response = restTemplate.exchange(RestURI.ISSUERS_CREDENTIALS + "?type={list}&holderIdentifier={holderIdentifier}" + , HttpMethod.GET, entity, String.class, String.join(",", typesOfVcs), wallet.getBpn()); credentialList = TestUtils.getVerifiableCredentials(response, objectMapper); Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); - //all VC must be type of MEMBERSHIP_CREDENTIAL_CX - credentialList.forEach(vc -> { - Assertions.assertTrue(vc.getTypes().contains(MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL)); - }); + //here we at getting VCs from issuer table, it will have double entry + Assertions.assertEquals(typesOfVcs.size(), Objects.requireNonNull(credentialList).size()); - list = new ArrayList<>(); - list.add(MIWVerifiableCredentialType.SUMMARY_CREDENTIAL); - response = restTemplate.exchange(RestURI.ISSUERS_CREDENTIALS + "?type={list}&holderIdentifier={did}" - , HttpMethod.GET, entity, String.class, String.join(",", list), holderDID); + + response = restTemplate.exchange(RestURI.ISSUERS_CREDENTIALS + "?type={list}&holderIdentifier={holderIdentifier}" + , HttpMethod.GET, entity, String.class, typesOfVcs.get(0), wallet.getBpn()); credentialList = TestUtils.getVerifiableCredentials(response, objectMapper); Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); - Assertions.assertEquals(6, Objects.requireNonNull(credentialList).size()); //5 framework CV + 1 membership + Assertions.assertEquals(1, Objects.requireNonNull(credentialList).size()); + + } + + @Test + @DisplayName("Get Credentials as JWT") + void getCredentialsAsJWT200() throws JSONException { + String baseBPN = miwSettings.authorityWalletBpn(); + String holderBpn = TestUtils.getRandomBpmNumber(); + String holderDID = "did:web:localhost:" + holderBpn; + HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(baseBPN); + //save wallet + Wallet wallet = TestUtils.createWallet(holderBpn, holderDID, walletRepository); + + //create test data + List typesOfVcs = List.of("Type1", "Type2", "Type3"); + typesOfVcs.forEach(type -> { + TestUtils.issueCustomVCUsingBaseWallet(holderBpn, wallet.getDid(), miwSettings.authorityWalletDid(), type, AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()), miwSettings, objectMapper, restTemplate); + }); + + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity response = restTemplate.exchange(RestURI.ISSUERS_CREDENTIALS + "?holderIdentifier={did}&asJwt=true" + , HttpMethod.GET, entity, String.class, holderDID); + + Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); + Map responseMap = SerializeUtil.fromJson(response.getBody()); + List> vcsAsJwt = (ArrayList>) responseMap.get("content"); + //5 framework CV + 1 membership + 6 Summary VC + Assertions.assertEquals(typesOfVcs.size(), vcsAsJwt.size()); + vcsAsJwt.forEach(vc -> { + Assertions.assertNotNull(vc.get(StringPool.VC_JWT_KEY)); - for (VerifiableCredential vc : credentialList) { - Assertions.assertEquals(3, vc.getContext().size(), "Each credential requires 3 contexts"); - } + }); } @@ -166,9 +184,15 @@ void issueCredentialsWithoutBaseWalletBPN403() throws JsonProcessingException { String type = "TestCredential"; HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); - ResponseEntity response = issueVC(bpn, holderDid, holderDid, type, headers); + String baseBpn = miwSettings.authorityWalletBpn(); + //save wallet + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + TestUtils.createWallet(bpn, holderDid, restTemplate, baseBpn, defaultLocation); - Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response.getStatusCode().value()); + + Assertions.assertThrows(ForbiddenException.class, () -> { + TestUtils.issueCustomVCUsingBaseWallet(bpn, holderDid, holderDid, type, headers, miwSettings, objectMapper, restTemplate); + }); } @Test @@ -176,10 +200,14 @@ void issueCredentialsToBaseWallet200() throws JsonProcessingException { String type = "TestCredential"; HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()); - ResponseEntity response = issueVC(miwSettings.authorityWalletBpn(), miwSettings.authorityWalletDid(), miwSettings.authorityWalletDid(), type, headers); + String baseBpn = miwSettings.authorityWalletBpn(); + String bpn = TestUtils.getRandomBpmNumber(); + //save wallet + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn, defaultLocation); + + VerifiableCredential verifiableCredential = TestUtils.issueCustomVCUsingBaseWallet(baseBpn, miwSettings.authorityWalletDid(), miwSettings.authorityWalletDid(), type, headers, miwSettings, objectMapper, restTemplate); - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - VerifiableCredential verifiableCredential = new VerifiableCredential(new ObjectMapper().readValue(response.getBody(), Map.class)); Assertions.assertNotNull(verifiableCredential.getProof()); List credentials = holdersCredentialRepository.getByHolderDidAndType(miwSettings.authorityWalletDid(), type); @@ -188,19 +216,6 @@ void issueCredentialsToBaseWallet200() throws JsonProcessingException { Assertions.assertTrue(credentials.get(0).isSelfIssued()); //stored must be true } - - @Test - void issueSummaryCredentials400() throws com.fasterxml.jackson.core.JsonProcessingException { - - String bpn = TestUtils.getRandomBpmNumber(); - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()); - - ResponseEntity response = issueVC(bpn, did, miwSettings.authorityWalletDid(), MIWVerifiableCredentialType.SUMMARY_CREDENTIAL, headers); - - Assertions.assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusCode().value()); - } - @Test void issueCredentials200() throws com.fasterxml.jackson.core.JsonProcessingException { @@ -209,10 +224,13 @@ void issueCredentials200() throws com.fasterxml.jackson.core.JsonProcessingExcep String type = "TestCredential"; HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()); - ResponseEntity response = issueVC(bpn, did, miwSettings.authorityWalletDid(), type, headers); + String baseBpn = miwSettings.authorityWalletBpn(); + //save wallet + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn, defaultLocation); + + VerifiableCredential verifiableCredential = TestUtils.issueCustomVCUsingBaseWallet(bpn, did, miwSettings.authorityWalletDid(), type, headers, miwSettings, objectMapper, restTemplate); - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - VerifiableCredential verifiableCredential = new VerifiableCredential(new ObjectMapper().readValue(response.getBody(), Map.class)); Assertions.assertNotNull(verifiableCredential.getProof()); List credentials = holdersCredentialRepository.getByHolderDidAndType(did, type); @@ -228,35 +246,5 @@ void issueCredentials200() throws com.fasterxml.jackson.core.JsonProcessingExcep } - private ResponseEntity issueVC(String bpn, String holderDid, String issuerDid, String type, HttpHeaders headers) throws JsonProcessingException { - String baseBpn = miwSettings.authorityWalletBpn(); - //save wallet - TestUtils.createWallet(bpn, holderDid, restTemplate, baseBpn); - - // Create VC without proof - //VC Bulider - VerifiableCredentialBuilder verifiableCredentialBuilder = - new VerifiableCredentialBuilder(); - - //VC Subject - VerifiableCredentialSubject verifiableCredentialSubject = - new VerifiableCredentialSubject(Map.of("test", "test")); - - //Using Builder - VerifiableCredential credentialWithoutProof = - verifiableCredentialBuilder - .id(URI.create(issuerDid + "#" + UUID.randomUUID())) - .context(miwSettings.vcContexts()) - .type(List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, type)) - .issuer(URI.create(issuerDid)) //issuer must be base wallet - .expirationDate(miwSettings.vcExpiryDate().toInstant()) - .issuanceDate(Instant.now()) - .credentialSubject(verifiableCredentialSubject) - .build(); - - Map map = objectMapper.readValue(credentialWithoutProof.toJson(), Map.class); - HttpEntity entity = new HttpEntity<>(map, headers); - return restTemplate.exchange(RestURI.ISSUERS_CREDENTIALS + "?holderDid={did}", HttpMethod.POST, entity, String.class, holderDid); - } } diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/PresentationValidationTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/PresentationValidationTest.java similarity index 80% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/PresentationValidationTest.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/PresentationValidationTest.java index c168cc39c..4441d6385 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/PresentationValidationTest.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/PresentationValidationTest.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -21,7 +21,12 @@ package org.eclipse.tractusx.managedidentitywallets.vc; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.SneakyThrows; import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; @@ -29,13 +34,13 @@ import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dto.CreateWalletRequest; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueMembershipCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.exception.WalletNotFoundProblem; +import org.eclipse.tractusx.managedidentitywallets.dto.CredentialsResponse; import org.eclipse.tractusx.managedidentitywallets.service.IssuersCredentialService; import org.eclipse.tractusx.managedidentitywallets.service.PresentationService; import org.eclipse.tractusx.managedidentitywallets.service.WalletService; import org.eclipse.tractusx.managedidentitywallets.utils.AuthenticationUtils; import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; +import org.eclipse.tractusx.ssi.lib.exception.did.DidParseException; import org.eclipse.tractusx.ssi.lib.model.did.Did; import org.eclipse.tractusx.ssi.lib.model.did.DidParser; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; @@ -43,7 +48,6 @@ import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentationBuilder; import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentationType; import org.eclipse.tractusx.ssi.lib.serialization.SerializeUtil; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -64,8 +68,10 @@ import java.util.Map; import java.util.UUID; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {ManagedIdentityWalletsApplication.class}) -@ContextConfiguration(initializers = {TestContextInitializer.class}) +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { ManagedIdentityWalletsApplication.class }) +@ContextConfiguration(initializers = { TestContextInitializer.class }) class PresentationValidationTest { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() @@ -87,53 +93,46 @@ class PresentationValidationTest { private String bpnOperator; private Did tenant_1; private Did tenant_2; - private VerifiableCredential membershipCredential_1; - private VerifiableCredential membershipCredential_2; + private VerifiableCredential vc_1; + private VerifiableCredential vc_2; + @SneakyThrows @BeforeEach - public void setup() { + public void setup() throws DidParseException { bpnOperator = miwSettings.authorityWalletBpn(); CreateWalletRequest createWalletRequest = new CreateWalletRequest(); - createWalletRequest.setBpn(bpnTenant_1); - createWalletRequest.setName("My Test Tenant Wallet"); + createWalletRequest.setBusinessPartnerNumber(bpnTenant_1); + createWalletRequest.setCompanyName("My Test Tenant Wallet"); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpnTenant_1; + createWalletRequest.setDidUrl(defaultLocation); Wallet tenantWallet = walletService.createWallet(createWalletRequest, bpnOperator); tenant_1 = DidParser.parse(tenantWallet.getDid()); CreateWalletRequest createWalletRequest2 = new CreateWalletRequest(); - createWalletRequest2.setBpn(bpnTenant_2); - createWalletRequest2.setName("My Test Tenant Wallet"); + createWalletRequest2.setBusinessPartnerNumber(bpnTenant_2); + createWalletRequest2.setCompanyName("My Test Tenant Wallet"); + String defaultLocation2 = miwSettings.host() + COLON_SEPARATOR + bpnTenant_2; + createWalletRequest2.setDidUrl(defaultLocation2); Wallet tenantWallet2 = walletService.createWallet(createWalletRequest2, bpnOperator); tenant_2 = DidParser.parse(tenantWallet2.getDid()); - IssueMembershipCredentialRequest issueMembershipCredentialRequest = new IssueMembershipCredentialRequest(); - issueMembershipCredentialRequest.setBpn(bpnTenant_1); - membershipCredential_1 = issuersCredentialService.issueMembershipCredential(issueMembershipCredentialRequest, bpnOperator); - IssueMembershipCredentialRequest issueMembershipCredentialRequest2 = new IssueMembershipCredentialRequest(); - issueMembershipCredentialRequest2.setBpn(bpnTenant_2); - membershipCredential_2 = issuersCredentialService.issueMembershipCredential(issueMembershipCredentialRequest2, bpnOperator); - } + Map type1 = TestUtils.getCredentialAsMap(miwSettings.authorityWalletBpn(), miwSettings.authorityWalletDid(), miwSettings.authorityWalletDid(), "Type1", miwSettings, new com.fasterxml.jackson.databind.ObjectMapper()); - @AfterEach - public void cleanUp() { - try { - Wallet tenantWallet = walletService.getWalletByIdentifier(bpnTenant_1, false, bpnOperator); - walletService.delete(tenantWallet.getId()); - } catch (WalletNotFoundProblem e) { - // ignore - } - try { - Wallet tenantWallet = walletService.getWalletByIdentifier(bpnTenant_2, false, bpnOperator); - walletService.delete(tenantWallet.getId()); - } catch (WalletNotFoundProblem e) { - // ignore - } + CredentialsResponse rs1 = issuersCredentialService.issueCredentialUsingBaseWallet(tenantWallet.getDid(), type1, false, bpnOperator); + vc_1 = new ObjectMapper().convertValue(rs1, VerifiableCredential.class); + + + Map type2 = TestUtils.getCredentialAsMap(miwSettings.authorityWalletBpn(), miwSettings.authorityWalletDid(), miwSettings.authorityWalletDid(), "Type2", miwSettings, new com.fasterxml.jackson.databind.ObjectMapper()); + + CredentialsResponse rs2 = issuersCredentialService.issueCredentialUsingBaseWallet(tenantWallet.getDid(), type2, false, bpnOperator); + vc_2 = new ObjectMapper().convertValue(rs2, VerifiableCredential.class); } @Test void testSuccessfulValidation() { - Map presentation = createPresentationJwt(membershipCredential_1, tenant_1); + Map presentation = createPresentationJwt(vc_1, tenant_1); VerifiablePresentationValidationResponse response = validateJwtOfCredential(presentation); Assertions.assertTrue(response.valid); } @@ -141,7 +140,7 @@ void testSuccessfulValidation() { @Test @SneakyThrows public void testSuccessfulValidationForMultipleVC() { - Map creationResponse = createPresentationJwt(List.of(membershipCredential_1, membershipCredential_2), tenant_1); + Map creationResponse = createPresentationJwt(List.of(vc_1, vc_2), tenant_1); // get the payload of the json web token String encodedJwtPayload = ((String) creationResponse.get("vp")).split("\\.")[1]; Map decodedJwtPayload = OBJECT_MAPPER.readValue(Base64.getUrlDecoder().decode(encodedJwtPayload), Map.class); @@ -157,7 +156,7 @@ public void testSuccessfulValidationForMultipleVC() { public void testValidationFailureOfCredentialWitInvalidExpirationDate() { // test is related to this old issue where the signature check still succeeded // https://github.com/eclipse-tractusx/SSI-agent-lib/issues/4 - VerifiableCredential copyCredential = new VerifiableCredential(membershipCredential_1); + VerifiableCredential copyCredential = new VerifiableCredential(vc_1); // e.g. an attacker tries to extend the validity of a verifiable credential copyCredential.put(VerifiableCredential.EXPIRATION_DATE, "2500-09-30T22:00:00Z"); Map presentation = createPresentationJwt(copyCredential, tenant_1); @@ -170,10 +169,10 @@ public void testValidationFailureOfCredentialWitInvalidExpirationDate() { public void testValidationFailureOfCredentialWitInvalidExpirationDateInSecondCredential() { // test is related to this old issue where the signature check still succeeded // https://github.com/eclipse-tractusx/SSI-agent-lib/issues/4 - VerifiableCredential copyCredential = new VerifiableCredential(membershipCredential_1); + VerifiableCredential copyCredential = new VerifiableCredential(vc_1); // e.g. an attacker tries to extend the validity of a verifiable credential copyCredential.put(VerifiableCredential.EXPIRATION_DATE, "2500-09-30T22:00:00Z"); - Map presentation = createPresentationJwt(List.of(membershipCredential_1, copyCredential), tenant_1); + Map presentation = createPresentationJwt(List.of(vc_1, copyCredential), tenant_1); VerifiablePresentationValidationResponse response = validateJwtOfCredential(presentation); Assertions.assertFalse(response.valid); } @@ -181,7 +180,7 @@ public void testValidationFailureOfCredentialWitInvalidExpirationDateInSecondCre @Test @SneakyThrows void testValidationFailureOfPresentationPayloadManipulation() { - Map presentation = createPresentationJwt(membershipCredential_1, tenant_1); + Map presentation = createPresentationJwt(vc_1, tenant_1); String jwt = (String) presentation.get(StringPool.VP); String payload = jwt.split("\\.")[1]; @@ -196,7 +195,7 @@ void testValidationFailureOfPresentationPayloadManipulation() { .context(List.of(VerifiablePresentation.DEFAULT_CONTEXT)) .id(URI.create("did:test:" + UUID.randomUUID())) .type(List.of(VerifiablePresentationType.VERIFIABLE_PRESENTATION)) - .verifiableCredentials(List.of(membershipCredential_2)) + .verifiableCredentials(List.of(vc_2)) .build(); payloadMap.put("vp", newPresentation); String newPayloadJson = OBJECT_MAPPER.writeValueAsString(payloadMap); diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/VerifiableCredentialIssuerEqualProofSignerTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/VerifiableCredentialIssuerEqualProofSignerTest.java similarity index 80% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/VerifiableCredentialIssuerEqualProofSignerTest.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/VerifiableCredentialIssuerEqualProofSignerTest.java index 428381346..4537be004 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/VerifiableCredentialIssuerEqualProofSignerTest.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/VerifiableCredentialIssuerEqualProofSignerTest.java @@ -1,6 +1,26 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + package org.eclipse.tractusx.managedidentitywallets.vc; -import com.fasterxml.jackson.core.JsonProcessingException; import lombok.SneakyThrows; import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; @@ -11,7 +31,7 @@ import org.eclipse.tractusx.managedidentitywallets.service.PresentationService; import org.eclipse.tractusx.managedidentitywallets.service.WalletKeyService; import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; -import org.eclipse.tractusx.ssi.lib.crypt.x21559.x21559PrivateKey; +import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519PrivateKey; import org.eclipse.tractusx.ssi.lib.model.proof.jws.JWSSignature2020; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialBuilder; @@ -33,6 +53,8 @@ import java.util.Map; import java.util.UUID; +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; + @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { ManagedIdentityWalletsApplication.class }) @ContextConfiguration(initializers = { TestContextInitializer.class }) public class VerifiableCredentialIssuerEqualProofSignerTest { @@ -56,12 +78,14 @@ public class VerifiableCredentialIssuerEqualProofSignerTest { @Test public void test() { var bpn1 = "BPNL000000000FOO"; - var response1 = TestUtils.createWallet(bpn1, "did:web:localhost%3A8080:BPNL000000000FOO", restTemplate, miwSettings.authorityWalletBpn()); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn1; + var response1 = TestUtils.createWallet(bpn1, "did:web:localhost%3A8080:BPNL000000000FOO", restTemplate, miwSettings.authorityWalletBpn(), defaultLocation); Assertions.assertTrue(response1.getStatusCode().is2xxSuccessful(), "Wallet 1 creation failed. " + response1.getBody()); var wallet1 = commonService.getWalletByIdentifier(bpn1); var bpn2 = "BPNL000000000BAR"; - var response2 = TestUtils.createWallet(bpn2, "did:web:localhost%3A8080:BPNL000000000BAR", restTemplate, miwSettings.authorityWalletBpn()); + String defaultLocation2 = miwSettings.host() + COLON_SEPARATOR + bpn2; + var response2 = TestUtils.createWallet(bpn2, "did:web:localhost%3A8080:BPNL000000000BAR", restTemplate, miwSettings.authorityWalletBpn(), defaultLocation2); Assertions.assertTrue(response2.getStatusCode().is2xxSuccessful(), "Wallet 2 creation failed. " + response2.getBody()); var wallet2 = commonService.getWalletByIdentifier(bpn2); @@ -84,7 +108,7 @@ public void test() { } @SneakyThrows - private VerifiableCredential issueVC(String issuerDid, Wallet signerWallet) throws JsonProcessingException { + private VerifiableCredential issueVC(String issuerDid, Wallet signerWallet) { List contexts = new ArrayList(); contexts.add(URI.create("https://www.w3.org/2018/credentials/v1")); // if the credential does not contain the JWS proof-context add it @@ -107,10 +131,10 @@ private VerifiableCredential issueVC(String issuerDid, Wallet signerWallet) thro LinkedDataProofGenerator generator = LinkedDataProofGenerator.newInstance(SignatureType.JWS); URI verificationMethod = signerWallet.getDidDocument().getVerificationMethods().get(0).getId(); - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifierAsBytes(signerWallet.getId()); + byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdAsBytes(signerWallet.getId(), signerWallet.getAlgorithm()); JWSSignature2020 proof = - (JWSSignature2020) generator.createProof(builder.build(), verificationMethod, new x21559PrivateKey(privateKeyBytes)); + new JWSSignature2020(generator.createProof(builder.build(), verificationMethod, new X25519PrivateKey(privateKeyBytes))); //Adding Proof to VC builder.proof(proof); diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vp/PresentationServiceTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vp/PresentationServiceTest.java new file mode 100644 index 000000000..31768e042 --- /dev/null +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vp/PresentationServiceTest.java @@ -0,0 +1,234 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,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 + * ****************************************************************************** + */ + +package org.eclipse.tractusx.managedidentitywallets.vp; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.JWTParser; +import com.nimbusds.jwt.SignedJWT; +import lombok.SneakyThrows; +import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; +import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; +import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; +import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.JtiRecord; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.JtiRepository; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; +import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; +import org.eclipse.tractusx.managedidentitywallets.exception.MissingVcTypesException; +import org.eclipse.tractusx.managedidentitywallets.exception.PermissionViolationException; +import org.eclipse.tractusx.managedidentitywallets.service.IssuersCredentialService; +import org.eclipse.tractusx.managedidentitywallets.service.PresentationService; +import org.eclipse.tractusx.managedidentitywallets.utils.AuthenticationUtils; +import org.eclipse.tractusx.managedidentitywallets.utils.TestConstants; +import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; +import org.eclipse.tractusx.ssi.lib.model.verifiable.Verifiable; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; +import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentation; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.ContextConfiguration; + +import java.util.Map; +import java.util.UUID; + +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.BPN_CREDENTIAL_READ; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.BPN_CREDENTIAL_WRITE; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.DID_BPN_1; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.EXP_VALID_DATE; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.IAT_VALID_DATE; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.INVALID_CREDENTIAL_READ; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.JWK_INNER; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestConstants.VERIFIABLE_PRESENTATION; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestUtils.buildClaimsSet; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestUtils.buildJWTToken; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestUtils.createWallet; +import static org.eclipse.tractusx.managedidentitywallets.utils.TestUtils.generateUuid; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { + ManagedIdentityWalletsApplication.class }) +@ContextConfiguration(initializers = { TestContextInitializer.class }) +class PresentationServiceTest { + + @Autowired + private MIWSettings miwSettings; + + @Autowired + private PresentationService presentationService; + + @Autowired + private TestRestTemplate restTemplate; + + @Autowired + private JtiRepository jtiRepository; + + @Autowired + private IssuersCredentialService issuersCredentialService; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private WalletRepository walletRepository; + + @SneakyThrows + @Test + void createPresentation200ResponseAsJWT() { + boolean asJwt = true; + String bpn = TestUtils.getRandomBpmNumber(); + String did = generateWalletAndGetDid(bpn); + String jtiValue = generateUuid(); + String accessToken = generateAccessToken(did, did, did, BPN_CREDENTIAL_READ, jtiValue); + JtiRecord jtiRecord = buildJti(jtiValue, false); + jtiRepository.save(jtiRecord); + + //issue BPN vc + TestUtils.issueCustomVCUsingBaseWallet(bpn, did, miwSettings.authorityWalletDid(), StringPool.BPN_CREDENTIAL, + AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()), miwSettings, objectMapper, restTemplate); + + Map presentation = presentationService.createVpWithRequiredScopes(SignedJWT.parse(accessToken), asJwt); + String vpAsJwt = String.valueOf(presentation.get(VERIFIABLE_PRESENTATION)); + JWT jwt = JWTParser.parse(vpAsJwt); + + Assertions.assertNotNull(presentation); + Assertions.assertEquals(did, jwt.getJWTClaimsSet().getSubject()); + Assertions.assertEquals(did, jwt.getJWTClaimsSet().getIssuer()); + } + + @SneakyThrows + @Test + void createPresentation200ResponseAsJsonLD() { + boolean asJwt = false; + String bpn = TestUtils.getRandomBpmNumber(); + String did = generateWalletAndGetDid(bpn); + String jtiValue = generateUuid(); + String accessToken = generateAccessToken(did, did, did, BPN_CREDENTIAL_READ, jtiValue); + JtiRecord jtiRecord = buildJti(jtiValue, false); + jtiRepository.save(jtiRecord); + + //issue BPN vc + TestUtils.issueCustomVCUsingBaseWallet(bpn, did, miwSettings.authorityWalletDid(), StringPool.BPN_CREDENTIAL, + AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()), miwSettings, objectMapper, restTemplate); + + Map presentation = presentationService.createVpWithRequiredScopes(SignedJWT.parse(accessToken), asJwt); + Assertions.assertNotNull(presentation); + + VerifiablePresentation vp = (VerifiablePresentation) presentation.get(VERIFIABLE_PRESENTATION); + Assertions.assertNotNull(vp.getVerifiableCredentials()); + VerifiableCredential verifiableCredential = vp.getVerifiableCredentials().get(0); + VerifiableCredentialSubject verifiableCredentialSubject = verifiableCredential.getCredentialSubject().get(0); + Assertions.assertNotNull(verifiableCredentialSubject); + Assertions.assertEquals(bpn, verifiableCredentialSubject.get(StringPool.BPN)); + Assertions.assertEquals(did, verifiableCredentialSubject.get(Verifiable.ID)); + } + + @SneakyThrows + @Test + void createPresentation200ResponseNoJtiRecord() { + boolean asJwt = true; + String bpn = TestUtils.getRandomBpmNumber(); + String did = generateWalletAndGetDid(bpn); + String jtiValue = generateUuid(); + String accessToken = generateAccessToken(did, did, did, BPN_CREDENTIAL_READ, jtiValue); + + //issue BPN vc + TestUtils.issueCustomVCUsingBaseWallet(bpn, did, miwSettings.authorityWalletDid(), StringPool.BPN_CREDENTIAL, + AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()), miwSettings, objectMapper, restTemplate); + + + Map presentation = presentationService.createVpWithRequiredScopes(SignedJWT.parse(accessToken), asJwt); + String vpAsJwt = String.valueOf(presentation.get(VERIFIABLE_PRESENTATION)); + JWT jwt = JWTParser.parse(vpAsJwt); + + Assertions.assertNotNull(presentation); + Assertions.assertEquals(did, jwt.getJWTClaimsSet().getSubject()); + Assertions.assertEquals(did, jwt.getJWTClaimsSet().getIssuer()); + } + + @Test + void createPresentationIncorrectVcTypeResponse() { + boolean asJwt = true; + String bpn = TestUtils.getRandomBpmNumber(); + String did = generateWalletAndGetDid(bpn); + String jtiValue = generateUuid(); + String accessToken = generateAccessToken(did, did, did, INVALID_CREDENTIAL_READ, jtiValue); + JtiRecord jtiRecord = buildJti(jtiValue, false); + jtiRepository.save(jtiRecord); + + Assertions.assertThrows(MissingVcTypesException.class, () -> + presentationService.createVpWithRequiredScopes(SignedJWT.parse(accessToken), asJwt)); + } + + @Test + void createPresentationIncorrectRightsRequested() { + boolean asJwt = true; + String jtiValue = generateUuid(); + String accessToken = generateAccessToken(DID_BPN_1, DID_BPN_1, DID_BPN_1, BPN_CREDENTIAL_WRITE, jtiValue); + JtiRecord jtiRecord = buildJti(jtiValue, false); + jtiRepository.save(jtiRecord); + + Assertions.assertThrows(PermissionViolationException.class, () -> + presentationService.createVpWithRequiredScopes(SignedJWT.parse(accessToken), asJwt)); + } + + @Test + void createPresentationIncorrectJtiAlreadyUsed() { + boolean asJwt = false; + String bpn = TestUtils.getRandomBpmNumber(); + String did = generateWalletAndGetDid(bpn); + String jtiValue = generateUuid(); + String accessToken = generateAccessToken(did, did, did, BPN_CREDENTIAL_READ, jtiValue); + JtiRecord jtiRecord = buildJti(jtiValue, true); + jtiRepository.save(jtiRecord); + + BadDataException ex = Assertions.assertThrows(BadDataException.class, () -> presentationService.createVpWithRequiredScopes(SignedJWT.parse(accessToken), asJwt)); + Assertions.assertEquals("The token was already used", ex.getMessage()); + } + + @SneakyThrows + private String generateWalletAndGetDid(String bpn) { + String baseBpn = miwSettings.authorityWalletBpn(); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + ResponseEntity createWalletResponse = createWallet(bpn, "name", restTemplate, baseBpn, defaultLocation); + Wallet wallet = TestUtils.getWalletFromString(createWalletResponse.getBody()); + Wallet issuerWallet = walletRepository.getByBpn(miwSettings.authorityWalletBpn()); + return wallet.getDid(); + } + + private JtiRecord buildJti(String value, boolean isUsed) { + return JtiRecord.builder().jti(UUID.fromString(value)).isUsedStatus(isUsed).build(); + } + + @SneakyThrows + private String generateAccessToken(String issUrl, String sub, String aud, String scope, String jwt) { + JWTClaimsSet innerSet = buildClaimsSet(issUrl, sub, aud, TestConstants.NONCE, scope, EXP_VALID_DATE, IAT_VALID_DATE, jwt); + return buildJWTToken(JWK_INNER, innerSet); + } +} diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vp/PresentationTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vp/PresentationTest.java similarity index 85% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/vp/PresentationTest.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vp/PresentationTest.java index fec437455..6b9e55b2e 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vp/PresentationTest.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/vp/PresentationTest.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -25,28 +25,29 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; +import lombok.SneakyThrows; import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; -import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType; import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; import org.eclipse.tractusx.managedidentitywallets.controller.PresentationController; import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; +import org.eclipse.tractusx.managedidentitywallets.service.IssuersCredentialService; import org.eclipse.tractusx.managedidentitywallets.utils.AuthenticationUtils; import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; import org.eclipse.tractusx.ssi.lib.did.resolver.DidResolver; import org.eclipse.tractusx.ssi.lib.did.web.DidWebFactory; -import org.eclipse.tractusx.ssi.lib.exception.DidDocumentResolverNotRegisteredException; -import org.eclipse.tractusx.ssi.lib.exception.JwtException; import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtVerifier; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialBuilder; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; import org.jetbrains.annotations.NotNull; +import org.json.JSONException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; @@ -54,16 +55,29 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.*; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.oauth2.jwt.JwtException; import org.springframework.test.context.ContextConfiguration; import java.net.URI; import java.text.ParseException; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {ManagedIdentityWalletsApplication.class}) -@ContextConfiguration(initializers = {TestContextInitializer.class}) +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { ManagedIdentityWalletsApplication.class }) +@ContextConfiguration(initializers = { TestContextInitializer.class }) class PresentationTest { @Autowired @@ -81,9 +95,15 @@ class PresentationTest { @Autowired private MIWSettings miwSettings; + @Autowired + private IssuersCredentialService issuersCredentialService; + + @Autowired + private WalletRepository walletRepository; + @Test - void validateVPAssJsonLd400() throws JsonProcessingException { + void validateVPAssJsonLd400() throws JsonProcessingException, JSONException { //create VP String bpn = TestUtils.getRandomBpmNumber(); String audience = "companyA"; @@ -100,7 +120,7 @@ void validateVPAssJsonLd400() throws JsonProcessingException { @Test - void validateVPAsJwt() throws JsonProcessingException { + void validateVPAsJwt() throws JsonProcessingException, JSONException { String bpn = TestUtils.getRandomBpmNumber(); String audience = "companyA"; ResponseEntity vpResponse = createBpnVCAsJwt(bpn, audience); @@ -117,7 +137,8 @@ void validateVPAsJwt() throws JsonProcessingException { } @Test - void validateVPAsJwtWithInvalidSignatureAndInValidAudienceAndExpiryDateValidation() throws JsonProcessingException, DidDocumentResolverNotRegisteredException, JwtException, InterruptedException { + @SneakyThrows + void validateVPAsJwtWithInvalidSignatureAndInValidAudienceAndExpiryDateValidation() { //create VP String bpn = TestUtils.getRandomBpmNumber(); String audience = "companyA"; @@ -146,7 +167,7 @@ void validateVPAsJwtWithInvalidSignatureAndInValidAudienceAndExpiryDateValidatio } @Test - void validateVPAsJwtWithValidAudienceAndDateValidation() throws JsonProcessingException { + void validateVPAsJwtWithValidAudienceAndDateValidation() throws JsonProcessingException, JSONException { //create VP String bpn = TestUtils.getRandomBpmNumber(); String audience = "companyA"; @@ -163,7 +184,7 @@ void validateVPAsJwtWithValidAudienceAndDateValidation() throws JsonProcessingEx } @Test - void validateVPAsJwtWithInValidVCDateValidation() throws JsonProcessingException { + void validateVPAsJwtWithInValidVCDateValidation() throws JsonProcessingException, JSONException { //create VP String bpn = TestUtils.getRandomBpmNumber(); String audience = "companyA"; @@ -181,7 +202,7 @@ void validateVPAsJwtWithInValidVCDateValidation() throws JsonProcessingException } @Test - void createPresentationAsJWT201() throws JsonProcessingException, ParseException { + void createPresentationAsJWT201() throws JsonProcessingException, ParseException, JSONException { String bpn = TestUtils.getRandomBpmNumber(); String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); String audience = "companyA"; @@ -196,7 +217,7 @@ void createPresentationAsJWT201() throws JsonProcessingException, ParseException Assertions.assertEquals(iss, did); } - private ResponseEntity createBpnVCAsJwt(String bpn, String audience) throws JsonProcessingException { + private ResponseEntity createBpnVCAsJwt(String bpn, String audience) throws JsonProcessingException, JSONException { Map request = getIssueVPRequest(bpn); HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); @@ -210,7 +231,7 @@ private ResponseEntity createBpnVCAsJwt(String bpn, String audience) throws @Test - void createPresentationAsJsonLD201() throws JsonProcessingException { + void createPresentationAsJsonLD201() throws JsonProcessingException, JSONException { String bpn = TestUtils.getRandomBpmNumber(); String didWeb = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); @@ -228,7 +249,7 @@ void createPresentationAsJsonLD201() throws JsonProcessingException { } @Test - void createPresentationWithInvalidBPNAccess403() throws JsonProcessingException { + void createPresentationWithInvalidBPNAccess403() throws JsonProcessingException, JSONException { String bpn = TestUtils.getRandomBpmNumber(); String didWeb = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); @@ -244,14 +265,22 @@ void createPresentationWithInvalidBPNAccess403() throws JsonProcessingException } @NotNull - private Map getIssueVPRequest(String bpn) throws JsonProcessingException { + private Map getIssueVPRequest(String bpn) throws JsonProcessingException, JSONException { String baseBpn = miwSettings.authorityWalletBpn(); - ResponseEntity response = TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + ResponseEntity response = TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn, defaultLocation); Assertions.assertEquals(response.getStatusCode().value(), HttpStatus.CREATED.value()); Wallet wallet = TestUtils.getWalletFromString(response.getBody()); //get BPN credentials - List credentials = holdersCredentialRepository.getByHolderDidAndType(wallet.getDid(), MIWVerifiableCredentialType.BPN_CREDENTIAL); + List vcs = new ArrayList<>(); + List typesOfVcs = List.of("Type1", "Type2", "Type3"); + + typesOfVcs.forEach(type -> { + VerifiableCredential verifiableCredential = TestUtils.issueCustomVCUsingBaseWallet(wallet.getBpn(), wallet.getDid(), miwSettings.authorityWalletDid(), type, AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()), miwSettings, objectMapper, restTemplate); + vcs.add(verifiableCredential); + }); + List credentials = holdersCredentialRepository.getByHolderDid(wallet.getDid()); Map map = objectMapper.readValue(credentials.get(0).getData().toJson(), Map.class); @@ -263,9 +292,10 @@ private Map getIssueVPRequest(String bpn) throws JsonProcessingE } @NotNull - private ResponseEntity getIssueVPRequestWithShortExpiry(String bpn, String audience) throws JsonProcessingException { + private ResponseEntity getIssueVPRequestWithShortExpiry(String bpn, String audience) throws JsonProcessingException, JSONException { String baseBpn = miwSettings.authorityWalletBpn(); - ResponseEntity response = TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + ResponseEntity response = TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn, defaultLocation); Assertions.assertEquals(response.getStatusCode().value(), HttpStatus.CREATED.value()); Wallet wallet = TestUtils.getWalletFromString(response.getBody()); @@ -292,6 +322,7 @@ private ResponseEntity getIssueVPRequestWithShortExpiry(String bpn, String return vpResponse; } + private ResponseEntity issueVC(String bpn, String holderDid, String issuerDid, String type, HttpHeaders headers, List contexts, Instant expiry) throws JsonProcessingException { // Create VC without proof //VC Bulider @@ -299,9 +330,8 @@ private ResponseEntity issueVC(String bpn, String holderDid, String issu new VerifiableCredentialBuilder(); //VC Subject - VerifiableCredentialSubject verifiableCredentialSubject = new VerifiableCredentialSubject(Map.of(StringPool.TYPE, MIWVerifiableCredentialType.BPN_CREDENTIAL, - StringPool.ID, holderDid, - StringPool.BPN, bpn)); + VerifiableCredentialSubject verifiableCredentialSubject = new VerifiableCredentialSubject(Map.of(StringPool.TYPE, "CustomType", + StringPool.ID, holderDid)); //Using Builder VerifiableCredential credentialWithoutProof = diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java similarity index 86% rename from src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java rename to miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java index ae034efee..8371a0fea 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java @@ -1,6 +1,6 @@ /* * ******************************************************************************* - * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -26,9 +26,8 @@ import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; -import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType; import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; -import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; @@ -36,12 +35,11 @@ import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; import org.eclipse.tractusx.managedidentitywallets.dto.CreateWalletRequest; +import org.eclipse.tractusx.managedidentitywallets.service.IssuersCredentialService; import org.eclipse.tractusx.managedidentitywallets.service.WalletService; import org.eclipse.tractusx.managedidentitywallets.utils.AuthenticationUtils; import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; import org.eclipse.tractusx.ssi.lib.did.web.DidWebFactory; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -53,7 +51,11 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.*; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; import java.net.URI; @@ -62,9 +64,11 @@ import java.util.Map; import java.util.Objects; +import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {ManagedIdentityWalletsApplication.class}) -@ContextConfiguration(initializers = {TestContextInitializer.class}) + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { ManagedIdentityWalletsApplication.class }) +@ContextConfiguration(initializers = { TestContextInitializer.class }) class WalletTest { @Autowired @@ -88,6 +92,9 @@ class WalletTest { @Autowired private WalletService walletService; + @Autowired + private IssuersCredentialService issuersCredentialService; + @Test void createDuplicateAuthorityWalletTest() { @@ -103,11 +110,6 @@ void authorityWalletExistTest() { Assertions.assertEquals(wallet.getBpn(), miwSettings.authorityWalletBpn()); Assertions.assertEquals(wallet.getName(), miwSettings.authorityWalletName()); Assertions.assertNotNull(wallet.getDidDocument()); - - //check BPN credentials issued for authority wallet - List vcs = holdersCredentialRepository.getByHolderDidAndType(wallet.getDid(), MIWVerifiableCredentialType.BPN_CREDENTIAL); - Assertions.assertFalse(vcs.isEmpty()); - Assertions.assertTrue(vcs.get(0).isSelfIssued()); } @@ -117,7 +119,7 @@ void createWalletTest403() { String name = "Sample Wallet"; HttpHeaders headers = AuthenticationUtils.getInvalidUserHttpHeaders(); - CreateWalletRequest request = CreateWalletRequest.builder().bpn(bpn).name(name).build(); + CreateWalletRequest request = CreateWalletRequest.builder().businessPartnerNumber(bpn).companyName(name).build(); HttpEntity entity = new HttpEntity<>(request, headers); @@ -131,7 +133,8 @@ void createWalletTestWithUserToken403() { String name = "Sample Wallet"; HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); - CreateWalletRequest request = CreateWalletRequest.builder().bpn(bpn).name(name).build(); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + CreateWalletRequest request = CreateWalletRequest.builder().businessPartnerNumber(bpn).companyName(name).didUrl(defaultLocation).build(); HttpEntity entity = new HttpEntity<>(request, headers); @@ -141,12 +144,13 @@ void createWalletTestWithUserToken403() { @Test @DisplayName("Create wallet with invalid BPN, it should return 400 ") - void createWalletWithInvalidBPNTest400() throws JsonProcessingException, JSONException { + void createWalletWithInvalidBPNTest400() throws JSONException { String bpn = "invalid bpn"; String name = "Sample Wallet"; String baseBpn = miwSettings.authorityWalletBpn(); - ResponseEntity response = TestUtils.createWallet(bpn, name, restTemplate, baseBpn); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + ResponseEntity response = TestUtils.createWallet(bpn, name, restTemplate, baseBpn, defaultLocation); Assertions.assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusCode().value()); } @@ -157,12 +161,14 @@ void createWalletTest201() throws JsonProcessingException, JSONException { String name = "Sample Wallet"; String baseBpn = miwSettings.authorityWalletBpn(); - ResponseEntity response = TestUtils.createWallet(bpn, name, restTemplate, baseBpn); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + ResponseEntity response = TestUtils.createWallet(bpn, name, restTemplate, baseBpn, defaultLocation); Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); Wallet wallet = TestUtils.getWalletFromString(response.getBody()); Assertions.assertNotNull(response.getBody()); Assertions.assertNotNull(wallet.getDidDocument()); + Assertions.assertEquals(2, wallet.getDidDocument().getVerificationMethods().size()); List context = wallet.getDidDocument().getContext(); miwSettings.didDocumentContextUrls().forEach(uri -> { Assertions.assertTrue(context.contains(uri)); @@ -174,7 +180,8 @@ void createWalletTest201() throws JsonProcessingException, JSONException { Assertions.assertEquals(walletFromDB.getBpn(), bpn); Assertions.assertEquals(walletFromDB.getName(), name); Assertions.assertNotNull(walletFromDB); - WalletKey walletKey = walletKeyRepository.getByWalletId(walletFromDB.getId()); + // walletKey1 + WalletKey walletKey = walletKeyRepository.getByWalletIdAndAlgorithm(walletFromDB.getId(), walletFromDB.getAlgorithm()); Assertions.assertNotNull(walletKey); Assertions.assertEquals(walletFromDB.getBpn(), bpn); @@ -183,7 +190,16 @@ void createWalletTest201() throws JsonProcessingException, JSONException { Assertions.assertNotNull(walletKey.getKeyId()); Assertions.assertEquals(walletKey.getKeyId(), keyId); - //check if BPN and Summary credentials is issued + //walletKey2 + WalletKey walletKey2 = walletKeyRepository.getByWalletIdAndAlgorithm(walletFromDB.getId(), SupportedAlgorithms.ES256K.name()); + Assertions.assertNotNull(walletKey2); + + //validate keyId2 + String keyId2 = wallet.getDidDocument().getVerificationMethods().get(1).getId().toString().split("#")[1]; + Assertions.assertNotNull(walletKey2.getKeyId()); + Assertions.assertEquals(walletKey2.getKeyId(), keyId2); + + //check that BPN and Summary credentials are not automatically issued on creation HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); HttpEntity entity = new HttpEntity<>(headers); @@ -191,22 +207,7 @@ void createWalletTest201() throws JsonProcessingException, JSONException { ResponseEntity getWalletResponse = restTemplate.exchange(RestURI.API_WALLETS_IDENTIFIER + "?withCredentials={withCredentials}", HttpMethod.GET, entity, String.class, bpn, "true"); Assertions.assertEquals(getWalletResponse.getStatusCode().value(), HttpStatus.OK.value()); Wallet body = TestUtils.getWalletFromString(getWalletResponse.getBody()); - Assertions.assertEquals(2, body.getVerifiableCredentials().size()); - - VerifiableCredential verifiableCredential = body.getVerifiableCredentials().stream() - .filter(vp -> vp.getTypes().contains(MIWVerifiableCredentialType.BPN_CREDENTIAL)) - .findFirst() - .orElse(null); - Assertions.assertEquals(verifiableCredential.getCredentialSubject().get(0).get(StringPool.ID), wallet.getDid()); - Assertions.assertEquals(verifiableCredential.getCredentialSubject().get(0).get(StringPool.BPN), wallet.getBpn()); - Assertions.assertEquals(MIWVerifiableCredentialType.BPN_CREDENTIAL, verifiableCredential.getCredentialSubject().get(0).get(StringPool.TYPE)); - - VerifiableCredential summaryVerifiableCredential = body.getVerifiableCredentials().stream() - .filter(vc -> vc.getTypes().contains(MIWVerifiableCredentialType.SUMMARY_CREDENTIAL)).findFirst() - .orElse(null); - VerifiableCredentialSubject subject = summaryVerifiableCredential.getCredentialSubject().get(0); - List list = (List) subject.get(StringPool.ITEMS); - Assertions.assertTrue(list.contains(MIWVerifiableCredentialType.BPN_CREDENTIAL)); + Assertions.assertEquals(0, body.getVerifiableCredentials().size()); } @@ -217,7 +218,8 @@ void storeCredentialsTest201() throws JsonProcessingException { String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); String baseBpn = miwSettings.authorityWalletBpn(); - TestUtils.createWallet(bpn, "name", restTemplate, baseBpn); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + TestUtils.createWallet(bpn, "name", restTemplate, baseBpn, defaultLocation); ResponseEntity response = storeCredential(bpn, did); @@ -294,7 +296,8 @@ void storeCredentialsWithDifferentHolder403() throws JsonProcessingException { String bpn = TestUtils.getRandomBpmNumber(); String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); String baseBpn = miwSettings.authorityWalletBpn(); - TestUtils.createWallet(bpn, "name", restTemplate, baseBpn); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + TestUtils.createWallet(bpn, "name", restTemplate, baseBpn, defaultLocation); HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders("Some random pbn"); @@ -305,19 +308,20 @@ void storeCredentialsWithDifferentHolder403() throws JsonProcessingException { } @Test - void createWalletWithDuplicateBpn409() throws JsonProcessingException { + void createWalletWithDuplicateBpn409() throws JsonProcessingException, JSONException { String bpn = TestUtils.getRandomBpmNumber(); String name = "Sample Wallet"; String baseBpn = miwSettings.authorityWalletBpn(); //save wallet - ResponseEntity response = TestUtils.createWallet(bpn, name, restTemplate, baseBpn); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + ResponseEntity response = TestUtils.createWallet(bpn, name, restTemplate, baseBpn, defaultLocation); TestUtils.getWalletFromString(response.getBody()); Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); //try with again with same BPN - ResponseEntity response1 = TestUtils.createWallet(bpn, name, restTemplate, baseBpn); + ResponseEntity response1 = TestUtils.createWallet(bpn, name, restTemplate, baseBpn, defaultLocation); Assertions.assertEquals(HttpStatus.CONFLICT.value(), response1.getStatusCode().value()); } @@ -338,7 +342,8 @@ void getWalletByIdentifierWithInvalidBPNTest403() { String bpn = TestUtils.getRandomBpmNumber(); String baseBpn = miwSettings.authorityWalletBpn(); - TestUtils.createWallet(bpn, "sample name", restTemplate, baseBpn); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + TestUtils.createWallet(bpn, "sample name", restTemplate, baseBpn, defaultLocation); //create token with different BPN HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders("invalid BPN"); @@ -350,13 +355,14 @@ void getWalletByIdentifierWithInvalidBPNTest403() { } @Test - void getWalletByIdentifierBPNTest200() throws JsonProcessingException { + void getWalletByIdentifierBPNTest200() throws JsonProcessingException, JSONException { String bpn = TestUtils.getRandomBpmNumber(); String name = "Sample Name"; String baseBpn = miwSettings.authorityWalletBpn(); //Create entry - Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, name, restTemplate, baseBpn).getBody()); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, name, restTemplate, baseBpn, defaultLocation).getBody()); //get wallet without credentials HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); @@ -373,14 +379,15 @@ void getWalletByIdentifierBPNTest200() throws JsonProcessingException { @Test - void getWalletByIdentifierBPNWithCredentialsTest200() throws JsonProcessingException { + void getWalletByIdentifierBPNWithCredentialsTest200() throws JsonProcessingException, JSONException { String bpn = TestUtils.getRandomBpmNumber(); String name = "Sample Name"; String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); String baseBpn = miwSettings.authorityWalletBpn(); //Create entry - Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, name, restTemplate, baseBpn).getBody()); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, name, restTemplate, baseBpn, defaultLocation).getBody()); //store credentials ResponseEntity response = storeCredential(bpn, did); @@ -396,20 +403,21 @@ void getWalletByIdentifierBPNWithCredentialsTest200() throws JsonProcessingExcep Wallet body = TestUtils.getWalletFromString(getWalletResponse.getBody()); Assertions.assertEquals(HttpStatus.OK.value(), getWalletResponse.getStatusCode().value()); Assertions.assertNotNull(getWalletResponse.getBody()); - Assertions.assertEquals(3, body.getVerifiableCredentials().size()); //BPN VC + Summery VC + Stored VC + Assertions.assertEquals(1, body.getVerifiableCredentials().size()); //BPN VC + Summery VC + Stored VC Assertions.assertEquals(body.getBpn(), bpn); } @Test @Disabled("the endpoint has an issue that prevents resolving did with a port number") - void getWalletByIdentifierDidTest200() throws JsonProcessingException { + void getWalletByIdentifierDidTest200() throws JsonProcessingException, JSONException { String bpn = TestUtils.getRandomBpmNumber(); String name = "Sample Name"; String baseBpn = miwSettings.authorityWalletBpn(); //Create entry - Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, name, restTemplate, baseBpn).getBody()); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, name, restTemplate, baseBpn, defaultLocation).getBody()); HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); HttpEntity entity = new HttpEntity<>(headers); @@ -447,13 +455,14 @@ void getWallets403() { @Test - void getWallets200() throws JsonProcessingException { + void getWallets200() throws JsonProcessingException, JSONException { String bpn = TestUtils.getRandomBpmNumber(); String name = "Sample Name"; String baseBpn = miwSettings.authorityWalletBpn(); //Create entry - TestUtils.createWallet(bpn, name, restTemplate, baseBpn); + String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; + TestUtils.createWallet(bpn, name, restTemplate, baseBpn, defaultLocation); HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(); HttpEntity entity = new HttpEntity<>(headers); @@ -519,7 +528,7 @@ private ResponseEntity storeCredential(String bpn, String did) throws JsonP } - private List getWalletsFromString(String body) throws JsonProcessingException { + private List getWalletsFromString(String body) throws JsonProcessingException, JSONException { List walletList = new ArrayList<>(); JSONArray array = new JSONArray(new JSONObject(body).getJSONArray("content")); diff --git a/miw/src/test/resources/application-test.yaml b/miw/src/test/resources/application-test.yaml new file mode 100644 index 000000000..6bd0a679d --- /dev/null +++ b/miw/src/test/resources/application-test.yaml @@ -0,0 +1,36 @@ +################################################################################ +# Copyright (c) 2021,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 +################################################################################ + +spring: + main: + allow-circular-references: true +miw: + host: localhost + encryptionKey: ohb7echohNe3zoo1so0eiC2phue4liux + authorityWalletDid: did:web:localhost:BPNL000000000000 + authorityWalletName: Catena-x + authorityWalletBpn: BPNL000000000000 + security: + enabled: true + realm: miw_test + clientId: miw_private_client + +debug: true + + diff --git a/miw/src/test/resources/credential-subject-2.json b/miw/src/test/resources/credential-subject-2.json new file mode 100644 index 000000000..6b191fdaa --- /dev/null +++ b/miw/src/test/resources/credential-subject-2.json @@ -0,0 +1,18 @@ +{ + "id": "https://localhost/.well-known/participant.json", + "type": "gx:DummyParticipant", + "gx:legalName": "Sample Company", + "gx:legalRegistrationNumber": + { + "gx:taxID": "113123123" + }, + "gx:headquarterAddress": + { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "gx:legalAddress": + { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "gx-terms-and-conditions:gaiaxTermsAndConditions": "70c1d713215f95191a11d38fe2341faed27d19e083917bc8732ca4fea4976700" +} \ No newline at end of file diff --git a/miw/src/test/resources/credential-subject.json b/miw/src/test/resources/credential-subject.json new file mode 100644 index 000000000..d0327d670 --- /dev/null +++ b/miw/src/test/resources/credential-subject.json @@ -0,0 +1,18 @@ +{ + "id": "https://localhost/.well-known/participant.json", + "type": "gx:LegalParticipant", + "gx:legalName": "Sample Company", + "gx:legalRegistrationNumber": + { + "gx:taxID": "113123123" + }, + "gx:headquarterAddress": + { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "gx:legalAddress": + { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "gx-terms-and-conditions:gaiaxTermsAndConditions": "70c1d713215f95191a11d38fe2341faed27d19e083917bc8732ca4fea4976700" +} diff --git a/miw/src/test/resources/db/changelog/changes/add-signing-service-column.sql b/miw/src/test/resources/db/changelog/changes/add-signing-service-column.sql new file mode 100644 index 000000000..54ecd8d77 --- /dev/null +++ b/miw/src/test/resources/db/changelog/changes/add-signing-service-column.sql @@ -0,0 +1,5 @@ +--liquibase formatted sql +--changeset pmanaras:3 +ALTER TABLE public.wallet ADD signing_service_type VARCHAR(255) NOT NULL DEFAULT 'LOCAL'; + + diff --git a/miw/src/test/resources/db/changelog/changes/without-changes-init.sql b/miw/src/test/resources/db/changelog/changes/without-changes-init.sql new file mode 100644 index 000000000..236ab799c --- /dev/null +++ b/miw/src/test/resources/db/changelog/changes/without-changes-init.sql @@ -0,0 +1,82 @@ +--liquibase formatted sql + +--changeset nitin:1 +CREATE TABLE IF NOT EXISTS public.wallet +( + id bigserial NOT NULL, + name varchar(255) NOT NULL, + did varchar(255) NOT NULL, + bpn varchar(255) NOT NULL, + algorithm varchar(255) NOT NULL DEFAULT 'ED25519'::character varying, + did_document text NOT NULL, + created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at timestamp(6) NULL, + modified_from varchar(255) NULL, + CONSTRAINT uk_bpn UNIQUE (bpn), + CONSTRAINT uk_did UNIQUE (did), + CONSTRAINT wallet_pkey PRIMARY KEY (id), + CONSTRAINT wallet_fk FOREIGN KEY (modified_from) REFERENCES public.wallet (bpn) ON DELETE SET NULL +); +COMMENT ON TABLE public.wallet IS 'This table will store wallets'; + +CREATE TABLE IF NOT EXISTS public.wallet_key +( + id bigserial NOT NULL, + wallet_id bigserial NOT NULL, + vault_access_token varchar(1000) NOT NULL, + reference_key varchar(255) NOT NULL, + private_key text NOT NULL, + public_key text NOT NULL, + created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at timestamp(6) NULL, + modified_from varchar(255) NULL, + CONSTRAINT wallet_key_pkey PRIMARY KEY (id), + CONSTRAINT wallet_fk_2 FOREIGN KEY (wallet_id) REFERENCES public.wallet (id) ON DELETE CASCADE, + CONSTRAINT wallet_key_fk FOREIGN KEY (modified_from) REFERENCES public.wallet (bpn) ON DELETE CASCADE +); +COMMENT ON TABLE public.wallet_key IS 'This table will store key pair of wallets'; + + +CREATE TABLE IF NOT EXISTS public.issuers_credential +( + id bigserial NOT NULL, + holder_did varchar(255) NOT NULL, + issuer_did varchar(255) NOT NULL, + credential_id varchar(255) NOT NULL, + credential_data text NOT NULL, + credential_type varchar(255) NULL, + created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at timestamp(6) NULL, + modified_from varchar(255) NULL, + CONSTRAINT issuers_credential_pkey PRIMARY KEY (id), + CONSTRAINT issuers_credential_fk FOREIGN KEY (modified_from) REFERENCES public.wallet (bpn) ON DELETE SET NULL, + CONSTRAINT issuers_credential_holder_wallet_fk FOREIGN KEY (holder_did) REFERENCES public.wallet (did) ON DELETE CASCADE +); +COMMENT ON TABLE public.issuers_credential IS 'This table will store issuers credentials'; + + +CREATE TABLE IF NOT EXISTS public.holders_credential +( + id bigserial NOT NULL, + holder_did varchar(255) NOT NULL, + issuer_did varchar(255) NOT NULL, + credential_id varchar(255) NOT NULL, + credential_data text NOT NULL, + credential_type varchar(255) NULL, + is_self_issued bool NOT null default false, + is_stored bool NOT null default false, + created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at timestamp(6) NULL, + modified_from varchar(255) NULL, + CONSTRAINT holders_credential_pkey PRIMARY KEY (id), + CONSTRAINT holders_credential_fk FOREIGN KEY (modified_from) REFERENCES public.wallet (bpn) ON DELETE SET NULL, + CONSTRAINT holders_credential_holder_wallet_fk FOREIGN KEY (holder_did) REFERENCES public.wallet (did) ON DELETE CASCADE +); +COMMENT ON TABLE public.holders_credential IS 'This table will store holders credentials'; + +COMMENT ON COLUMN public.holders_credential.is_stored IS 'true is VC is stored using store VC api(Not issued by MIW)'; + +--changeset nitin:2 +ALTER TABLE public.wallet_key ADD key_id varchar(255) NULL; + + diff --git a/miw/src/test/resources/db/signing-service-migration-test/signing-service-changelog.xml b/miw/src/test/resources/db/signing-service-migration-test/signing-service-changelog.xml new file mode 100644 index 000000000..347a37e7b --- /dev/null +++ b/miw/src/test/resources/db/signing-service-migration-test/signing-service-changelog.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/miw/src/test/resources/db/signing-service-migration-test/without-changes.xml b/miw/src/test/resources/db/signing-service-migration-test/without-changes.xml new file mode 100644 index 000000000..171456e23 --- /dev/null +++ b/miw/src/test/resources/db/signing-service-migration-test/without-changes.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/src/test/resources/miw-test-realm.json b/miw/src/test/resources/miw-test-realm.json similarity index 100% rename from src/test/resources/miw-test-realm.json rename to miw/src/test/resources/miw-test-realm.json diff --git a/settings.gradle b/settings.gradle index 4e850ab19..b2cb866c2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,6 @@ rootProject.name = 'managedidentitywallets' +// docs: https://docs.gradle.org/current/userguide/multi_project_builds.html +// add sub-projects here, the path on disk and the name can be the same +// for example: +// include '' +include 'miw' diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/IssuersCredentialControllerApiDocs.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/IssuersCredentialControllerApiDocs.java deleted file mode 100644 index a137f6d85..000000000 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/apidocs/IssuersCredentialControllerApiDocs.java +++ /dev/null @@ -1,1106 +0,0 @@ -package org.eclipse.tractusx.managedidentitywallets.apidocs; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.ExampleObject; -import io.swagger.v3.oas.annotations.parameters.RequestBody; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import io.swagger.v3.oas.annotations.tags.Tag; - -public class IssuersCredentialControllerApiDocs { - /** - * The constant API_TAG_VERIFIABLE_CREDENTIAL_ISSUER. - */ - public static final String API_TAG_VERIFIABLE_CREDENTIAL_ISSUER = "Verifiable Credential - Issuer"; - /** - * The constant API_TAG_VERIFIABLE_CREDENTIAL_VALIDATION. - */ - public static final String API_TAG_VERIFIABLE_CREDENTIAL_VALIDATION = "Verifiable Credential - Validation"; - - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - @Tag(name = API_TAG_VERIFIABLE_CREDENTIAL_ISSUER) - @ApiResponses(value = { - @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "403", description = "The request could not be completed due to a forbidden access", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "500", description = "Any other internal server error", content = { - @Content(examples = { - @ExampleObject(name = "Internal server error", value = """ - { - "type": "about:blank", - "title": "Error Title", - "status": 500, - "detail": "Error Details", - "instance": "API endpoint", - "properties": { - "timestamp": 1689762476720 - } - } - """) - }) }), - @ApiResponse(responseCode = "400", description = "The input does not comply to the syntax requirements", content = { - @Content(examples = { - @ExampleObject(name = "Response in case of invalid data provided", value = """ - { - "type": "about:blank", - "title": "title", - "status": 400, - "detail": "details", - "instance": "API endpoint", - "properties": - { - "timestamp": 1689760833962, - "errors": - { - } - } - } - """) - }) - }), - @ApiResponse(responseCode = "200", description = "Issuer credential list", content = { - @Content(examples = { - @ExampleObject(name = "Issuer credential list", value = """ - { - "content": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#ae364f71-f054-4d91-b579-f001bcb3e59e", - "type": [ - "VerifiableCredential", - "BpnCredential" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T09:27:42Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "bpn": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "BpnCredential" - } - ], - "proof": { - "created": "2023-07-19T09:27:44Z", - "jws": "eyJhbGciOiJFZERTQSJ9..evDHQfW4EzJUt2HnS_WlmO8FFtywTGnwyywtCE7WP41my4Iscpqr4tbuVOqnZg85b4U8L3_ut8_pEONIhbExCQ", - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#" - } - }, - { - "type": [ - "VerifiableCredential", - "SummaryCredential" - ], - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/SummaryVC.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T09:11:39Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "contractTemplate": "https://public.catena-x.org/contracts/", - "holderIdentifier": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "items": [ - "BpnCredential" - ], - "type": "SummaryCredential" - } - ], - "proof": { - "created": "2023-07-19T09:11:41Z", - "jws": "eyJhbGciOiJFZERTQSJ9..YvoFhDip3TQAfZUIu0yc843oA4uGTg049dMFt_GoaMmPjiNB_B1EFOL-gDpwjIxTYNlGOO_CLp9qStbzlDTNBg", - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#" - } - }, - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", - "type": [ - "VerifiableCredential", - "BpnCredential" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T09:11:34Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "bpn": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "BpnCredential" - } - ], - "proof": { - "created": "2023-07-19T09:11:39Z", - "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#" - } - } - ], - "pageable": { - "sort": { - "empty": false, - "unsorted": false, - "sorted": true - }, - "offset": 0, - "pageNumber": 0, - "pageSize": 2147483647, - "paged": true, - "unpaged": false - }, - "last": true, - "totalPages": 1, - "totalElements": 3, - "first": true, - "size": 2147483647, - "number": 0, - "sort": { - "empty": false, - "unsorted": false, - "sorted": true - }, - "numberOfElements": 3, - "empty": false - } - """) - }) - }), - }) - @Operation(description = "Permission: **view_wallets** (The BPN of holderIdentifier must equal BPN of caller)\n\n Search verifiable credentials with filter criteria", summary = "Query Verifiable Credentials") - public @interface GetCredentialsApiDocs { - } - - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - @RequestBody(content = { - @Content(examples = @ExampleObject(""" - { - "bpn": "BPNL000000000000" - } - """)) - }) - @Tag(name = API_TAG_VERIFIABLE_CREDENTIAL_ISSUER) - @ApiResponses(value = { - @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "403", description = "The request could not be completed due to a forbidden access", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "500", description = "Any other internal server error", content = { - @Content(examples = { - @ExampleObject(name = "Internal server error", value = """ - { - "type": "about:blank", - "title": "Error Title", - "status": 500, - "detail": "Error Details", - "instance": "API endpoint", - "properties": { - "timestamp": 1689762476720 - } - } - """) - }) }), - @ApiResponse(responseCode = "409", description = "The request could not be completed due to a conflict.", content = { - @Content(examples = { - @ExampleObject(name = "MembershipCredential already exist", value = """ - { - "type": "about:blank", - "title": "Credential of type MembershipCredential is already exists ", - "status": 409, - "detail": "Credential of type MembershipCredential is already exists ", - "instance": "/api/credentials/issuer/membership", - "properties": { - "timestamp": 1689772483831 - } - } - """) - }) }), - @ApiResponse(responseCode = "404", description = "Wallet not found with provided identifier", content = { - @Content(examples = { - @ExampleObject(name = "Wallet not found with provided identifier", value = """ - { - "type": "about:blank", - "title": "Error Title", - "status": 404, - "detail": "Error Details", - "instance": "API endpoint", - "properties": { - "timestamp": 1689762476720 - } - } - """) - }) }), - @ApiResponse(responseCode = "400", description = "The input does not comply to the syntax requirements", content = { - @Content(examples = { - @ExampleObject(name = "Response in case of invalid data provided", value = """ - { - "type": "about:blank", - "title": "Invalid data provided", - "status": 400, - "detail": "details", - "instance": "API endpoint", - "properties": - { - "timestamp": 1689760833962, - "errors": - { - "filed": "filed error message" - } - } - } - """) - }) - }), - @ApiResponse(responseCode = "201", description = "Issuer credential", content = { - @Content(examples = { - @ExampleObject(name = "Membership credential", value = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#0d6b6447-99de-4bc5-94f3-3ac0ae8ee188", - "type": [ - "VerifiableCredential", - "MembershipCredential" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T13:13:53Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "holderIdentifier": "BPNL000000000000", - "startTime": "2023-07-19T13:13:53.581081Z", - "memberOf": "Catena-X", - "id": "did:web:localhost:BPNL000000000000", - "type": "MembershipCredential", - "status": "Active" - } - ], - "proof": { - "proofPurpose": "proofPurpose", - "verificationMethod": "did:web:localhost:BPNL000000000000#", - "type": "JsonWebSignature2020", - "created": "2023-07-19T13:13:57Z", - "jws": "eyJhbGciOiJFZERTQSJ9..zt7SyONY1shO7N6KrabQJr9uNrToM1Bc4eagTQc1LxAfZ1v-SSp9Y-2cpZNDV8AR08r4L8VbtWrR9t2dNoAfDw" - } - } - """) - }) - }) }) - @Operation(summary = "Issue a Membership Verifiable Credential with base wallet issuer", description = "Permission: **update_wallets** (The BPN of base wallet must equal BPN of caller)\n\n Issue a verifiable credential by base wallet") - public @interface IssueMembershipCredentialApiDoc { - } - - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - @RequestBody(content = { - @Content(examples = @ExampleObject(""" - { - "bpn": "BPNL000000000000", - "activityType": "vehicleDismantle", - "allowedVehicleBrands": [ - "Audi", "Abarth", "Alfa Romeo", "Chrysler" - ] - } - """)) - }) - @Tag(name = API_TAG_VERIFIABLE_CREDENTIAL_ISSUER) - @ApiResponses(value = { - @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "403", description = "The request could not be completed due to a forbidden access", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "500", description = "Any other internal server error", content = { - @Content(examples = { - @ExampleObject(name = "Internal server error", value = """ - { - "type": "about:blank", - "title": "Error Title", - "status": 500, - "detail": "Error Details", - "instance": "API endpoint", - "properties": { - "timestamp": 1689762476720 - } - } - """) - }) }), - @ApiResponse(responseCode = "409", description = "The request could not be completed due to a conflict.", content = { - @Content(examples = { - @ExampleObject(name = "DismantlerCredential already exist", value = """ - { - "type": "about:blank", - "title": "Credential of type DismantlerCredential is already exists ", - "status": 409, - "detail": "Credential of type DismantlerCredential is already exists ", - "instance": "/api/credentials/issuer/dismantler", - "properties": { - "timestamp": 1689773804746 - } - } - """) - }) }), - @ApiResponse(responseCode = "404", description = "Wallet not found with provided identifier", content = { - @Content(examples = { - @ExampleObject(name = "Wallet not found with provided identifier", value = """ - { - "type": "about:blank", - "title": "Error Title", - "status": 404, - "detail": "Error Details", - "instance": "API endpoint", - "properties": { - "timestamp": 1689762476720 - } - } - """) - }) }), - @ApiResponse(responseCode = "400", description = "The input does not comply to the syntax requirements", content = { - @Content(examples = { - @ExampleObject(name = "Response in case of invalid data provided", value = """ - { - "type": "about:blank", - "title": "Invalid data provided", - "status": 400, - "detail": "details", - "instance": "API endpoint", - "properties": - { - "timestamp": 1689760833962, - "errors": - { - "filed": "filed error message" - } - } - } - """) - }) - }), - @ApiResponse(responseCode = "201", description = "Dismantler Credential", content = { - @Content(examples = { - @ExampleObject(name = "Dismantler Credential", value = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#5caac86c-8ef8-4aab-9d2b-fb18c62560a9", - "type": [ - "VerifiableCredential", - "DismantlerCredential" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T13:35:33Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "holderIdentifier": "BPNL000000000000", - "allowedVehicleBrands": [ - "Audi", - "Abarth", - "Alfa Romeo", - "Chrysler" - ], - "id": "did:web:localhost:BPNL000000000000", - "activityType": "vehicleDismantle", - "type": "DismantlerCredential" - } - ], - "proof": { - "proofPurpose": "proofPurpose", - "verificationMethod": "did:web:localhost:BPNL000000000000#", - "type": "JsonWebSignature2020", - "created": "2023-07-19T13:35:38Z", - "jws": "eyJhbGciOiJFZERTQSJ9..UI82uq6iyqoaKjZIhJiV24v_Bqnj_7EqWiqZ3VWjqkoHLnr7JDtW5KVywWPl27j_baLBxxnM5jqjQdSK4rfbBg" - } - } - """) - }) - }) - }) - @Operation(summary = "Issue a Dismantler Verifiable Credential with base wallet issuer", description = "Permission: **update_wallets** (The BPN of base wallet must equal BPN of caller)\n\n Issue a verifiable credential by base wallet") - public @interface IssueDismantlerCredentialApiDoc { - } - - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - @RequestBody(content = { - @Content(examples = { - @ExampleObject(name = "BehaviorTwinCredential", value = """ - { - "holderIdentifier": "BPNL000000000000", - "type": "BehaviorTwinCredential", - "contract-template": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contract-version": "1.0.0" - } - """), - @ExampleObject(name = "PcfCredential", value = """ - { - "holderIdentifier": "BPNL000000000000", - "type": "PcfCredential", - "contract-template": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contract-version": "1.0.0" - } - """), - @ExampleObject(name = "SustainabilityCredential", value = """ - { - "holderIdentifier": "BPNL000000000000", - "type": "SustainabilityCredential", - "contract-template": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contract-version": "1.0.0" - } - """), - @ExampleObject(name = "QualityCredential", value = """ - { - "holderIdentifier": "BPNL000000000000", - "type": "QualityCredential", - "contract-template": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contract-version": "1.0.0" - } - """), - @ExampleObject(name = "TraceabilityCredential", value = """ - { - "holderIdentifier": "BPNL000000000000", - "type": "TraceabilityCredential", - "contract-template": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contract-version": "1.0.0" - } - """), - @ExampleObject(name = "BehaviorTwinCredential", value = """ - { - "holderIdentifier": "BPNL000000000000", - "type": "BehaviorTwinCredential", - "contract-template": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contract-version": "1.0.0" - } - """), - @ExampleObject(name = "ResiliencyCredential", value = """ - { - "holderIdentifier": "BPNL000000000000", - "type": "ResiliencyCredential", - "contract-template": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contract-version": "1.0.0" - } - """) - - }) - }) - @Tag(name = API_TAG_VERIFIABLE_CREDENTIAL_ISSUER) - @Operation(summary = "Issue a Use Case Verifiable Credential with base wallet issuer", description = "Permission: **update_wallets** (The BPN of base wallet must equal BPN of caller)\n\n Issue a verifiable credential by base wallet") - @ApiResponses(value = { - @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "403", description = "The request could not be completed due to a forbidden access", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "500", description = "Any other internal server error", content = { - @Content(examples = { - @ExampleObject(name = "Internal server error", value = """ - { - "type": "about:blank", - "title": "Error Title", - "status": 500, - "detail": "Error Details", - "instance": "API endpoint", - "properties": { - "timestamp": 1689762476720 - } - } - """) - }) }), - @ApiResponse(responseCode = "404", description = "Wallet not found with provided identifier", content = { - @Content(examples = { - @ExampleObject(name = "Wallet not found with provided identifier", value = """ - { - "type": "about:blank", - "title": "Error Title", - "status": 404, - "detail": "Error Details", - "instance": "API endpoint", - "properties": { - "timestamp": 1689762476720 - } - } - """) - }) }), - @ApiResponse(responseCode = "400", description = "The input does not comply to the syntax requirements", content = { - @Content(examples = { - @ExampleObject(name = "Response in case of invalid data provided", value = """ - { - "type": "about:blank", - "title": "Invalid data provided", - "status": 400, - "detail": "details", - "instance": "API endpoint", - "properties": - { - "timestamp": 1689760833962, - "errors": - { - "filed": "filed error message" - } - } - } - """) - }) - }), - @ApiResponse(responseCode = "201", description = "Framework credential", content = { - @Content(examples = { - @ExampleObject(name = "BehaviorTwin credential", value = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#46a8c5e6-b195-4ec9-85cd-665c57d296ab", - "type": [ - "VerifiableCredential", - "UseCaseFrameworkCondition" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T13:49:58Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "holderIdentifier": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "BehaviorTwinCredential", - "contractTemplate": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contractVersion": "1.0.0" - } - ], - "proof": { - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#", - "created": "2023-07-19T13:50:02Z", - "jws": "eyJhbGciOiJFZERTQSJ9..IkfgC6Gn9sOT1uu1zMiDIIqw6pV4Z8axkKvphegsCVWT9uo0HZp4J9L1ILxR-huINGR5QlGIKiVuLGB5kKDOAQ" - } - } - """), - @ExampleObject(name = "Pcf Credential", value = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#46a8c5e6-b195-4ec9-85cd-665c57d296ab", - "type": [ - "VerifiableCredential", - "UseCaseFrameworkCondition" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T13:49:58Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "holderIdentifier": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "PcfCredential", - "contractTemplate": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contractVersion": "1.0.0" - } - ], - "proof": { - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#", - "created": "2023-07-19T13:50:02Z", - "jws": "eyJhbGciOiJFZERTQSJ9..IkfgC6Gn9sOT1uu1zMiDIIqw6pV4Z8axkKvphegsCVWT9uo0HZp4J9L1ILxR-huINGR5QlGIKiVuLGB5kKDOAQ" - } - } - """), - @ExampleObject(name = "Sustainability Credential", value = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#46a8c5e6-b195-4ec9-85cd-665c57d296ab", - "type": [ - "VerifiableCredential", - "UseCaseFrameworkCondition" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T13:49:58Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "holderIdentifier": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "SustainabilityCredential", - "contractTemplate": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contractVersion": "1.0.0" - } - ], - "proof": { - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#", - "created": "2023-07-19T13:50:02Z", - "jws": "eyJhbGciOiJFZERTQSJ9..IkfgC6Gn9sOT1uu1zMiDIIqw6pV4Z8axkKvphegsCVWT9uo0HZp4J9L1ILxR-huINGR5QlGIKiVuLGB5kKDOAQ" - } - } - """), - @ExampleObject(name = "Quality Credential", value = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#46a8c5e6-b195-4ec9-85cd-665c57d296ab", - "type": [ - "VerifiableCredential", - "UseCaseFrameworkCondition" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T13:49:58Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "holderIdentifier": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "QualityCredential", - "contractTemplate": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contractVersion": "1.0.0" - } - ], - "proof": { - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#", - "created": "2023-07-19T13:50:02Z", - "jws": "eyJhbGciOiJFZERTQSJ9..IkfgC6Gn9sOT1uu1zMiDIIqw6pV4Z8axkKvphegsCVWT9uo0HZp4J9L1ILxR-huINGR5QlGIKiVuLGB5kKDOAQ" - } - } - """), - @ExampleObject(name = "Traceability Credential", value = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#46a8c5e6-b195-4ec9-85cd-665c57d296ab", - "type": [ - "VerifiableCredential", - "UseCaseFrameworkCondition" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T13:49:58Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "holderIdentifier": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "TraceabilityCredential", - "contractTemplate": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contractVersion": "1.0.0" - } - ], - "proof": { - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#", - "created": "2023-07-19T13:50:02Z", - "jws": "eyJhbGciOiJFZERTQSJ9..IkfgC6Gn9sOT1uu1zMiDIIqw6pV4Z8axkKvphegsCVWT9uo0HZp4J9L1ILxR-huINGR5QlGIKiVuLGB5kKDOAQ" - } - } - """), - @ExampleObject(name = "Resiliency Credential", value = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#46a8c5e6-b195-4ec9-85cd-665c57d296ab", - "type": [ - "VerifiableCredential", - "UseCaseFrameworkCondition" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T13:49:58Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "holderIdentifier": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "ResiliencyCredential", - "contractTemplate": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", - "contractVersion": "1.0.0" - } - ], - "proof": { - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#", - "created": "2023-07-19T13:50:02Z", - "jws": "eyJhbGciOiJFZERTQSJ9..IkfgC6Gn9sOT1uu1zMiDIIqw6pV4Z8axkKvphegsCVWT9uo0HZp4J9L1ILxR-huINGR5QlGIKiVuLGB5kKDOAQ" - } - } - """) - }) - }) - }) - public @interface IssueFrameworkCredentialApiDocs { - } - - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - @Tag(name = API_TAG_VERIFIABLE_CREDENTIAL_VALIDATION) - @ApiResponses(value = { - @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "403", description = "The request could not be completed due to a forbidden access", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "500", description = "Any other internal server error", content = { - @Content(examples = { - @ExampleObject(name = "Internal server error", value = """ - { - "type": "about:blank", - "title": "Error Title", - "status": 500, - "detail": "Error Details", - "instance": "API endpoint", - "properties": { - "timestamp": 1689762476720 - } - } - """) - }) }), - @ApiResponse(responseCode = "200", description = "Validate Verifiable Credentials", content = { - @Content(examples = { - @ExampleObject(name = "Verifiable Credentials without check expiry", value = """ - { - "valid": true, - "vc": { - "issuanceDate": "2023-07-19T09:11:34Z", - "credentialSubject": [ - { - "bpn": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "BpnCredential" - } - ], - "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", - "proof": { - "created": "2023-07-19T09:11:39Z", - "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#" - }, - "type": [ - "VerifiableCredential", - "BpnCredential" - ], - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "expirationDate": "2024-12-31T18:30:00Z" - } - } - """), - @ExampleObject(name = "Verifiable Credentials with check expiry", value = """ - { - "valid": true, - "validateExpiryDate": true, - "vc": { - "issuanceDate": "2023-07-19T09:11:34Z", - "credentialSubject": [ - { - "bpn": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "BpnCredential" - } - ], - "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", - "proof": { - "created": "2023-07-19T09:11:39Z", - "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#" - }, - "type": [ - "VerifiableCredential", - "BpnCredential" - ], - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "expirationDate": "2024-12-31T18:30:00Z" - } - } - """), - @ExampleObject(name = "Verifiable expired credentials with check expiry ", value = """ - { - "valid": false, - "validateExpiryDate": false, - "vc": { - "issuanceDate": "2023-07-19T09:11:34Z", - "credentialSubject": [ - { - "bpn": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "BpnCredential" - } - ], - "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", - "proof": { - "created": "2023-07-19T09:11:39Z", - "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#" - }, - "type": [ - "VerifiableCredential", - "BpnCredential" - ], - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "expirationDate": "2022-12-31T18:30:00Z" - } - } - """), - @ExampleObject(name = "Verifiable Credentials with invalid signature", value = """ - { - "valid": false, - "vc": { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", - "type": [ - "VerifiableCredential", - "BpnCredential" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "expirationDate": "2024-12-31T18:30:00Z" - "issuanceDate": "2023-07-19T09:11:34Z", - "credentialSubject": [ - { - "bpn": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "BpnCredential" - } - ], - "proof": { - "created": "2023-07-19T09:11:39Z", - "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhf", - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#" - }, - } - } - """) - }) - }) }) - @Operation(summary = "Validate Verifiable Credentials", description = "Permission: **view_wallets** OR **view_wallet** \n\n Validate Verifiable Credentials") - @RequestBody(content = { - @Content(examples = @ExampleObject(""" - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", - "type": [ - "VerifiableCredential", - "BpnCredential" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T09:11:34Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "bpn": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "BpnCredential" - } - ], - "proof": { - "created": "2023-07-19T09:11:39Z", - "jws": "eyJhbGciOiJFZERTQSJ9..fdn2qU85auOltdHDLdHI7sJVV1ZPdftpiXd_ndXN0dFgSDWiIrScdD03wtvKLq_H-shQWfh2RYeMmrlEzAhfDw", - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#" - } - } - """)) - }) - public @interface ValidateVerifiableCredentialApiDocs { - } - - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - @Tag(name = API_TAG_VERIFIABLE_CREDENTIAL_ISSUER) - @ApiResponses(value = { - @ApiResponse(responseCode = "401", description = "The request could not be completed due to a failed authorization.", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "403", description = "The request could not be completed due to a forbidden access", content = { - @Content(examples = {}) }), - @ApiResponse(responseCode = "500", description = "Any other internal server error", content = { - @Content(examples = { - @ExampleObject(name = "Internal server error", value = """ - { - "type": "about:blank", - "title": "Error Title", - "status": 500, - "detail": "Error Details", - "instance": "API endpoint", - "properties": { - "timestamp": 1689762476720 - } - } - """) - }) }), - @ApiResponse(responseCode = "404", description = "Wallet not found with provided identifier", content = { - @Content(examples = { - @ExampleObject(name = "Wallet not found with provided identifier", value = """ - { - "type": "about:blank", - "title": "Error Title", - "status": 404, - "detail": "Error Details", - "instance": "API endpoint", - "properties": { - "timestamp": 1689762476720 - } - } - """) - }) }), - @ApiResponse(responseCode = "400", description = "The input does not comply to the syntax requirements", content = { - @Content(examples = { - @ExampleObject(name = "Response in case of invalid data provided", value = """ - { - "type": "about:blank", - "title": "Invalid data provided", - "status": 400, - "detail": "details", - "instance": "API endpoint", - "properties": - { - "timestamp": 1689760833962, - "errors": - { - "filed": "filed error message" - } - } - } - """) - }) - }), - @ApiResponse(responseCode = "201", description = "Issuer credential", content = { - @Content(examples = { - @ExampleObject(name = "Issuer credential", value = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#ff084e7a-1b46-4a2f-a78d-3d701a0bd6e4", - "type": [ - "VerifiableCredential", - "BpnCredential" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T12:18:30Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "bpn": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "BpnCredential" - } - ], - "proof": { - "proofPurpose": "proofPurpose", - "type": "JsonWebSignature2020", - "verificationMethod": "did:web:localhost:BPNL000000000000#", - "created": "2023-07-19T12:18:34Z", - "jws": "eyJhbGciOiJFZERTQSJ9..0Ua1vcTQAYwQY3PPuHr4RQxqW6iIngrHQQx1oPgk2uzqUpcbfY2YUxXAnbNA333-lSuvNhiV_1NLfBnCEcI2DQ" - } - } - """) - }) - }) }) - @Operation(summary = "Issue Verifiable Credential", description = "Permission: **update_wallets** (The BPN of the base wallet must equal BPN of caller)\nIssue a verifiable credential with a given issuer DID") - @RequestBody(content = { - @Content(examples = @ExampleObject(""" - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json", - "https://w3id.org/security/suites/jws-2020/v1" - ], - "id": "did:web:localhost:BPNL000000000000#f73e3631-ba87-4a03-bea3-b28700056879", - "type": [ - "VerifiableCredential", - "BpnCredential" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-07-19T09:11:34Z", - "expirationDate": "2024-12-31T18:30:00Z", - "credentialSubject": [ - { - "bpn": "BPNL000000000000", - "id": "did:web:localhost:BPNL000000000000", - "type": "BpnCredential" - } - ] - } - """)) - }) - public @interface IssueVerifiableCredentialUsingBaseWalletApiDocs { - } -} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/IssueFrameworkCredentialRequest.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/IssueFrameworkCredentialRequest.java deleted file mode 100644 index 78a13e68a..000000000 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/IssueFrameworkCredentialRequest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * ******************************************************************************* - * Copyright (c) 2021,2023 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 - * ****************************************************************************** - */ - -package org.eclipse.tractusx.managedidentitywallets.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Size; -import lombok.*; - - -/** - * The type Issue framework credential request. - */ -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class IssueFrameworkCredentialRequest { - - @NotBlank(message = "Please provide holder identifier") - @Size(min = 5, max = 255, message = "Please provide valid identifier") - private String holderIdentifier; - - @NotBlank(message = "Please provide type") - private String type; - - @NotBlank(message = "Please provide contract-template") - @JsonProperty("contract-template") - private String contractTemplate; - - @NotBlank(message = "Please provide contract-template") - @JsonProperty("contract-version") - private String contractVersion; -} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialService.java deleted file mode 100644 index 683605f99..000000000 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialService.java +++ /dev/null @@ -1,557 +0,0 @@ -/* - * ******************************************************************************* - * Copyright (c) 2021,2023 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 - * ****************************************************************************** - */ - -package org.eclipse.tractusx.managedidentitywallets.service; - -import com.smartsensesolutions.java.commons.FilterRequest; -import com.smartsensesolutions.java.commons.base.repository.BaseRepository; -import com.smartsensesolutions.java.commons.base.service.BaseService; -import com.smartsensesolutions.java.commons.criteria.CriteriaOperator; -import com.smartsensesolutions.java.commons.operator.Operator; -import com.smartsensesolutions.java.commons.sort.Sort; -import com.smartsensesolutions.java.commons.sort.SortType; -import com.smartsensesolutions.java.commons.specification.SpecificationUtil; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.text.StringEscapeUtils; -import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; -import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType; -import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.IssuersCredential; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueDismantlerCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueFrameworkCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueMembershipCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; -import org.eclipse.tractusx.managedidentitywallets.exception.DuplicateCredentialProblem; -import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; -import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils; -import org.eclipse.tractusx.managedidentitywallets.utils.Validate; -import org.eclipse.tractusx.ssi.lib.did.resolver.DidResolver; -import org.eclipse.tractusx.ssi.lib.did.web.DidWebResolver; -import org.eclipse.tractusx.ssi.lib.did.web.util.DidWebParser; -import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; -import org.eclipse.tractusx.ssi.lib.proof.LinkedDataProofValidation; -import org.eclipse.tractusx.ssi.lib.proof.SignatureType; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -import java.net.http.HttpClient; -import java.time.Instant; -import java.util.*; - -/** - * The type Issuers credential service. - */ -@Service -@Slf4j -public class IssuersCredentialService extends BaseService { - - /** - * The constant BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN. - */ - public static final String BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN = "Base wallet BPN is not matching with request BPN(from token)"; - - private final IssuersCredentialRepository issuersCredentialRepository; - private final MIWSettings miwSettings; - - private final SpecificationUtil credentialSpecificationUtil; - - private final WalletKeyService walletKeyService; - - private final HoldersCredentialRepository holdersCredentialRepository; - - private final CommonService commonService; - - /** - * Instantiates a new Issuers credential service. - * - * @param issuersCredentialRepository the issuers credential repository - * @param miwSettings the miw settings - * @param credentialSpecificationUtil the credential specification util - * @param walletKeyService the wallet key service - * @param holdersCredentialRepository the holders credential repository - * @param commonService the common service - */ - public IssuersCredentialService(IssuersCredentialRepository issuersCredentialRepository, MIWSettings miwSettings, - SpecificationUtil credentialSpecificationUtil, - WalletKeyService walletKeyService, HoldersCredentialRepository holdersCredentialRepository, CommonService commonService) { - this.issuersCredentialRepository = issuersCredentialRepository; - this.miwSettings = miwSettings; - this.credentialSpecificationUtil = credentialSpecificationUtil; - this.walletKeyService = walletKeyService; - this.holdersCredentialRepository = holdersCredentialRepository; - this.commonService = commonService; - } - - - @Override - protected BaseRepository getRepository() { - return issuersCredentialRepository; - } - - @Override - protected SpecificationUtil getSpecificationUtil() { - return credentialSpecificationUtil; - } - - - /** - * Gets credentials. - * - * @param credentialId the credential id - * @param holderIdentifier the issuer identifier - * @param type the type - * @param sortColumn the sort column - * @param sortType the sort type - * @param pageNumber the page number - * @param size the size - * @param callerBPN the caller bpn - * @return the credentials - */ - public PageImpl getCredentials(String credentialId, String holderIdentifier, List type, String sortColumn, String sortType, int pageNumber, int size, String callerBPN) { - FilterRequest filterRequest = new FilterRequest(); - filterRequest.setSize(size); - filterRequest.setPage(pageNumber); - - //Issuer must be caller of API - Wallet issuerWallet = commonService.getWalletByIdentifier(callerBPN); - filterRequest.appendCriteria(StringPool.ISSUER_DID, Operator.EQUALS, issuerWallet.getDid()); - - if (StringUtils.hasText(holderIdentifier)) { - Wallet holderWallet = commonService.getWalletByIdentifier(holderIdentifier); - filterRequest.appendCriteria(StringPool.HOLDER_DID, Operator.EQUALS, holderWallet.getDid()); - } - - if (StringUtils.hasText(credentialId)) { - filterRequest.appendCriteria(StringPool.CREDENTIAL_ID, Operator.EQUALS, credentialId); - } - FilterRequest request = new FilterRequest(); - if (!CollectionUtils.isEmpty(type)) { - request.setPage(filterRequest.getPage()); - request.setSize(filterRequest.getSize()); - request.setCriteriaOperator(CriteriaOperator.OR); - for (String str : type) { - request.appendCriteria(StringPool.TYPE, Operator.CONTAIN, str); - } - } - - Sort sort = new Sort(); - sort.setColumn(sortColumn); - sort.setSortType(SortType.valueOf(sortType.toUpperCase())); - filterRequest.setSort(sort); - Page filter = filter(filterRequest, request, CriteriaOperator.AND); - - List list = new ArrayList<>(filter.getContent().size()); - for (IssuersCredential credential : filter.getContent()) { - list.add(credential.getData()); - } - return new PageImpl<>(list, filter.getPageable(), filter.getTotalElements()); - } - - - /** - * Issue bpn credential - * - * @param baseWallet the base wallet - * @param holderWallet the holder wallet - * @param authority the authority - * @return the verifiable credential - */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED) - public VerifiableCredential issueBpnCredential(Wallet baseWallet, Wallet holderWallet, boolean authority) { - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifierAsBytes(baseWallet.getId()); - List types = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, MIWVerifiableCredentialType.BPN_CREDENTIAL); - VerifiableCredentialSubject verifiableCredentialSubject = new VerifiableCredentialSubject(Map.of(StringPool.TYPE, MIWVerifiableCredentialType.BPN_CREDENTIAL, - StringPool.ID, holderWallet.getDid(), - StringPool.BPN, holderWallet.getBpn())); - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(verifiableCredentialSubject, - types, baseWallet.getDidDocument(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate(), authority); - - //Store Credential in holder wallet - holdersCredential = holdersCredentialRepository.save(holdersCredential); - - //Store Credential in issuers table - IssuersCredential issuersCredential = IssuersCredential.of(holdersCredential); - issuersCredentialRepository.save(issuersCredential); - - //update summery VC - updateSummeryCredentials(baseWallet.getDidDocument(), privateKeyBytes, baseWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), MIWVerifiableCredentialType.BPN_CREDENTIAL); - - log.debug("BPN credential issued for bpn -{}", StringEscapeUtils.escapeJava(holderWallet.getBpn())); - - return issuersCredential.getData(); - } - - /** - * Issue framework credential verifiable credential. - * - * @param request the request - * @param callerBPN the caller bpn - * @return the verifiable credential - */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED) - public VerifiableCredential issueFrameworkCredential(IssueFrameworkCredentialRequest request, String callerBPN) { - - //validate type - Validate.isFalse(miwSettings.supportedFrameworkVCTypes().contains(request.getType())).launch(new BadDataException("Framework credential of type " + request.getType() + " is not supported, supported values are " + miwSettings.supportedFrameworkVCTypes())); - - //Fetch Holder Wallet - Wallet holderWallet = commonService.getWalletByIdentifier(request.getHolderIdentifier()); - - Wallet baseWallet = commonService.getWalletByIdentifier(miwSettings.authorityWalletBpn()); - - validateAccess(callerBPN, baseWallet); - // get Key - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifierAsBytes(baseWallet.getId()); - - //if base wallet issue credentials to itself - boolean isSelfIssued = isSelfIssued(holderWallet.getBpn()); - - VerifiableCredentialSubject subject = new VerifiableCredentialSubject(Map.of( - StringPool.TYPE, request.getType(), - StringPool.ID, holderWallet.getDid(), - StringPool.HOLDER_IDENTIFIER, holderWallet.getBpn(), - StringPool.CONTRACT_TEMPLATE, request.getContractTemplate(), - StringPool.CONTRACT_VERSION, request.getContractVersion())); - List types = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, MIWVerifiableCredentialType.USE_CASE_FRAMEWORK_CONDITION); - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(subject, types, baseWallet.getDidDocument(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate(), isSelfIssued); - - //save in holder wallet - holdersCredential = holdersCredentialRepository.save(holdersCredential); - - //Store Credential in issuers table - IssuersCredential issuersCredential = IssuersCredential.of(holdersCredential); - issuersCredential = create(issuersCredential); - - //update summery cred - updateSummeryCredentials(baseWallet.getDidDocument(), privateKeyBytes, baseWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), request.getType()); - - log.debug("Framework VC of type ->{} issued to bpn ->{}", StringEscapeUtils.escapeJava(request.getType()), StringEscapeUtils.escapeJava(holderWallet.getBpn())); - - // Return VC - return issuersCredential.getData(); - } - - /** - * Issue dismantler credential verifiable credential. - * - * @param request the request - * @param callerBPN the caller bpn - * @return the verifiable credential - */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED) - public VerifiableCredential issueDismantlerCredential(IssueDismantlerCredentialRequest request, String callerBPN) { - - //Fetch Holder Wallet - Wallet holderWallet = commonService.getWalletByIdentifier(request.getBpn()); - - // Fetch Issuer Wallet - Wallet issuerWallet = commonService.getWalletByIdentifier(miwSettings.authorityWalletBpn()); - - validateAccess(callerBPN, issuerWallet); - - //check duplicate - isCredentialExit(holderWallet.getDid(), MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL); - - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifierAsBytes(issuerWallet.getId()); - - //if base wallet issue credentials to itself - boolean isSelfIssued = isSelfIssued(request.getBpn()); - - VerifiableCredentialSubject subject = new VerifiableCredentialSubject(Map.of(StringPool.TYPE, MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL, - StringPool.ID, holderWallet.getDid(), - StringPool.HOLDER_IDENTIFIER, holderWallet.getBpn(), - StringPool.ACTIVITY_TYPE, request.getActivityType(), - StringPool.ALLOWED_VEHICLE_BRANDS, request.getAllowedVehicleBrands() == null ? Collections.emptySet() : request.getAllowedVehicleBrands())); - List types = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL); - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(subject, types, issuerWallet.getDidDocument(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate(), isSelfIssued); - - - //save in holder wallet - holdersCredential = holdersCredentialRepository.save(holdersCredential); - - //Store Credential in issuers table - IssuersCredential issuersCredential = IssuersCredential.of(holdersCredential); - issuersCredential = create(issuersCredential); - - //update summery VC - updateSummeryCredentials(issuerWallet.getDidDocument(), privateKeyBytes, issuerWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL); - - log.debug("Dismantler VC issued to bpn -> {}", StringEscapeUtils.escapeJava(request.getBpn())); - - // Return VC - return issuersCredential.getData(); - } - - /** - * Issue membership credential verifiable credential. - * - * @param issueMembershipCredentialRequest the issue membership credential request - * @param callerBPN the caller bpn - * @return the verifiable credential - */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED) - public VerifiableCredential issueMembershipCredential(IssueMembershipCredentialRequest issueMembershipCredentialRequest, String callerBPN) { - - //Fetch Holder Wallet - Wallet holderWallet = commonService.getWalletByIdentifier(issueMembershipCredentialRequest.getBpn()); - - //check duplicate - isCredentialExit(holderWallet.getDid(), VerifiableCredentialType.MEMBERSHIP_CREDENTIAL); - - // Fetch Issuer Wallet - Wallet issuerWallet = commonService.getWalletByIdentifier(miwSettings.authorityWalletBpn()); - - validateAccess(callerBPN, issuerWallet); - - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifierAsBytes(issuerWallet.getId()); - List types = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, VerifiableCredentialType.MEMBERSHIP_CREDENTIAL); - - //if base wallet issue credentials to itself - boolean isSelfIssued = isSelfIssued(issueMembershipCredentialRequest.getBpn()); - - //VC Subject - VerifiableCredentialSubject verifiableCredentialSubject = new VerifiableCredentialSubject(Map.of(StringPool.TYPE, VerifiableCredentialType.MEMBERSHIP_CREDENTIAL, - StringPool.ID, holderWallet.getDid(), - StringPool.HOLDER_IDENTIFIER, holderWallet.getBpn(), - StringPool.MEMBER_OF, issuerWallet.getName(), - StringPool.STATUS, "Active", - StringPool.START_TIME, Instant.now().toString())); - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(verifiableCredentialSubject, types, issuerWallet.getDidDocument(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate(), isSelfIssued); - - - //save in holder wallet - holdersCredential = holdersCredentialRepository.save(holdersCredential); - - IssuersCredential issuersCredential = IssuersCredential.of(holdersCredential); - - //Store Credential in issuer table - issuersCredential = create(issuersCredential); - - //update summery VC - updateSummeryCredentials(issuerWallet.getDidDocument(), privateKeyBytes, issuerWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), VerifiableCredentialType.MEMBERSHIP_CREDENTIAL); - - log.debug("Membership VC issued to bpn ->{}", StringEscapeUtils.escapeJava(issueMembershipCredentialRequest.getBpn())); - - // Return VC - return issuersCredential.getData(); - } - - - /** - * Issue credential using base wallet - * - * @param holderDid the holder did - * @param data the data - * @param callerBpn the caller bpn - * @return the verifiable credential - */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED) - public VerifiableCredential issueCredentialUsingBaseWallet(String holderDid, Map data, String callerBpn) { - //Fetch Holder Wallet - Wallet holderWallet = commonService.getWalletByIdentifier(holderDid); - - VerifiableCredential verifiableCredential = new VerifiableCredential(data); - - //Summary VC can not be issued using API, as summary VC is issuing at runtime - verifiableCredential.getTypes().forEach(type -> Validate.isTrue(type.equals(MIWVerifiableCredentialType.SUMMARY_CREDENTIAL)).launch(new BadDataException("Can not issue " + MIWVerifiableCredentialType.SUMMARY_CREDENTIAL + " type VC using API"))); - - Wallet issuerWallet = commonService.getWalletByIdentifier(verifiableCredential.getIssuer().toString()); - - validateAccess(callerBpn, issuerWallet); - - // get issuer Key - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifierAsBytes(issuerWallet.getId()); - - boolean isSelfIssued = isSelfIssued(holderWallet.getBpn()); - - // Create Credential - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(verifiableCredential.getCredentialSubject().get(0), - verifiableCredential.getTypes(), issuerWallet.getDidDocument(), - privateKeyBytes, - holderWallet.getDid(), - verifiableCredential.getContext(), Date.from(verifiableCredential.getExpirationDate()), isSelfIssued); - - - //save in holder wallet - holdersCredential = holdersCredentialRepository.save(holdersCredential); - - //Store Credential in issuers table - IssuersCredential issuersCredential = IssuersCredential.of(holdersCredential); - issuersCredential = create(issuersCredential); - - log.debug("VC type of {} issued to bpn ->{}", StringEscapeUtils.escapeJava(verifiableCredential.getTypes().toString()), StringEscapeUtils.escapeJava(holderWallet.getBpn())); - - // Return VC - return issuersCredential.getData(); - } - - /** - * Credentials validation map. - * - * @param data the data - * @param withCredentialExpiryDate the with credential expiry date - * @return the map - */ - @SneakyThrows - public Map credentialsValidation(Map data, boolean withCredentialExpiryDate) { - VerifiableCredential verifiableCredential = new VerifiableCredential(data); - - DidResolver didResolver = new DidWebResolver(HttpClient.newHttpClient(), new DidWebParser(), miwSettings.enforceHttps()); - - LinkedDataProofValidation proofValidation = LinkedDataProofValidation.newInstance(didResolver); - - boolean valid = proofValidation.verify(verifiableCredential); - - Map response = new TreeMap<>(); - - //check expiry - boolean dateValidation = CommonService.validateExpiry(withCredentialExpiryDate, verifiableCredential, response); - - response.put(StringPool.VALID, valid && dateValidation); - response.put("vc", verifiableCredential); - - return response; - } - - - private void validateAccess(String callerBpn, Wallet issuerWallet) { - //validate BPN access, VC must be issued by base wallet - Validate.isFalse(callerBpn.equals(issuerWallet.getBpn())).launch(new ForbiddenException(BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN)); - - //issuer must be base wallet - Validate.isFalse(issuerWallet.getBpn().equals(miwSettings.authorityWalletBpn())).launch(new ForbiddenException(BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN)); - } - - - private void isCredentialExit(String holderDid, String credentialType) { - Validate.isTrue(holdersCredentialRepository.existsByHolderDidAndType(holderDid, credentialType)).launch(new DuplicateCredentialProblem("Credential of type " + credentialType + " is already exists ")); - } - - private boolean isSelfIssued(String holderBpn) { - return holderBpn.equals(miwSettings.authorityWalletBpn()); - } - - - /** - * Update summery credentials. - * - * @param issuerDidDocument the issuer did document - * @param issuerPrivateKey the issuer private key - * @param holderBpn the holder bpn - * @param holderDid the holder did - * @param type the type - */ - private void updateSummeryCredentials(DidDocument issuerDidDocument, byte[] issuerPrivateKey, String issuerDid, String holderBpn, String holderDid, String type) { - - //get last issued summary vc to holder to update items - Page filter = getLastIssuedSummaryCredential(issuerDid, holderDid); - List items; - if (!filter.getContent().isEmpty()) { - IssuersCredential issuersCredential = filter.getContent().get(0); - - //check if summery VC has subject - Validate.isTrue(issuersCredential.getData().getCredentialSubject().isEmpty()).launch(new BadDataException("VC subject not found in existing su,,ery VC")); - - //Check if we have only one subject in summery VC - Validate.isTrue(issuersCredential.getData().getCredentialSubject().size() > 1).launch(new BadDataException("VC subjects can more then 1 in case of summery VC")); - - VerifiableCredentialSubject subject = issuersCredential.getData().getCredentialSubject().get(0); - if (subject.containsKey(StringPool.ITEMS)) { - items = (List) subject.get(StringPool.ITEMS); - if (!items.contains(type)) { - items.add(type); - } - } else { - items = List.of(type); - - } - } else { - items = List.of(type); - } - log.debug("Issuing summary VC with items ->{}", StringEscapeUtils.escapeJava(items.toString())); - - //get summery VC of holder - List vcs = holdersCredentialRepository.getByHolderDidAndIssuerDidAndTypeAndStored(holderDid, issuerDid, MIWVerifiableCredentialType.SUMMARY_CREDENTIAL, false); //deleted only not stored VC - if (CollectionUtils.isEmpty(vcs)) { - log.debug("No summery VC found for did ->{}, checking in issuer", StringEscapeUtils.escapeJava(holderDid)); - } else { - //delete old summery VC from holder table, delete only not stored VC - log.debug("Deleting older summary VC fir bpn -{}", holderBpn); - holdersCredentialRepository.deleteAll(vcs); - } - - //issue new summery VC - boolean isSelfIssued = isSelfIssued(holderBpn); - - VerifiableCredentialSubject subject = new VerifiableCredentialSubject(Map.of(StringPool.ID, holderDid, - StringPool.HOLDER_IDENTIFIER, holderBpn, - StringPool.ITEMS, items, - StringPool.TYPE, MIWVerifiableCredentialType.SUMMARY_CREDENTIAL, - StringPool.CONTRACT_TEMPLATE, miwSettings.contractTemplatesUrl())); - - List types = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, MIWVerifiableCredentialType.SUMMARY_CREDENTIAL); - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(subject, types, - issuerDidDocument, - issuerPrivateKey, - holderDid, miwSettings.summaryVcContexts(), miwSettings.vcExpiryDate(), isSelfIssued); - - - //save in holder wallet - holdersCredentialRepository.save(holdersCredential); - - //Store Credential in issuers table - issuersCredentialRepository.save(IssuersCredential.of(holdersCredential)); - - log.info("Summery VC updated for holder did -> {}", StringEscapeUtils.escapeJava(holderDid)); - } - - private Page getLastIssuedSummaryCredential(String issuerDid, String holderDid) { - FilterRequest filterRequest = new FilterRequest(); - - //we need latest one record - filterRequest.setPage(0); - filterRequest.setSize(1); - Sort sort = new Sort(); - sort.setColumn(StringPool.CREATED_AT); - sort.setSortType(SortType.valueOf("desc".toUpperCase())); - filterRequest.setSort(sort); - - filterRequest.appendCriteria(StringPool.HOLDER_DID, Operator.EQUALS, holderDid); - filterRequest.appendCriteria(StringPool.ISSUER_DID, Operator.EQUALS, issuerDid); - filterRequest.appendCriteria(StringPool.TYPE, Operator.EQUALS, MIWVerifiableCredentialType.SUMMARY_CREDENTIAL); - - return filter(filterRequest); - } -} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java deleted file mode 100644 index 5cdb07a94..000000000 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * ******************************************************************************* - * Copyright (c) 2021,2023 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 - * ****************************************************************************** - */ - -package org.eclipse.tractusx.managedidentitywallets.service; - -import com.smartsensesolutions.java.commons.base.repository.BaseRepository; -import com.smartsensesolutions.java.commons.base.service.BaseService; -import com.smartsensesolutions.java.commons.specification.SpecificationUtil; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.bouncycastle.util.io.pem.PemReader; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; -import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; -import org.eclipse.tractusx.ssi.lib.crypt.x21559.x21559PrivateKey; -import org.springframework.stereotype.Service; - -import java.io.StringReader; - -/** - * The type Wallet key service. - */ -@Service -@Slf4j -@RequiredArgsConstructor -public class WalletKeyService extends BaseService { - - private final WalletKeyRepository walletKeyRepository; - - private final SpecificationUtil specificationUtil; - - private final EncryptionUtils encryptionUtils; - - @Override - protected BaseRepository getRepository() { - return walletKeyRepository; - } - - @Override - protected SpecificationUtil getSpecificationUtil() { - return specificationUtil; - } - - /** - * Get private key by wallet identifier as bytes byte [ ]. - * - * @param walletId the wallet id - * @return the byte [ ] - */ - @SneakyThrows - public byte[] getPrivateKeyByWalletIdentifierAsBytes(long walletId) { - return getPrivateKeyByWalletIdentifier(walletId).asByte(); - } - - /** - * Gets private key by wallet identifier. - * - * @param walletId the wallet id - * @return the private key by wallet identifier - */ - @SneakyThrows - - public x21559PrivateKey getPrivateKeyByWalletIdentifier(long walletId) { - WalletKey wallet = walletKeyRepository.getByWalletId(walletId); - String privateKey = encryptionUtils.decrypt(wallet.getPrivateKey()); - byte[] content = new PemReader(new StringReader(privateKey)).readPemObject().getContent(); - return new x21559PrivateKey(content); - } - -} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java deleted file mode 100644 index 13668bf5e..000000000 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * ******************************************************************************* - * Copyright (c) 2021,2023 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 - * ****************************************************************************** - */ - -package org.eclipse.tractusx.managedidentitywallets.utils; - -import lombok.SneakyThrows; -import lombok.experimental.UtilityClass; -import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; -import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; -import org.eclipse.tractusx.ssi.lib.crypt.x21559.x21559PrivateKey; -import org.eclipse.tractusx.ssi.lib.exception.InvalidePrivateKeyFormat; -import org.eclipse.tractusx.ssi.lib.exception.UnsupportedSignatureTypeException; -import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; -import org.eclipse.tractusx.ssi.lib.model.proof.jws.JWSSignature2020; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialBuilder; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; -import org.eclipse.tractusx.ssi.lib.proof.LinkedDataProofGenerator; -import org.eclipse.tractusx.ssi.lib.proof.SignatureType; - -import java.net.URI; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; -import java.util.regex.Pattern; - -/** - * The type Common utils. - */ -@UtilityClass -public class CommonUtils { - - public static final Pattern BPN_NUMBER_PATTERN = Pattern.compile(StringPool.BPN_NUMBER_REGEX); - - - /** - * Gets identifier type. - * - * @param identifier the identifier - * @return the identifier type - */ - public static String getIdentifierType(String identifier) { - if (identifier.startsWith("did:web")) { - return StringPool.DID; - } else { - Validate.isFalse(BPN_NUMBER_PATTERN.matcher(identifier).matches()).launch(new BadDataException("Invalid BPN number - " + identifier)); - return StringPool.BPN; - } - } - - - /** - * Gets credential. - * - * @param subject the subject - * @param types the types - * @param issuerDoc the issuer doc - * @param privateKeyBytes the private key bytes - * @param holderDid the holder did - * @return the credential - */ - public static HoldersCredential getHoldersCredential(VerifiableCredentialSubject subject, List types, DidDocument issuerDoc, - byte[] privateKeyBytes, String holderDid, List contexts, Date expiryDate, boolean selfIssued) { - List cloneTypes = new ArrayList<>(types); - - // Create VC - VerifiableCredential verifiableCredential = createVerifiableCredential(issuerDoc, types, - subject, privateKeyBytes, contexts, expiryDate); - - cloneTypes.remove(VerifiableCredentialType.VERIFIABLE_CREDENTIAL); - - // Create Credential - return HoldersCredential.builder() - .holderDid(holderDid) - .issuerDid(issuerDoc.getId().toString()) - .type(String.join(",", cloneTypes)) - .credentialId(verifiableCredential.getId().toString()) - .data(verifiableCredential) - .selfIssued(selfIssued) - .build(); - } - - @SneakyThrows({UnsupportedSignatureTypeException.class, InvalidePrivateKeyFormat.class}) - private static VerifiableCredential createVerifiableCredential(DidDocument issuerDoc, List verifiableCredentialType, - VerifiableCredentialSubject verifiableCredentialSubject, - byte[] privateKey, List contexts, Date expiryDate) { - //VC Builder - - // if the credential does not contain the JWS proof-context add it - URI jwsUri = URI.create("https://w3id.org/security/suites/jws-2020/v1"); - if (!contexts.contains(jwsUri)) { - contexts.add(jwsUri); - } - - // check if the expiryDate is set - // if its null then it will be ignored from the SSI Lib (VerifiableCredentialBuilder) and will not be added to the VC - Instant expiryInstant = null; - if (expiryDate != null) { - expiryInstant = expiryDate.toInstant(); - } - - URI id = URI.create(UUID.randomUUID().toString()); - VerifiableCredentialBuilder builder = new VerifiableCredentialBuilder() - .context(contexts) - .id(URI.create(issuerDoc.getId() + "#" + id)) - .type(verifiableCredentialType) - .issuer(issuerDoc.getId()) - .expirationDate(expiryInstant) - .issuanceDate(Instant.now()) - .credentialSubject(verifiableCredentialSubject); - - - LinkedDataProofGenerator generator = LinkedDataProofGenerator.newInstance(SignatureType.JWS); - URI verificationMethod = issuerDoc.getVerificationMethods().get(0).getId(); - - JWSSignature2020 proof = - (JWSSignature2020) generator.createProof(builder.build(), verificationMethod, new x21559PrivateKey(privateKey)); - - - //Adding Proof to VC - builder.proof(proof); - - //Create Credential - return builder.build(); - } -} diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/DismantlerHoldersCredentialTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/DismantlerHoldersCredentialTest.java deleted file mode 100644 index 37dec0b5d..000000000 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/DismantlerHoldersCredentialTest.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * ******************************************************************************* - * Copyright (c) 2021,2023 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 - * ****************************************************************************** - */ - -package org.eclipse.tractusx.managedidentitywallets.vc; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; -import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; -import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; -import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType; -import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; -import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.IssuersCredential; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueDismantlerCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueMembershipCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.utils.AuthenticationUtils; -import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; -import org.eclipse.tractusx.ssi.lib.did.web.DidWebFactory; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; -import org.json.JSONException; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.*; -import org.springframework.test.context.ContextConfiguration; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {ManagedIdentityWalletsApplication.class}) -@ContextConfiguration(initializers = {TestContextInitializer.class}) -class DismantlerHoldersCredentialTest { - @Autowired - private HoldersCredentialRepository holdersCredentialRepository; - @Autowired - private WalletRepository walletRepository; - - @Autowired - private WalletKeyRepository walletKeyRepository; - - @Autowired - private TestRestTemplate restTemplate; - - @Autowired - private MIWSettings miwSettings; - - @Autowired - private IssuersCredentialRepository issuersCredentialRepository; - - - @Test - void issueDismantlerCredentialTest403() { - String bpn = TestUtils.getRandomBpmNumber(); - - HttpHeaders headers = AuthenticationUtils.getInvalidUserHttpHeaders(); - - IssueMembershipCredentialRequest request = IssueMembershipCredentialRequest.builder().bpn(bpn).build(); - - HttpEntity entity = new HttpEntity<>(request, headers); - - ResponseEntity response = restTemplate.exchange(RestURI.CREDENTIALS_ISSUER_DISMANTLER, HttpMethod.POST, entity, VerifiableCredential.class); - Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response.getStatusCode().value()); - } - - - @Test - void issueDismantlerCredentialToBaseWalletTest201() throws JSONException { - Wallet wallet = walletRepository.getByBpn(miwSettings.authorityWalletBpn()); - String oldSummaryCredentialId = TestUtils.getSummaryCredentialId(wallet.getDid(), holdersCredentialRepository); - ResponseEntity response = issueDismantlerCredential(miwSettings.authorityWalletBpn(), miwSettings.authorityWalletBpn()); - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - List credentials = holdersCredentialRepository.getByHolderDidAndType(miwSettings.authorityWalletDid(), MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL); - Assertions.assertFalse(credentials.isEmpty()); - Assertions.assertTrue(credentials.get(0).isSelfIssued()); //self issued must be false - Assertions.assertFalse(credentials.get(0).isStored()); //stored must be false - //check summary credential - TestUtils.checkSummaryCredential(miwSettings.authorityWalletDid(), wallet.getDid(), holdersCredentialRepository, issuersCredentialRepository, MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL, oldSummaryCredentialId); - } - - - @Test - void issueDismantlerCredentialTest201() throws JsonProcessingException, JSONException { - - String bpn = TestUtils.getRandomBpmNumber(); - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - String baseBpn = miwSettings.authorityWalletBpn(); - - //create wallet - Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn).getBody()); - String oldSummaryCredentialId = TestUtils.getSummaryCredentialId(wallet.getDid(), holdersCredentialRepository); - - ResponseEntity response = issueDismantlerCredential(bpn, did); - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - - ObjectMapper objectMapper = new ObjectMapper(); - Map map = objectMapper.readValue(response.getBody(), Map.class); - VerifiableCredential verifiableCredential = new VerifiableCredential(map); - Assertions.assertTrue(verifiableCredential.getTypes().contains(MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL)); - - TestUtils.checkVC(verifiableCredential, miwSettings); - - - Assertions.assertEquals(StringPool.VEHICLE_DISMANTLE, verifiableCredential.getCredentialSubject().get(0).get(StringPool.ACTIVITY_TYPE).toString()); - - List credentials = holdersCredentialRepository.getByHolderDidAndType(wallet.getDid(), MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL); - Assertions.assertFalse(credentials.isEmpty()); - TestUtils.checkVC(credentials.get(0).getData(), miwSettings); - Assertions.assertFalse(credentials.get(0).isSelfIssued()); //self issued must be false - Assertions.assertFalse(credentials.get(0).isStored()); //stored must be false - - VerifiableCredential data = credentials.get(0).getData(); - - Assertions.assertEquals(StringPool.VEHICLE_DISMANTLE, data.getCredentialSubject().get(0).get(StringPool.ACTIVITY_TYPE).toString()); - - //check in issuer wallet - List issuerVCs = issuersCredentialRepository.getByIssuerDidAndHolderDidAndType(miwSettings.authorityWalletDid(), wallet.getDid(), MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL); - Assertions.assertEquals(1, issuerVCs.size()); - TestUtils.checkVC(issuerVCs.get(0).getData(), miwSettings); - Assertions.assertEquals(StringPool.VEHICLE_DISMANTLE, issuerVCs.get(0).getData().getCredentialSubject().get(0).get(StringPool.ACTIVITY_TYPE).toString()); - - //check summary credential - TestUtils.checkSummaryCredential(miwSettings.authorityWalletDid(), wallet.getDid(), holdersCredentialRepository, issuersCredentialRepository, MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL, oldSummaryCredentialId); - } - - @Test - void issueDismantlerCredentialWithInvalidBpnAccess409() { - String bpn = TestUtils.getRandomBpmNumber(); - - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - - //create entry - Wallet wallet = TestUtils.createWallet(bpn, did, walletRepository); - - HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); //token must contain base wallet BPN - - IssueDismantlerCredentialRequest request = IssueDismantlerCredentialRequest.builder() - .activityType(StringPool.VEHICLE_DISMANTLE) - .bpn(bpn) - .allowedVehicleBrands(Set.of("BMW")) - .build(); - - - HttpEntity entity = new HttpEntity<>(request, headers); - - ResponseEntity response = restTemplate.exchange(RestURI.CREDENTIALS_ISSUER_DISMANTLER, HttpMethod.POST, entity, String.class); - Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response.getStatusCode().value()); - - } - - @Test - void issueDismantlerCredentialWithoutAllowedVehicleBrands() { - String bpn = TestUtils.getRandomBpmNumber(); - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - Wallet wallet = TestUtils.createWallet(bpn, did, walletRepository); - - HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()); //token must contain base wallet BPN - - IssueDismantlerCredentialRequest request = IssueDismantlerCredentialRequest.builder() - .activityType(StringPool.VEHICLE_DISMANTLE) - .bpn(bpn) - .allowedVehicleBrands(null) - .build(); - - HttpEntity entity = new HttpEntity<>(request, headers); - - ResponseEntity response = restTemplate.exchange(RestURI.CREDENTIALS_ISSUER_DISMANTLER, HttpMethod.POST, entity, String.class); - - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - } - - @Test - void issueDismantlerCredentialWithDuplicateBpn409() { - - String bpn = TestUtils.getRandomBpmNumber(); - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - - //create entry - Wallet wallet = TestUtils.createWallet(bpn, did, walletRepository); - ResponseEntity response = issueDismantlerCredential(bpn, did); - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - - //issue duplicate - ResponseEntity duplicateResponse = issueDismantlerCredential(bpn, did); - Assertions.assertEquals(HttpStatus.CONFLICT.value(), duplicateResponse.getStatusCode().value()); - } - - - private ResponseEntity issueDismantlerCredential(String bpn, String did) { - - - HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()); //token must contain base wallet BPN - - IssueDismantlerCredentialRequest request = IssueDismantlerCredentialRequest.builder() - .activityType(StringPool.VEHICLE_DISMANTLE) - .bpn(bpn) - .allowedVehicleBrands(Set.of("BMW")) - .build(); - - - HttpEntity entity = new HttpEntity<>(request, headers); - - return restTemplate.exchange(RestURI.CREDENTIALS_ISSUER_DISMANTLER, HttpMethod.POST, entity, String.class); - } -} \ No newline at end of file diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/FrameworkHoldersCredentialTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/FrameworkHoldersCredentialTest.java deleted file mode 100644 index 21e0ab498..000000000 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/FrameworkHoldersCredentialTest.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * ******************************************************************************* - * Copyright (c) 2021,2023 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 - * ****************************************************************************** - */ - -package org.eclipse.tractusx.managedidentitywallets.vc; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; -import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; -import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; -import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType; -import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; -import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.IssuersCredential; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueFrameworkCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueMembershipCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.utils.AuthenticationUtils; -import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; -import org.eclipse.tractusx.ssi.lib.did.web.DidWebFactory; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; -import org.json.JSONException; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.*; -import org.springframework.test.context.ContextConfiguration; - -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; - -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {ManagedIdentityWalletsApplication.class}) -@ContextConfiguration(initializers = {TestContextInitializer.class}) -class FrameworkHoldersCredentialTest { - @Autowired - private HoldersCredentialRepository holdersCredentialRepository; - @Autowired - private WalletRepository walletRepository; - - @Autowired - private TestRestTemplate restTemplate; - - @Autowired - private MIWSettings miwSettings; - - @Autowired - private IssuersCredentialRepository issuersCredentialRepository; - - - @Test - void issueFrameworkCredentialTest403() { - String bpn = TestUtils.getRandomBpmNumber(); - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - HttpHeaders headers = AuthenticationUtils.getInvalidUserHttpHeaders(); - - IssueMembershipCredentialRequest request = IssueMembershipCredentialRequest.builder().bpn(bpn).build(); - - HttpEntity entity = new HttpEntity<>(request, headers); - - ResponseEntity response = restTemplate.exchange(RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, HttpMethod.POST, entity, VerifiableCredential.class); - Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response.getStatusCode().value()); - } - - @Test - void issueFrameworkCredentialWithInvalidBpnAccessTest403() throws JsonProcessingException, JSONException { - String bpn = TestUtils.getRandomBpmNumber(); - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - TestUtils.createWallet(bpn, did, walletRepository); - - String type = "BehaviorTwinCredential"; - - HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); - - IssueFrameworkCredentialRequest twinRequest = TestUtils.getIssueFrameworkCredentialRequest(bpn, type); - - HttpEntity entity = new HttpEntity<>(twinRequest, headers); - - ResponseEntity response = restTemplate.exchange(RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, HttpMethod.POST, entity, String.class); - Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response.getStatusCode().value()); - } - - @Test - void issueFrameWorkVCToBaseWalletTest201() throws JSONException, JsonProcessingException { - String bpn = miwSettings.authorityWalletBpn(); - String type = "PcfCredential"; - //create wallet - Wallet wallet = walletRepository.getByBpn(miwSettings.authorityWalletBpn()); - String oldSummaryCredentialId = TestUtils.getSummaryCredentialId(wallet.getDid(), holdersCredentialRepository); - - HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()); - - IssueFrameworkCredentialRequest twinRequest = TestUtils.getIssueFrameworkCredentialRequest(bpn, type); - - HttpEntity entity = new HttpEntity<>(twinRequest, headers); - - ResponseEntity response = restTemplate.exchange(RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, HttpMethod.POST, entity, String.class); - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - - List credentials = holdersCredentialRepository.getByHolderDidAndType(miwSettings.authorityWalletDid(), MIWVerifiableCredentialType.USE_CASE_FRAMEWORK_CONDITION); - Assertions.assertFalse(credentials.isEmpty()); - - VerifiableCredential vcFromDB = credentials.get(0).getData(); - TestUtils.checkVC(vcFromDB, miwSettings); - - Assertions.assertFalse(credentials.get(0).isStored()); //stored must be false - Assertions.assertTrue(credentials.get(0).isSelfIssued()); //self issue must be false - - //check summary credential - TestUtils.checkSummaryCredential(miwSettings.authorityWalletDid(), wallet.getDid(), holdersCredentialRepository, issuersCredentialRepository, type, oldSummaryCredentialId); - } - - @ParameterizedTest - @MethodSource("getTypes") - void issueFrameWorkVCTest201(IssueFrameworkCredentialRequest request) throws JsonProcessingException, JSONException { - String bpn = request.getHolderIdentifier(); - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - - String type = request.getType(); - - createAndValidateVC(bpn, did, type); - //check in issuer tables - List issuerVCs = issuersCredentialRepository.getByIssuerDidAndHolderDidAndType(miwSettings.authorityWalletDid(), did, MIWVerifiableCredentialType.USE_CASE_FRAMEWORK_CONDITION); - Assertions.assertEquals(1, issuerVCs.size()); - } - - static Stream getTypes() { - return Stream.of( - IssueFrameworkCredentialRequest.builder().holderIdentifier(TestUtils.getRandomBpmNumber()).type("BehaviorTwinCredential").build(), - IssueFrameworkCredentialRequest.builder().holderIdentifier(TestUtils.getRandomBpmNumber()).type("PcfCredential").build(), - IssueFrameworkCredentialRequest.builder().holderIdentifier(TestUtils.getRandomBpmNumber()).type("QualityCredential").build(), - IssueFrameworkCredentialRequest.builder().holderIdentifier(TestUtils.getRandomBpmNumber()).type("ResiliencyCredential").build(), - IssueFrameworkCredentialRequest.builder().holderIdentifier(TestUtils.getRandomBpmNumber()).type("SustainabilityCredential").build(), - IssueFrameworkCredentialRequest.builder().holderIdentifier(TestUtils.getRandomBpmNumber()).type("TraceabilityCredential").build() - ); - } - - - @Test - @DisplayName("Issue framework with invalid type") - void issueFrameworkCredentialTest400() throws JsonProcessingException, JSONException { - String bpn = TestUtils.getRandomBpmNumber(); - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - Wallet wallet = TestUtils.createWallet(bpn, did, walletRepository); - - - String type = "cx-traceability1"; - - HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()); - - IssueFrameworkCredentialRequest twinRequest = TestUtils.getIssueFrameworkCredentialRequest(bpn, type); - - HttpEntity entity = new HttpEntity<>(twinRequest, headers); - - ResponseEntity response = restTemplate.exchange(RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, HttpMethod.POST, entity, String.class); - Assertions.assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusCode().value()); - - } - - private void createAndValidateVC(String bpn, String did, String type) throws JsonProcessingException { - //create wallet - String baseBpn = miwSettings.authorityWalletBpn(); - Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn).getBody()); - String oldSummaryCredentialId = TestUtils.getSummaryCredentialId(wallet.getDid(), holdersCredentialRepository); - - HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(miwSettings.authorityWalletBpn()); - - IssueFrameworkCredentialRequest twinRequest = TestUtils.getIssueFrameworkCredentialRequest(bpn, type); - - HttpEntity entity = new HttpEntity<>(twinRequest, headers); - - ResponseEntity response = restTemplate.exchange(RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, HttpMethod.POST, entity, String.class); - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - - validate(wallet, type, response, miwSettings, oldSummaryCredentialId); - - } - - private void validate(Wallet wallet, String type, ResponseEntity response, MIWSettings miwSettings, String oldSummaryCredentialId) throws JsonProcessingException { - ObjectMapper objectMapper = new ObjectMapper(); - Map map = objectMapper.readValue(response.getBody(), Map.class); - VerifiableCredential verifiableCredential = new VerifiableCredential(map); - Assertions.assertTrue(verifiableCredential.getTypes().contains(MIWVerifiableCredentialType.USE_CASE_FRAMEWORK_CONDITION)); - - TestUtils.checkVC(verifiableCredential, miwSettings); - - Assertions.assertEquals(verifiableCredential.getCredentialSubject().get(0).get(StringPool.TYPE), type); - Assertions.assertEquals(verifiableCredential.getCredentialSubject().get(0).get(StringPool.HOLDER_IDENTIFIER), wallet.getBpn()); - - Assertions.assertEquals(verifiableCredential.getCredentialSubject().get(0).get(StringPool.ID), wallet.getDid()); - - List credentials = holdersCredentialRepository.getByHolderDidAndType(wallet.getDid(), MIWVerifiableCredentialType.USE_CASE_FRAMEWORK_CONDITION); - Assertions.assertFalse(credentials.isEmpty()); - - VerifiableCredential vcFromDB = credentials.get(0).getData(); - TestUtils.checkVC(vcFromDB, miwSettings); - - Assertions.assertFalse(credentials.get(0).isStored()); //stored must be false - Assertions.assertFalse(credentials.get(0).isSelfIssued()); //self issue must be false - Assertions.assertEquals(vcFromDB.getCredentialSubject().get(0).get(StringPool.TYPE), type); - Assertions.assertEquals(vcFromDB.getCredentialSubject().get(0).get(StringPool.ID), wallet.getDid()); - Assertions.assertEquals(vcFromDB.getCredentialSubject().get(0).get(StringPool.HOLDER_IDENTIFIER), wallet.getBpn()); - - //check summary credential - TestUtils.checkSummaryCredential(miwSettings.authorityWalletDid(), wallet.getDid(), holdersCredentialRepository, issuersCredentialRepository, type, oldSummaryCredentialId); - } -} \ No newline at end of file diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/MembershipHoldersCredentialTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/MembershipHoldersCredentialTest.java deleted file mode 100644 index 9f2c41550..000000000 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/MembershipHoldersCredentialTest.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * ******************************************************************************* - * Copyright (c) 2021,2023 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 - * ****************************************************************************** - */ - -package org.eclipse.tractusx.managedidentitywallets.vc; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; -import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; -import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer; -import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType; -import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; -import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.IssuersCredential; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; -import org.eclipse.tractusx.managedidentitywallets.dto.IssueMembershipCredentialRequest; -import org.eclipse.tractusx.managedidentitywallets.utils.AuthenticationUtils; -import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; -import org.eclipse.tractusx.ssi.lib.did.web.DidWebFactory; -import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; -import org.jetbrains.annotations.NotNull; -import org.json.JSONException; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.*; -import org.springframework.test.context.ContextConfiguration; - -import java.util.List; -import java.util.Map; -import java.util.Objects; - -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {ManagedIdentityWalletsApplication.class}) -@ContextConfiguration(initializers = {TestContextInitializer.class}) -class MembershipHoldersCredentialTest { - @Autowired - private HoldersCredentialRepository holdersCredentialRepository; - @Autowired - private WalletRepository walletRepository; - - @Autowired - private TestRestTemplate restTemplate; - - @Autowired - private MIWSettings miwSettings; - - @Autowired - private IssuersCredentialRepository issuersCredentialRepository; - - @Autowired - private ObjectMapper objectMapper; - - - @Test - void issueMembershipCredentialTest403() { - String bpn = TestUtils.getRandomBpmNumber(); - - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - - HttpHeaders headers = AuthenticationUtils.getInvalidUserHttpHeaders(); - - IssueMembershipCredentialRequest request = IssueMembershipCredentialRequest.builder().bpn(bpn).build(); - - HttpEntity entity = new HttpEntity<>(request, headers); - - ResponseEntity response = restTemplate.exchange(RestURI.CREDENTIALS_ISSUER_MEMBERSHIP, HttpMethod.POST, entity, VerifiableCredential.class); - Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response.getStatusCode().value()); - } - - @Test - void testIssueSummeryVCAfterDeleteSummaryVCFromHolderWallet() throws JsonProcessingException { - String bpn = TestUtils.getRandomBpmNumber(); - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - String baseBpn = miwSettings.authorityWalletBpn(); - - // create wallet, in background bpn and summary credential generated - Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn).getBody()); - - List byHolderDid = holdersCredentialRepository.getByHolderDid(did); - - //delete all VC - holdersCredentialRepository.deleteAll(byHolderDid); - - //issue membership - ResponseEntity response = TestUtils.issueMembershipVC(restTemplate, bpn, miwSettings.authorityWalletBpn()); - Assertions.assertEquals(response.getStatusCode().value(), HttpStatus.CREATED.value()); - - //check summary VC in holder wallet - List summaryVcs = holdersCredentialRepository.getByHolderDidAndIssuerDidAndTypeAndStored(did, miwSettings.authorityWalletDid(), MIWVerifiableCredentialType.SUMMARY_CREDENTIAL, false); - Assertions.assertFalse(summaryVcs.isEmpty()); - - //check items, it should be 2 - List items = (List) summaryVcs.get(0).getData().getCredentialSubject().get(0).get(StringPool.ITEMS); - - Assertions.assertTrue(items.contains(MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL)); - Assertions.assertTrue(items.contains(MIWVerifiableCredentialType.BPN_CREDENTIAL)); - } - - @Test - void testStoredSummaryVCTest() throws JsonProcessingException { - String bpn = TestUtils.getRandomBpmNumber(); - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - String baseBpn = miwSettings.authorityWalletBpn(); - - // create wallet, in background bpn and summary credential generated - Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn).getBody()); - - - String vc = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", - "type": [ - "VerifiableCredential", - "SummaryCredential" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-06-02T12:00:00Z", - "expirationDate": "2022-06-16T18:56:59Z", - "credentialSubject": [{ - "id": "did:web:localhost:BPNL000000000000", - "holderIdentifier": "BPN of holder", - "type": "Summary-List", - "name": "CX-Credentials", - "items": [ - "MembershipCredential","DismantlerCredential","PcfCredential","SustainabilityCredential","QualityCredential","TraceabilityCredential","BehaviorTwinCredential","BpnCredential" - ], - "contract-templates": "https://public.catena-x.org/contracts/" - },{ - "name":"test name" - }], - "proof": { - "type": "Ed25519Signature2018", - "created": "2023-06-02T12:00:00Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:web:example.com#key-1", - "jws": "eyJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2MjM1NzA3NDEsImV4cCI6MTYyMzU3NDM0MSwianRpIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5YWJjIiwicHJvb2YiOnsiaWQiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIiwibmFtZSI6IkJlaXNwaWVsLU9yZ2FuaXNhdGlvbiJ9fQ.SignedExampleSignature" - } - } - """; - HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); - - Map map = objectMapper.readValue(vc.replace("##did", did), Map.class); - HttpEntity entity = new HttpEntity<>(map, headers); - - ResponseEntity response = restTemplate.exchange(RestURI.API_WALLETS_IDENTIFIER_CREDENTIALS, HttpMethod.POST, entity, Map.class, bpn); - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - - //issue membership - ResponseEntity response1 = TestUtils.issueMembershipVC(restTemplate, bpn, miwSettings.authorityWalletBpn()); - Assertions.assertEquals(HttpStatus.CREATED.value(), response1.getStatusCode().value()); - - //stored VC should not be deleted - List summaryCredential = holdersCredentialRepository.getByHolderDidAndIssuerDidAndTypeAndStored(wallet.getDid(), "did:web:localhost:BPNL000000000000", "SummaryCredential", true); - Assertions.assertFalse(summaryCredential.isEmpty()); - - } - - @Test - void issueMembershipCredentialToBaseWalletTest400() throws JsonProcessingException { - String bpn = TestUtils.getRandomBpmNumber(); - String baseBpn = miwSettings.authorityWalletBpn(); - - // create wallet, in background bpn and summary credential generated - Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn).getBody()); - - //add 2 subject in VC for testing - List vcs = issuersCredentialRepository.getByIssuerDidAndHolderDidAndType(miwSettings.authorityWalletDid(), wallet.getDid(), MIWVerifiableCredentialType.SUMMARY_CREDENTIAL); - - String vc = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", - "type": [ - "VerifiableCredential", - "SummaryCredential" - ], - "issuer": "did:web:localhost:BPNL000000000000", - "issuanceDate": "2023-06-02T12:00:00Z", - "expirationDate": "2022-06-16T18:56:59Z", - "credentialSubject": [{ - "id": "did:web:localhost:BPNL000000000000", - "holderIdentifier": "BPN of holder", - "type": "Summary-List", - "name": "CX-Credentials", - "items": [ - "MembershipCredential","DismantlerCredential","PcfCredential","SustainabilityCredential","QualityCredential","TraceabilityCredential","BehaviorTwinCredential","BpnCredential" - ], - "contract-templates": "https://public.catena-x.org/contracts/" - },{ - "name":"test name" - }], - "proof": { - "type": "Ed25519Signature2018", - "created": "2023-06-02T12:00:00Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:web:example.com#key-1", - "jws": "eyJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2MjM1NzA3NDEsImV4cCI6MTYyMzU3NDM0MSwianRpIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5YWJjIiwicHJvb2YiOnsiaWQiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIiwibmFtZSI6IkJlaXNwaWVsLU9yZ2FuaXNhdGlvbiJ9fQ.SignedExampleSignature" - } - } - """; - VerifiableCredential verifiableCredential = new VerifiableCredential(new ObjectMapper().readValue(vc, Map.class)); - vcs.get(0).setData(verifiableCredential); - - issuersCredentialRepository.save(vcs.get(0)); - - //Check if we do not have items in subject - ResponseEntity response = TestUtils.issueMembershipVC(restTemplate, bpn, miwSettings.authorityWalletBpn()); - Assertions.assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusCode().value()); - - vcs.get(0).getData().getCredentialSubject().remove(1); - vcs.get(0).getData().getCredentialSubject().get(0).remove(StringPool.ITEMS); - issuersCredentialRepository.save(vcs.get(0)); - } - - - @Test - void issueMembershipCredentialToBaseWalletTest201() throws JsonProcessingException, JSONException { - - Wallet wallet = walletRepository.getByBpn(miwSettings.authorityWalletBpn()); - String oldSummaryCredentialId = TestUtils.getSummaryCredentialId(wallet.getDid(), holdersCredentialRepository); - - ResponseEntity response = TestUtils.issueMembershipVC(restTemplate, miwSettings.authorityWalletBpn(), miwSettings.authorityWalletBpn()); - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - - VerifiableCredential verifiableCredential = getVerifiableCredential(response); - - TestUtils.checkVC(verifiableCredential, miwSettings); - - validateTypes(verifiableCredential); - - List holderVCs = holdersCredentialRepository.getByHolderDidAndType(wallet.getDid(), MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL); - Assertions.assertFalse(holderVCs.isEmpty()); - - TestUtils.checkVC(holderVCs.get(0).getData(), miwSettings); - Assertions.assertTrue(holderVCs.get(0).isSelfIssued()); //must be self issued true - Assertions.assertFalse(holderVCs.get(0).isStored()); //store must be false - - //check in issuer tables - List issuerVCs = issuersCredentialRepository.getByIssuerDidAndHolderDidAndType(miwSettings.authorityWalletDid(), wallet.getDid(), MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL); - Assertions.assertEquals(1, issuerVCs.size()); - TestUtils.checkVC(issuerVCs.get(0).getData(), miwSettings); - //check summary credential - TestUtils.checkSummaryCredential(miwSettings.authorityWalletDid(), wallet.getDid(), holdersCredentialRepository, issuersCredentialRepository, MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL, oldSummaryCredentialId); - } - - - @Test - void issueMembershipCredentialTest201() throws JsonProcessingException, JSONException { - - String bpn = TestUtils.getRandomBpmNumber(); - String baseBpn = miwSettings.authorityWalletBpn(); - - //create wallet - Wallet wallet = TestUtils.getWalletFromString(TestUtils.createWallet(bpn, bpn, restTemplate, baseBpn).getBody()); - String oldSummaryCredentialId = TestUtils.getSummaryCredentialId(wallet.getDid(), holdersCredentialRepository); - - ResponseEntity response = TestUtils.issueMembershipVC(restTemplate, bpn, miwSettings.authorityWalletBpn()); - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - - VerifiableCredential verifiableCredential = getVerifiableCredential(response); - - TestUtils.checkVC(verifiableCredential, miwSettings); - - validateTypes(verifiableCredential); - - List holderVCs = holdersCredentialRepository.getByHolderDidAndType(wallet.getDid(), MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL); - Assertions.assertFalse(holderVCs.isEmpty()); - Assertions.assertFalse(holderVCs.get(0).isSelfIssued()); //must be self issued false - Assertions.assertFalse(holderVCs.get(0).isStored()); //store must be false - - - TestUtils.checkVC(holderVCs.get(0).getData(), miwSettings); - - //check in issuer tables - List issuerVCs = issuersCredentialRepository.getByIssuerDidAndHolderDidAndType(miwSettings.authorityWalletDid(), wallet.getDid(), MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL); - Assertions.assertEquals(1, issuerVCs.size()); - TestUtils.checkVC(issuerVCs.get(0).getData(), miwSettings); - - //check summary credential - TestUtils.checkSummaryCredential(miwSettings.authorityWalletDid(), wallet.getDid(), holdersCredentialRepository, issuersCredentialRepository, MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL, oldSummaryCredentialId); - } - - - @Test - void issueMembershipCredentialWithInvalidBpnAccess409() { - String bpn = TestUtils.getRandomBpmNumber(); - - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - - //save wallet - TestUtils.createWallet(bpn, did, walletRepository); - - HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn); - IssueMembershipCredentialRequest request = IssueMembershipCredentialRequest.builder().bpn(bpn).build(); - HttpEntity entity = new HttpEntity<>(request, headers); - - ResponseEntity response = restTemplate.exchange(RestURI.CREDENTIALS_ISSUER_MEMBERSHIP, HttpMethod.POST, entity, String.class); - Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response.getStatusCode().value()); - } - - @Test - void issueMembershipCredentialWithDuplicateBpn409() { - - String bpn = TestUtils.getRandomBpmNumber(); - - String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); - - //save wallet - TestUtils.createWallet(bpn, did, walletRepository); - - ResponseEntity response = TestUtils.issueMembershipVC(restTemplate, bpn, miwSettings.authorityWalletBpn()); - Assertions.assertEquals(HttpStatus.CREATED.value(), response.getStatusCode().value()); - - ResponseEntity duplicateResponse = TestUtils.issueMembershipVC(restTemplate, bpn, miwSettings.authorityWalletBpn()); - - Assertions.assertEquals(HttpStatus.CONFLICT.value(), duplicateResponse.getStatusCode().value()); - } - - - @NotNull - private VerifiableCredential getVerifiableCredential(ResponseEntity response) throws JsonProcessingException { - Map map = objectMapper.readValue(response.getBody(), Map.class); - return new VerifiableCredential(map); - } - - private void validateTypes(VerifiableCredential verifiableCredential) { - Assertions.assertTrue(verifiableCredential.getTypes().contains(MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL)); - Assertions.assertEquals("Test-X", verifiableCredential.getCredentialSubject().get(0).get(StringPool.MEMBER_OF)); - } -} diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml deleted file mode 100644 index 8cef28cf0..000000000 --- a/src/test/resources/application-test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -spring: - main: - allow-circular-references: true -miw: - host: localhost - encryptionKey: ohb7echohNe3zoo1so0eiC2phue4liux - authorityWalletDid: did:web:localhost:BPNL000000000000 - authorityWalletName: Catena-x - authorityWalletBpn: BPNL000000000000 - security: - enabled: true - realm: miw_test - clientId: miw_private_client \ No newline at end of file