diff --git a/.github/actions/run-deployment-test/action.yml b/.github/actions/run-deployment-test/action.yml index aebe8c38a..0564771c3 100644 --- a/.github/actions/run-deployment-test/action.yml +++ b/.github/actions/run-deployment-test/action.yml @@ -40,10 +40,6 @@ inputs: required: true description: "The directory that contains the docker file, e.g. edc-controlplane/edc-runtime-memory" - values_file: - required: true - description: "A yaml file that contains the values for the test installation. will be modified!" - runs: using: "composite" steps: @@ -76,11 +72,6 @@ runs: ################################################### # Install the test infrastructure ################################################### - - name: "Generate test credentials" - shell: bash - run: |- - sh -c "edc-tests/deployment/src/main/resources/prepare-test.sh \ - ${{ inputs.values_file }}" - name: Install Runtime shell: bash diff --git a/.github/actions/setup-java/action.yml b/.github/actions/setup-java/action.yml index 76fe240d4..bace6be67 100644 --- a/.github/actions/setup-java/action.yml +++ b/.github/actions/setup-java/action.yml @@ -26,7 +26,7 @@ runs: using: "composite" steps: - name: Setup JDK 17 - uses: actions/setup-java@v3.13.0 + uses: actions/setup-java@v4.1.0 with: java-version: '17' distribution: 'temurin' diff --git a/.github/actions/setup-memory-runtime/action.yml b/.github/actions/setup-memory-runtime/action.yml new file mode 100644 index 000000000..3de89f767 --- /dev/null +++ b/.github/actions/setup-memory-runtime/action.yml @@ -0,0 +1,73 @@ +################################################################################# +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +################################################################################# + + +--- +name: "Setup TractusX EDC in memory runtime" +description: "Setup TractusX EDC in memory runtime" +runs: + using: "composite" + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-java + + - uses: actions/checkout@v4 + + - name: Dockerize TractusX EDC in memory distribution + shell: bash + run: | + ./gradlew :edc-controlplane:edc-runtime-memory:dockerize + + - name: Starting MIW, Keycloak and Postgres Servers + shell: bash + run: | + cd edc-tests/miw-tests/src/test/resources/docker-environment + docker compose up -d --wait + + - uses: nick-fields/retry@v3 + name: Wait for MIW + with: + timeout_minutes: 5 + max_attempts: 3 + command: | + code=$(curl -IL -sw "%{http_code}" http://localhost:8000/api/actuator/health -o /dev/null) + if [ "$code" -ne "401" ]; then + echo "MIW not ready yet, status = $code" + exit 1; + fi + + - name: Starting in memory TractusX EDC + shell: bash + run: | + cd dast + docker compose up -d + + + - uses: nick-fields/retry@v3 + name: Wait for TractusX EDC + with: + timeout_minutes: 5 + max_attempts: 3 + command: | + code=$(curl -IL -sw "%{http_code}" http://localhost:8181/api/check/health -o /dev/null) + if [ "$code" -ne "401" ]; then + echo "TractusX EDC not ready yet, status = $code" + docker logs dast-edc-runtime-1 + exit 1; + fi \ No newline at end of file diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml new file mode 100644 index 000000000..00114c621 --- /dev/null +++ b/.github/workflows/codeql.yaml @@ -0,0 +1,84 @@ +################################################################################# +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +################################################################################# + +name: "CodeQL" + +on: + push: + branches: [ "main" ] + paths-ignore: + - "**/*.md" + - "**/*.txt" + pull_request: + branches: [ "main" ] + paths-ignore: + - "**/*.md" + - "**/*.txt" + schedule: + - cron: "0 0 * * 0" + workflow_dispatch: + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + timeout-minutes: 360 + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ "java" ] # Define languages here + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file + # By default, queries listed here will override any specified in a config file + # Prefix the list here with "+" to use these queries and those in the config file + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # Use +security-extended,security-and-quality for wider security and better code quality + queries: +security-extended,security-and-quality + + + # build only production code, no test sources + - uses: ./.github/actions/setup-java + - name: Build Production Code + run: | + ./gradlew compileJava --no-daemon + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" + fail-on: error \ No newline at end of file diff --git a/.github/workflows/dast-scan.yaml b/.github/workflows/dast-scan.yaml new file mode 100644 index 000000000..4f7693c50 --- /dev/null +++ b/.github/workflows/dast-scan.yaml @@ -0,0 +1,123 @@ +################################################################################# +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +################################################################################# + +name: ZAP_ALL + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +jobs: + zap_scan: + runs-on: ubuntu-latest + name: OWASP ZAP API Scan + + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-memory-runtime + + - name: Fetch MIW VP token + run: ./dast/fetch-token.sh + + - name: API Catalog Request Test + id: catalog_request + run: | + dsp_response=$(curl -w "%{http_code}" --request POST \ + --url http://localhost:8282/api/v1/dsp/catalog/request \ + --header "Authorization: $VP_TOKEN" \ + --header 'Content-Type: application/json' \ + --data '{ + "@type": "dspace:CatalogRequestMessage", + "dspace:filter": { + "@type": "QuerySpec", + "limit": 50, + "offset": 0, + "sortOrder": "ASC", + "filterExpression": [] + }, + "@context": { + "@vocab": "https://w3id.org/edc/v0.0.1/ns/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "tx": "https://w3id.org/tractusx/v0.0.1/ns/", + "dcat": "http://www.w3.org/ns/dcat#", + "dct": "https://purl.org/dc/terms/", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + } + }') + + echo "Response: $dsp_response" + + - name: Generating report skeletons + if: success() || failure() + run: | + touch API_report.html + chmod a+w API_report.html + ls -lrt + + - name: Run ZAP API scan + run: | + set +e + + echo "Pulling ZAP image..." + docker pull ghcr.io/zaproxy/zaproxy:stable -q + echo "Starting ZAP Docker container..." + docker run --network miw-net -v ${GITHUB_WORKSPACE}:/zap/wrk/:rw ghcr.io/zaproxy/zaproxy:stable zap-api-scan.py -t http://edc-runtime:8282/api/v1/dsp -f openapi -r API_report.html -T 1 + + echo "... done." + + - name: Upload HTML report + if: success() || failure() + uses: actions/upload-artifact@v4 + with: + name: ZAP_API scan report + path: ./API_report.html + + zap_scan2: + runs-on: ubuntu-latest + name: OWASP ZAP FULL Scan + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-memory-runtime + + - name: Generating report skeletons + if: success() || failure() + run: | + touch fullscan_report.html + chmod a+w fullscan_report.html + ls -lrt + + - name: Perform ZAP FULL scan + run: | + set +e + + echo "Pulling ZAP image..." + docker pull ghcr.io/zaproxy/zaproxy:stable -q + echo "Starting ZAP Docker container..." + docker run --network miw-net -v ${GITHUB_WORKSPACE}:/zap/wrk/:rw ghcr.io/zaproxy/zaproxy:stable zap-full-scan.py -t http://edc-runtime:8282/api/v1/dsp -r fullscan_report.html -T 1 + + echo "... done." + + - name: Upload HTML report + if: success() || failure() + uses: actions/upload-artifact@v4 + with: + name: ZAP_FULL scan report + path: ./fullscan_report.html \ No newline at end of file diff --git a/.github/workflows/deployment-test.yaml b/.github/workflows/deployment-test.yaml index e5208e7fa..1ab1187e1 100644 --- a/.github/workflows/deployment-test.yaml +++ b/.github/workflows/deployment-test.yaml @@ -63,11 +63,9 @@ jobs: with: imagename: edc-runtime-memory rootDir: edc-controlplane/edc-runtime-memory - values_file: edc-tests/deployment/src/main/resources/helm/tractusx-connector-memory-test.yaml helm_command: |- helm install tx-inmem charts/tractusx-connector-memory \ -f edc-tests/deployment/src/main/resources/helm/tractusx-connector-memory-test.yaml \ - --set vault.secrets="client-secret:$(cat client.secret)" \ --wait-for-jobs --timeout=120s --dependency-update # wait for the pod to become ready @@ -87,7 +85,6 @@ jobs: with: imagename: "edc-controlplane-postgresql-hashicorp-vault edc-dataplane-hashicorp-vault" rootDir: "." - values_file: edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml helm_command: |- helm install tx-prod charts/tractusx-connector \ -f edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml \ @@ -121,7 +118,6 @@ jobs: with: imagename: "edc-controlplane-postgresql-azure-vault edc-dataplane-azure-vault" rootDir: "." - values_file: edc-tests/deployment/src/main/resources/helm/tractusx-connector-azure-vault-test.yaml helm_command: |- az keyvault secret set --vault-name ${{ secrets.AZURE_VAULT_NAME }} --name aes-keys --value "$(cat aes.key)" > /dev/null az keyvault secret set --vault-name ${{ secrets.AZURE_VAULT_NAME }} --name client-secret --value "$(cat client.secret)" > /dev/null diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 99e7468e7..3133badfa 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -54,7 +54,7 @@ jobs: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: Bump version in /charts - uses: mikefarah/yq@v4.40.5 + uses: mikefarah/yq@v4.43.1 with: cmd: |- find charts -name Chart.yaml -maxdepth 3 | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index e83831a5a..0db9c033a 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -58,11 +58,11 @@ jobs: steps: - uses: actions/checkout@v4 - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@0.12.0 + uses: aquasecurity/trivy-action@0.19.0 with: scan-type: "config" # ignore-unfixed: true - exit-code: "1" + exit-code: "0" hide-progress: false format: "sarif" output: "trivy-results-config.sarif" @@ -102,12 +102,12 @@ jobs: ## the next two steps will only execute if the image exists check was successful - name: Run Trivy vulnerability scanner if: success() && steps.imageCheck.outcome != 'failure' - uses: aquasecurity/trivy-action@0.12.0 + uses: aquasecurity/trivy-action@0.19.0 with: image-ref: "tractusx/${{ matrix.image }}:sha-${{ needs.git-sha7.outputs.value }}" format: "sarif" output: "trivy-results-${{ matrix.image }}.sarif" - exit-code: "1" + exit-code: "0" severity: "CRITICAL,HIGH" timeout: "10m0s" - name: Upload Trivy scan results to GitHub Security tab diff --git a/.github/workflows/upgradeability-test.yaml b/.github/workflows/upgradeability-test.yaml index ce2015790..a7f3749a4 100644 --- a/.github/workflows/upgradeability-test.yaml +++ b/.github/workflows/upgradeability-test.yaml @@ -56,26 +56,24 @@ jobs: version: 'v1.28.2' - name: Create k8s Kind Cluster - uses: helm/kind-action@v1.8.0 + uses: helm/kind-action@v1.9.0 - name: "Update helm repo" run: | helm repo add tractusx https://eclipse-tractusx.github.io/charts/dev helm repo update tractusx + ## Skip 0.6.0 when doing the compatibility check ref https://github.com/eclipse-tractusx/tractusx-edc/issues/1082 - name: "Get latest released version" id: get-version run: | - RELEASED_VERSION=$(helm search repo tractusx/tractusx-connector -o json | jq -r '.[0].version') + RELEASED_VERSION=$(helm search repo tractusx/tractusx-connector -l -o json | jq -r 'map(select(.version !="0.6.0")) | first | .version') echo "Last official release is $RELEASED_VERSION" echo "RELEASE=$RELEASED_VERSION" >> $GITHUB_ENV exit 0 - name: "Install latest release" run: | - sh -c "edc-tests/deployment/src/main/resources/prepare-test.sh \ - edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml" - helm upgrade --install tx-prod tractusx/tractusx-connector \ -f edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml \ --set "controlplane.image.tag=$RELEASE" \ diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml deleted file mode 100644 index 2ed6cae51..000000000 --- a/.github/workflows/veracode.yaml +++ /dev/null @@ -1,92 +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 -################################################################################# - - ---- -name: "Veracode" - -on: - schedule: - - cron: '0 2 * * *' - workflow_dispatch: - -jobs: - secret-presence: - runs-on: ubuntu-latest - outputs: - ORG_VERACODE_API_ID: ${{ steps.secret-presence.outputs.ORG_VERACODE_API_ID }} - ORG_VERACODE_API_KEY: ${{ steps.secret-presence.outputs.ORG_VERACODE_API_KEY }} - steps: - - name: Check whether secrets exist - id: secret-presence - run: | - [ ! -z "${{ secrets.ORG_VERACODE_API_ID }}" ] && echo "ORG_VERACODE_API_ID=true" >> $GITHUB_OUTPUT - [ ! -z "${{ secrets.ORG_VERACODE_API_KEY }}" ] && echo "ORG_VERACODE_API_KEY=true" >> $GITHUB_OUTPUT - exit 0 - - verify-formatting: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: ./.github/actions/setup-java - - name: Run Checkstyle - run: | - ./gradlew checkstyleMain checkstyleTest - - build: - runs-on: ubuntu-latest - needs: [ secret-presence, verify-formatting ] - permissions: - contents: read - strategy: - fail-fast: false - matrix: - variant: [ { dir: edc-controlplane, name: edc-runtime-memory }, - { dir: edc-controlplane, name: edc-controlplane-postgresql-hashicorp-vault }, - { dir: edc-controlplane, name: edc-controlplane-postgresql-azure-vault }, - { dir: edc-dataplane, name: edc-dataplane-azure-vault }, - { dir: edc-dataplane, name: edc-dataplane-hashicorp-vault } ] - steps: - # Set-Up - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-java - # Build - - name: Build ${{ matrix.variant.name }} - run: |- - ./gradlew -p ${{ matrix.variant.dir }}/${{ matrix.variant.name }} shadowJar - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - name: Tar gzip files for veracode upload - run: |- - tar -czvf ${{ matrix.variant.dir }}/${{ matrix.variant.name }}/build/libs/${{ matrix.variant.name }}.tar.gz ${{ matrix.variant.dir }}/${{ matrix.variant.name }}/build/libs/${{ matrix.variant.name }}.jar - - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@v1.0 - if: | - needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY - continue-on-error: true - with: - appname: tractusx-edc/${{ matrix.variant.name }} - createprofile: true - version: ${{ matrix.variant.name }}-${{ github.sha }} - filepath: ${{ matrix.variant.dir }}/${{ matrix.variant.name }}/build/libs/${{ matrix.variant.name }}.tar.gz - vid: ${{ secrets.ORG_VERACODE_API_ID }} - vkey: ${{ secrets.ORG_VERACODE_API_KEY }} diff --git a/edc-tests/miw-tests/src/test/resources/docker-environment/keycloak/health-check.sh b/.github/workflows/verify-dim.yaml old mode 100755 new mode 100644 similarity index 73% rename from edc-tests/miw-tests/src/test/resources/docker-environment/keycloak/health-check.sh rename to .github/workflows/verify-dim.yaml index 27efb3eea..091c788f8 --- a/edc-tests/miw-tests/src/test/resources/docker-environment/keycloak/health-check.sh +++ b/.github/workflows/verify-dim.yaml @@ -1,4 +1,3 @@ -#!/bin/bash ################################################################################# # Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) # @@ -18,16 +17,20 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# -# health check approach taken from https://stackoverflow.com/a/75693900/7079724 +name: "Verify DIM" -exec 3<>/dev/tcp/localhost/8080 +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: -echo -e "GET /health/ready HTTP/1.1\nhost: localhost:8080\n" >&3 +jobs: + dim-integration-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 -timeout --preserve-status 1 cat <&3 | grep -m 1 status | grep -m 1 UP -ERROR=$? + - uses: ./.github/actions/setup-java -exec 3<&- -exec 3>&- - -exit $ERROR + - name: Run DIM Integration tests + run: ./gradlew test -DincludeTags="DimIntegrationTest" \ No newline at end of file diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index 27510f350..258c67bcf 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -79,7 +79,6 @@ jobs: unit-tests: runs-on: ubuntu-latest - needs: [ verify-formatting, verify-license-headers ] steps: - uses: actions/checkout@v4 @@ -90,7 +89,6 @@ jobs: integration-tests: runs-on: ubuntu-latest - needs: [ verify-formatting, verify-license-headers ] steps: - uses: actions/checkout@v4 @@ -101,7 +99,6 @@ jobs: api-tests: runs-on: ubuntu-latest - needs: [ verify-formatting, verify-license-headers ] steps: - uses: actions/checkout@v4 @@ -112,20 +109,27 @@ jobs: end-to-end-tests: runs-on: ubuntu-latest - needs: [ verify-formatting, verify-license-headers ] + strategy: + fail-fast: false + matrix: + variant: [ { dir: edc-tests/edc-controlplane/catalog-tests }, + { dir: edc-tests/edc-controlplane/edr-api-tests }, + { dir: edc-tests/edc-controlplane/iatp-tests }, + { dir: edc-tests/edc-controlplane/policy-tests }, + { dir: edc-tests/edc-controlplane/transfer-tests }, + { dir: edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests } + ] steps: - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-java - - name: Run E2E tests - run: ./gradlew test -DincludeTags="EndToEndTest" -PverboseTest=true + - name: Run E2E tests (${{ matrix.variant.dir }}) + run: | + ./gradlew compileJava compileTestJava + ./gradlew -p ${{ matrix.variant.dir }} test -DincludeTags="EndToEndTest" postgres-tests: runs-on: ubuntu-latest - - needs: [ verify-formatting, verify-license-headers ] - services: postgres: image: postgres:14.2 @@ -139,54 +143,16 @@ jobs: - uses: ./.github/actions/setup-java - name: Run Postgresql E2E tests - run: ./gradlew test -DincludeTags="PostgresqlIntegrationTest" -PverboseTest=true + run: ./gradlew test -DincludeTags="PostgresqlIntegrationTest" dataplane-tests: runs-on: ubuntu-latest - needs: [ verify-formatting, verify-license-headers ] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-java - name: Run Azure/S3 dataplane tests - run: ./gradlew -p edc-tests/edc-dataplane test -DincludeTags="AzureCosmosDbIntegrationTest,AwsS3IntegrationTest" - - miw-integration-tests: - runs-on: ubuntu-latest - needs: [ verify-formatting, verify-license-headers ] - - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-java - - - uses: actions/checkout@v4 - - name: Starting MIW, Keycloak and Postgres Servers - run: | - cd edc-tests/miw-tests/src/test/resources/docker-environment - docker compose up --wait - - - uses: nick-fields/retry@v3 - name: Wait for MIW - with: - timeout_minutes: 5 - max_attempts: 3 - command: | - code=$(curl -IL -sw "%{http_code}" http://localhost:8000/api/actuator/health -o /dev/null) - if [ "$code" -ne "401" ]; then - echo "MIW not ready yet, status = $code" - exit 1; - fi - - - name: Seed test data - run: | - docker exec docker-environment-postgres-1 /opt/seed.sh - - - name: Run MIW Integration tests - run: | - ./gradlew -p edc-tests/miw-tests test -DincludeTags="MiwIntegrationTest" -PverboseTest=true - - name: Run SSI E2E tests run: | - pwd ./gradlew compileJava compileTestJava - ./gradlew -p edc-tests/e2e-tests test -DincludeTags="MiwIntegrationTest" -PverboseTest=true + ./gradlew -p edc-tests/edc-dataplane test -DincludeTags="AzureCosmosDbIntegrationTest,AwsS3IntegrationTest" diff --git a/DEPENDENCIES b/DEPENDENCIES index 4ed963509..5f23c4b7d 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -3,81 +3,117 @@ maven/mavencentral/com.apicatalog/iron-ed25519-cryptosuite-2020/0.8.1, Apache-2. maven/mavencentral/com.apicatalog/iron-verifiable-credentials/0.8.1, Apache-2.0, approved, #9234 maven/mavencentral/com.apicatalog/titanium-json-ld/1.0.0, Apache-2.0, approved, clearlydefined maven/mavencentral/com.apicatalog/titanium-json-ld/1.3.1, Apache-2.0, approved, #8912 -maven/mavencentral/com.apicatalog/titanium-json-ld/1.3.3, Apache-2.0, approved, #8912 +maven/mavencentral/com.apicatalog/titanium-json-ld/1.4.0, Apache-2.0, approved, #13683 maven/mavencentral/com.azure/azure-core-http-netty/1.13.11, MIT AND Apache-2.0, approved, #7948 -maven/mavencentral/com.azure/azure-core-http-netty/1.13.9, MIT AND Apache-2.0, approved, #7948 -maven/mavencentral/com.azure/azure-core-http-netty/1.14.0, , restricted, clearlydefined -maven/mavencentral/com.azure/azure-core/1.44.1, MIT, approved, clearlydefined +maven/mavencentral/com.azure/azure-core-http-netty/1.14.0, MIT AND Apache-2.0, approved, #13238 +maven/mavencentral/com.azure/azure-core-http-netty/1.14.1, MIT AND Apache-2.0, approved, #13238 maven/mavencentral/com.azure/azure-core/1.45.1, MIT AND Apache-2.0, approved, #11845 -maven/mavencentral/com.azure/azure-core/1.46.0, , restricted, clearlydefined -maven/mavencentral/com.azure/azure-identity/1.11.1, MIT, approved, clearlydefined -maven/mavencentral/com.azure/azure-identity/1.11.2, , restricted, clearlydefined +maven/mavencentral/com.azure/azure-core/1.46.0, MIT AND Apache-2.0, approved, #13234 +maven/mavencentral/com.azure/azure-core/1.47.0, MIT AND Apache-2.0, approved, #13678 +maven/mavencentral/com.azure/azure-identity/1.11.2, MIT AND Apache-2.0, approved, #13237 +maven/mavencentral/com.azure/azure-identity/1.11.4, MIT AND Apache-2.0, approved, #13237 maven/mavencentral/com.azure/azure-json/1.1.0, MIT AND Apache-2.0, approved, #10547 maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.7.3, MIT, approved, #10868 -maven/mavencentral/com.azure/azure-storage-blob/12.24.1, MIT, approved, #10568 -maven/mavencentral/com.azure/azure-storage-common/12.23.1, MIT, approved, #10569 -maven/mavencentral/com.azure/azure-storage-internal-avro/12.9.1, MIT, approved, #10560 +maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.8.1, MIT, approved, #13690 +maven/mavencentral/com.azure/azure-storage-blob/12.25.2, MIT, approved, #13400 +maven/mavencentral/com.azure/azure-storage-common/12.24.2, MIT, approved, #13402 +maven/mavencentral/com.azure/azure-storage-internal-avro/12.10.2, MIT, approved, #13399 +maven/mavencentral/com.ethlo.time/itu/1.7.0, Apache-2.0, approved, clearlydefined maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.10.3, Apache-2.0, approved, CQ21280 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.13.5, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.14.0, Apache-2.0, approved, #5303 +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.14.1, Apache-2.0, approved, #5303 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.1, Apache-2.0, approved, #7947 -maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.2, Apache-2.0, approved, #7947 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.3, Apache-2.0, approved, #7947 -maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.16.1, Apache-2.0, approved, #11606 +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.16.2, Apache-2.0, approved, #11606 +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.17.0, Apache-2.0, approved, #13672 maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.13.5, Apache-2.0, approved, #2133 +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.14.1, Apache-2.0 AND MIT, approved, #4303 maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.15.1, MIT AND Apache-2.0, approved, #7932 -maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.16.1, Apache-2.0 AND MIT, approved, #11602 +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.17.0, , approved, #13665 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.11.0, Apache-2.0, approved, CQ23093 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.4.2, Apache-2.0, approved, #2134 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.5, Apache-2.0, approved, #2134 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.14.0, Apache-2.0, approved, #4105 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.14.1, Apache-2.0, approved, #4105 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.14.2, Apache-2.0, approved, #4105 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.15.1, Apache-2.0, approved, #7934 -maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.15.2, Apache-2.0, approved, #7934 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.15.3, Apache-2.0, approved, #7934 -maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.16.1, Apache-2.0, approved, #11605 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.16.2, Apache-2.0, approved, #11605 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.17.0, Apache-2.0, approved, #13671 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.15.2, Apache-2.0, approved, #9160 -maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.16.1, Apache-2.0, approved, #13145 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.17.0, Apache-2.0, approved, #14192 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.13.5, Apache-2.0, approved, #3768 -maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.16.1, Apache-2.0, approved, #12438 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.17.0, Apache-2.0, approved, #13666 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.14.0, Apache-2.0, approved, #5933 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.15.1, Apache-2.0, approved, #8802 -maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.15.2, Apache-2.0, approved, #8802 -maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.16.1, Apache-2.0, approved, #11855 -maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jakarta-jsonp/2.16.1, Apache-2.0, approved, #11854 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.16.2, Apache-2.0, approved, #11855 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.17.0, Apache-2.0, approved, #13669 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jakarta-jsonp/2.17.0, Apache-2.0, approved, #14161 maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.13.5, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.14.0, Apache-2.0, approved, #4699 maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.1, Apache-2.0, approved, #7930 -maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.2, Apache-2.0, approved, #7930 -maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.16.1, Apache-2.0, approved, #11853 -maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-base/2.16.1, Apache-2.0, approved, #11851 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.16.2, Apache-2.0, approved, #11853 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.17.0, Apache-2.0, approved, #14160 +maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-base/2.17.0, Apache-2.0, approved, #14194 maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.15.1, Apache-2.0, approved, #9236 -maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.15.2, Apache-2.0, approved, #9236 -maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.16.1, Apache-2.0, approved, #11858 +maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.16.2, Apache-2.0, approved, #11858 +maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.17.0, Apache-2.0, approved, #14195 maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.15.3, Apache-2.0, approved, #9241 -maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.16.1, Apache-2.0, approved, #11856 +maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.17.0, Apache-2.0, approved, #13668 maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.15.1, Apache-2.0, approved, #7929 -maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.16.1, Apache-2.0, approved, #11852 -maven/mavencentral/com.fasterxml.woodstox/woodstox-core/6.5.1, Apache-2.0, approved, #7950 -maven/mavencentral/com.github.docker-java/docker-java-api/3.3.4, Apache-2.0, approved, #10346 -maven/mavencentral/com.github.docker-java/docker-java-transport-zerodep/3.3.4, Apache-2.0 AND (Apache-2.0 AND BSD-3-Clause), approved, #7946 -maven/mavencentral/com.github.docker-java/docker-java-transport/3.3.4, Apache-2.0, approved, #7942 +maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.17.0, Apache-2.0, approved, #14162 +maven/mavencentral/com.fasterxml.uuid/java-uuid-generator/4.1.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.woodstox/woodstox-core/6.6.1, Apache-2.0, approved, #12789 +maven/mavencentral/com.github.cliftonlabs/json-simple/3.0.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.github.docker-java/docker-java-api/3.3.6, Apache-2.0, approved, #10346 +maven/mavencentral/com.github.docker-java/docker-java-transport-zerodep/3.3.6, Apache-2.0 AND (Apache-2.0 AND BSD-3-Clause), approved, #7946 +maven/mavencentral/com.github.docker-java/docker-java-transport/3.3.6, Apache-2.0, approved, #7942 +maven/mavencentral/com.github.java-json-tools/btf/1.3, Apache-2.0 OR LGPL-3.0-or-later, approved, #2721 +maven/mavencentral/com.github.java-json-tools/jackson-coreutils-equivalence/1.0, LGPL-3.0 OR Apache-2.0, approved, clearlydefined +maven/mavencentral/com.github.java-json-tools/jackson-coreutils/2.0, Apache-2.0 OR LGPL-3.0-or-later, approved, #2719 +maven/mavencentral/com.github.java-json-tools/json-patch/1.13, Apache-2.0 OR LGPL-3.0-or-later, approved, CQ23929 +maven/mavencentral/com.github.java-json-tools/json-schema-core/1.2.14, Apache-2.0 OR LGPL-3.0-or-later, approved, #2722 +maven/mavencentral/com.github.java-json-tools/json-schema-validator/2.2.14, Apache-2.0 OR LGPL-3.0-or-later, approved, CQ20779 +maven/mavencentral/com.github.java-json-tools/msg-simple/1.2, Apache-2.0 OR LGPL-3.0-or-later, approved, #2720 +maven/mavencentral/com.github.java-json-tools/uri-template/0.10, Apache-2.0 OR LGPL-3.0-only, approved, #2723 maven/mavencentral/com.github.stephenc.jcip/jcip-annotations/1.0-1, Apache-2.0, approved, CQ21949 +maven/mavencentral/com.google.code.findbugs/jsr305/2.0.1, BSD-3-Clause AND CC-BY-2.5 AND LGPL-2.1+, approved, CQ13390 maven/mavencentral/com.google.code.findbugs/jsr305/3.0.2, Apache-2.0, approved, #20 maven/mavencentral/com.google.code.gson/gson/2.10.1, Apache-2.0, approved, #6159 -maven/mavencentral/com.google.collections/google-collections/1.0, Apache-2.0, approved, CQ3285 maven/mavencentral/com.google.crypto.tink/tink/1.12.0, Apache-2.0, approved, #12041 -maven/mavencentral/com.google.errorprone/error_prone_annotations/2.18.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.errorprone/error_prone_annotations/2.11.0, Apache-2.0, approved, clearlydefined maven/mavencentral/com.google.errorprone/error_prone_annotations/2.22.0, Apache-2.0, approved, #10661 +maven/mavencentral/com.google.errorprone/error_prone_annotations/2.26.1, Apache-2.0, approved, #13657 maven/mavencentral/com.google.guava/failureaccess/1.0.1, Apache-2.0, approved, CQ22654 -maven/mavencentral/com.google.guava/guava/32.0.1-jre, Apache-2.0 AND CC0-1.0 AND CC-PDDC, approved, #8772 +maven/mavencentral/com.google.guava/failureaccess/1.0.2, Apache-2.0, approved, CQ22654 +maven/mavencentral/com.google.guava/guava/28.1-android, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.guava/guava/28.2-android, Apache-2.0 AND LicenseRef-Public-Domain, approved, CQ22437 +maven/mavencentral/com.google.guava/guava/31.0.1-android, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.guava/guava/31.1-jre, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.guava/guava/33.1.0-jre, Apache-2.0 AND CC0-1.0, approved, #13675 maven/mavencentral/com.google.guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava, Apache-2.0, approved, CQ22657 -maven/mavencentral/com.google.j2objc/j2objc-annotations/2.8, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.j2objc/j2objc-annotations/1.3, Apache-2.0, approved, CQ21195 maven/mavencentral/com.google.protobuf/protobuf-java/3.24.3, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/com.googlecode.libphonenumber/libphonenumber/8.11.1, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.jayway.jsonpath/json-path/2.7.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.jcraft/jzlib/1.1.3, BSD-2-Clause, approved, CQ6218 +maven/mavencentral/com.lmax/disruptor/3.4.4, Apache-2.0, approved, clearlydefined maven/mavencentral/com.microsoft.azure/msal4j-persistence-extension/1.2.0, MIT, approved, clearlydefined -maven/mavencentral/com.microsoft.azure/msal4j/1.14.0, MIT, approved, clearlydefined +maven/mavencentral/com.microsoft.azure/msal4j/1.14.0, MIT, approved, #14159 +maven/mavencentral/com.microsoft.azure/msal4j/1.14.3, MIT, approved, #14159 maven/mavencentral/com.microsoft.azure/msal4j/1.4.0, MIT, approved, clearlydefined +maven/mavencentral/com.networknt/json-schema-validator/1.0.76, Apache-2.0, approved, CQ22638 maven/mavencentral/com.nimbusds/content-type/2.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.nimbusds/content-type/2.3, Apache-2.0, approved, clearlydefined maven/mavencentral/com.nimbusds/lang-tag/1.7, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.28, Apache-2.0, approved, clearlydefined maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.30.2, Apache-2.0, approved, clearlydefined maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.37.3, Apache-2.0, approved, #11701 maven/mavencentral/com.nimbusds/oauth2-oidc-sdk/10.7.1, Apache-2.0, approved, clearlydefined -maven/mavencentral/com.puppycrawl.tools/checkstyle/10.12.3, LGPL-2.1-only AND Apache-2.0 AND LGPL-2.1-or-later AND ANTLR-PD AND GPL-2.0-or-later AND LGPL-2.0-or-later AND (Apache-2.0 AND LGPL-2.1-or-later) AND LicenseRef-scancode-proprietary-license, restricted, #13190 +maven/mavencentral/com.nimbusds/oauth2-oidc-sdk/11.9.1, Apache-2.0, approved, #12667 +maven/mavencentral/com.puppycrawl.tools/checkstyle/10.14.2, LGPL-2.1-or-later AND (Apache-2.0 AND LGPL-2.1-or-later) AND Apache-2.0, approved, #13562 +maven/mavencentral/com.samskivert/jmustache/1.15, BSD-2-Clause, approved, clearlydefined maven/mavencentral/com.squareup.okhttp3/mockwebserver/5.0.0-alpha.12, Apache-2.0, approved, clearlydefined maven/mavencentral/com.squareup.okhttp3/mockwebserver3/5.0.0-alpha.12, Apache-2.0, approved, clearlydefined maven/mavencentral/com.squareup.okhttp3/okhttp-dnsoverhttps/4.12.0, Apache-2.0, approved, #11159 @@ -90,126 +126,170 @@ maven/mavencentral/com.squareup.okio/okio-jvm/3.7.0, Apache-2.0, approved, clear maven/mavencentral/com.squareup.okio/okio/3.6.0, Apache-2.0, approved, #11155 maven/mavencentral/com.squareup.okio/okio/3.7.0, Apache-2.0, approved, clearlydefined maven/mavencentral/com.sun.activation/jakarta.activation/2.0.0, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf +maven/mavencentral/com.sun.activation/jakarta.activation/2.0.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.mail/mailapi/1.6.2, CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, clearlydefined +maven/mavencentral/com.sun.xml.bind/jaxb-core/4.0.1, BSD-3-Clause, approved, ee4j.jaxb-impl +maven/mavencentral/com.sun.xml.bind/jaxb-impl/4.0.1, BSD-3-Clause, approved, ee4j.jaxb-impl +maven/mavencentral/commons-beanutils/commons-beanutils/1.8.3, Apache-2.0, approved, CQ4968 maven/mavencentral/commons-beanutils/commons-beanutils/1.9.4, Apache-2.0, approved, CQ12654 maven/mavencentral/commons-codec/commons-codec/1.11, Apache-2.0 AND BSD-3-Clause, approved, CQ15971 maven/mavencentral/commons-codec/commons-codec/1.15, Apache-2.0 AND BSD-3-Clause AND LicenseRef-Public-Domain, approved, CQ22641 maven/mavencentral/commons-collections/commons-collections/3.2.2, Apache-2.0, approved, CQ10385 +maven/mavencentral/commons-io/commons-io/2.11.0, Apache-2.0, approved, CQ23745 +maven/mavencentral/commons-logging/commons-logging/1.1.1, Apache-2.0, approved, CQ1907 maven/mavencentral/commons-logging/commons-logging/1.2, Apache-2.0, approved, CQ10162 maven/mavencentral/dev.failsafe/failsafe-okhttp/3.3.2, Apache-2.0, approved, #9178 maven/mavencentral/dev.failsafe/failsafe/3.3.1, Apache-2.0, approved, #9268 maven/mavencentral/dev.failsafe/failsafe/3.3.2, Apache-2.0, approved, #9268 -maven/mavencentral/info.picocli/picocli/4.7.4, Apache-2.0, approved, #4365 +maven/mavencentral/info.picocli/picocli/4.7.5, Apache-2.0, approved, #4365 maven/mavencentral/io.github.classgraph/classgraph/4.8.154, MIT, approved, CQ22530 -maven/mavencentral/io.github.classgraph/classgraph/4.8.162, MIT, approved, CQ22530 -maven/mavencentral/io.micrometer/micrometer-commons/1.12.2, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #11679 -maven/mavencentral/io.micrometer/micrometer-core/1.12.2, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #11678 -maven/mavencentral/io.micrometer/micrometer-observation/1.12.2, Apache-2.0, approved, #11680 +maven/mavencentral/io.github.classgraph/classgraph/4.8.165, MIT, approved, CQ22530 +maven/mavencentral/io.micrometer/micrometer-commons/1.12.4, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #11679 +maven/mavencentral/io.micrometer/micrometer-core/1.12.4, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #11678 +maven/mavencentral/io.micrometer/micrometer-observation/1.12.4, Apache-2.0, approved, #11680 maven/mavencentral/io.netty/netty-buffer/4.1.100.Final, Apache-2.0, approved, CQ21842 maven/mavencentral/io.netty/netty-buffer/4.1.101.Final, Apache-2.0, approved, CQ21842 -maven/mavencentral/io.netty/netty-codec-dns/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-buffer/4.1.108.Final, Apache-2.0, approved, CQ21842 +maven/mavencentral/io.netty/netty-buffer/4.1.86.Final, Apache-2.0, approved, CQ21842 maven/mavencentral/io.netty/netty-codec-dns/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-codec-http/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-codec-http/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http/4.1.108.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-codec-http2/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-codec-http2/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-socks/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http2/4.1.108.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http2/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-codec-socks/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-socks/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-codec/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-codec/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec/4.1.108.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-common/4.1.100.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 maven/mavencentral/io.netty/netty-common/4.1.101.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 -maven/mavencentral/io.netty/netty-handler-proxy/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-common/4.1.108.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 +maven/mavencentral/io.netty/netty-common/4.1.86.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 maven/mavencentral/io.netty/netty-handler-proxy/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-handler-proxy/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-handler/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-handler/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-resolver-dns-classes-macos/4.1.100.Final, Apache-2.0, approved, #6367 +maven/mavencentral/io.netty/netty-handler/4.1.108.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-handler/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-resolver-dns-classes-macos/4.1.101.Final, Apache-2.0, approved, #6367 -maven/mavencentral/io.netty/netty-resolver-dns-native-macos/4.1.100.Final, Apache-2.0, approved, #7004 maven/mavencentral/io.netty/netty-resolver-dns-native-macos/4.1.101.Final, Apache-2.0, approved, #7004 -maven/mavencentral/io.netty/netty-resolver-dns/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-resolver-dns/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-resolver/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-resolver/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver/4.1.108.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-tcnative-boringssl-static/2.0.56.Final, Apache-2.0 OR LicenseRef-Public-Domain OR BSD-2-Clause OR MIT, approved, CQ15280 maven/mavencentral/io.netty/netty-tcnative-boringssl-static/2.0.62.Final, Apache-2.0 OR LicenseRef-Public-Domain OR BSD-2-Clause OR MIT, approved, CQ15280 +maven/mavencentral/io.netty/netty-tcnative-classes/2.0.56.Final, Apache-2.0, approved, clearlydefined maven/mavencentral/io.netty/netty-tcnative-classes/2.0.62.Final, Apache-2.0, approved, clearlydefined maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.100.Final, Apache-2.0, approved, #6366 maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.101.Final, Apache-2.0, approved, #6366 -maven/mavencentral/io.netty/netty-transport-classes-kqueue/4.1.100.Final, Apache-2.0, approved, #4107 +maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.108.Final, Apache-2.0, approved, #6366 maven/mavencentral/io.netty/netty-transport-classes-kqueue/4.1.101.Final, Apache-2.0, approved, #4107 -maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport-native-kqueue/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-transport-native-kqueue/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.108.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-transport/4.1.100.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-transport/4.1.101.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport/4.1.108.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.opentelemetry.instrumentation/opentelemetry-instrumentation-annotations/1.32.0, Apache-2.0, approved, #11684 maven/mavencentral/io.opentelemetry/opentelemetry-api/1.32.0, Apache-2.0, approved, #11682 maven/mavencentral/io.opentelemetry/opentelemetry-context/1.32.0, Apache-2.0, approved, #11683 -maven/mavencentral/io.projectreactor.netty/reactor-netty-core/1.0.38, Apache-2.0, approved, #9687 -maven/mavencentral/io.projectreactor.netty/reactor-netty-core/1.0.39, Apache-2.0, approved, #9687 maven/mavencentral/io.projectreactor.netty/reactor-netty-core/1.0.40, Apache-2.0, approved, #9687 -maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.38, Apache-2.0, approved, #11661 -maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.39, Apache-2.0, approved, #11661 maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.40, Apache-2.0, approved, #11661 -maven/mavencentral/io.projectreactor/reactor-core/3.4.33, Apache-2.0, approved, #7517 maven/mavencentral/io.projectreactor/reactor-core/3.4.34, Apache-2.0, approved, #7517 +maven/mavencentral/io.prometheus/simpleclient/0.16.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.prometheus/simpleclient_common/0.16.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.prometheus/simpleclient_httpserver/0.16.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.prometheus/simpleclient_tracer_common/0.16.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.prometheus/simpleclient_tracer_otel/0.16.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.prometheus/simpleclient_tracer_otel_agent/0.16.0, Apache-2.0, approved, clearlydefined maven/mavencentral/io.rest-assured/json-path/5.4.0, Apache-2.0, approved, #12042 maven/mavencentral/io.rest-assured/rest-assured-common/5.4.0, Apache-2.0, approved, #12039 maven/mavencentral/io.rest-assured/rest-assured/5.4.0, Apache-2.0, approved, #12040 maven/mavencentral/io.rest-assured/xml-path/5.4.0, Apache-2.0, approved, #12038 maven/mavencentral/io.setl/rdf-urdna/1.1, Apache-2.0, approved, clearlydefined maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.15, Apache-2.0, approved, #5947 -maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.18, Apache-2.0, approved, #5947 -maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.20, Apache-2.0, approved, #5947 +maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.21, Apache-2.0, approved, #5947 maven/mavencentral/io.swagger.core.v3/swagger-annotations/2.2.15, Apache-2.0, approved, #11362 +maven/mavencentral/io.swagger.core.v3/swagger-annotations/2.2.8, Apache-2.0, approved, #11362 maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.15, Apache-2.0, approved, #5929 -maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.18, Apache-2.0, approved, #5929 +maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.21, Apache-2.0, approved, #5929 maven/mavencentral/io.swagger.core.v3/swagger-core/2.2.15, Apache-2.0, approved, #9265 +maven/mavencentral/io.swagger.core.v3/swagger-core/2.2.8, Apache-2.0, approved, #9265 maven/mavencentral/io.swagger.core.v3/swagger-integration-jakarta/2.2.15, Apache-2.0, approved, #11475 -maven/mavencentral/io.swagger.core.v3/swagger-integration-jakarta/2.2.18, Apache-2.0, approved, #11475 +maven/mavencentral/io.swagger.core.v3/swagger-integration-jakarta/2.2.21, Apache-2.0, approved, #11475 maven/mavencentral/io.swagger.core.v3/swagger-integration/2.2.15, Apache-2.0, approved, #10352 maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2-jakarta/2.2.15, Apache-2.0, approved, #11477 -maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2-jakarta/2.2.18, Apache-2.0, approved, #11477 +maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2-jakarta/2.2.21, Apache-2.0, approved, #11477 maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2/2.2.15, Apache-2.0, approved, #9814 maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.15, Apache-2.0, approved, #5919 -maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.18, Apache-2.0, approved, #5919 +maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.21, Apache-2.0, approved, #5919 maven/mavencentral/io.swagger.core.v3/swagger-models/2.2.15, Apache-2.0, approved, #10353 +maven/mavencentral/io.swagger.core.v3/swagger-models/2.2.8, Apache-2.0, approved, #10353 +maven/mavencentral/io.swagger.parser.v3/swagger-parser-core/2.1.10, Apache-2.0, approved, #11478 +maven/mavencentral/io.swagger.parser.v3/swagger-parser-v2-converter/2.1.10, Apache-2.0, approved, #9330 +maven/mavencentral/io.swagger.parser.v3/swagger-parser-v3/2.1.10, Apache-2.0, approved, #9323 +maven/mavencentral/io.swagger.parser.v3/swagger-parser/2.1.10, Apache-2.0, approved, #11316 +maven/mavencentral/io.swagger/swagger-annotations/1.6.9, Apache-2.0, approved, #3792 +maven/mavencentral/io.swagger/swagger-compat-spec-parser/1.0.64, Apache-2.0, approved, #11479 +maven/mavencentral/io.swagger/swagger-core/1.6.9, Apache-2.0, approved, #4358 +maven/mavencentral/io.swagger/swagger-models/1.6.9, Apache-2.0, approved, #11476 +maven/mavencentral/io.swagger/swagger-parser/1.0.64, Apache-2.0, approved, #4359 maven/mavencentral/jakarta.activation/jakarta.activation-api/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/jakarta.activation/jakarta.activation-api/2.1.0, 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.1, EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, #7907 -maven/mavencentral/jakarta.transaction/jakarta.transaction-api/2.0.0, 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.transaction/jakarta.transaction-api/2.0.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jta maven/mavencentral/jakarta.validation/jakarta.validation-api/2.0.2, Apache-2.0, approved, ee4j.validation maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.2, Apache-2.0, approved, ee4j.validation maven/mavencentral/jakarta.ws.rs/jakarta.ws.rs-api/3.1.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.rest maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/2.3.2, BSD-3-Clause, approved, ee4j.jaxb maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/3.0.0, BSD-3-Clause, approved, ee4j.jaxb +maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/3.0.1, BSD-3-Clause, approved, ee4j.jaxb maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/4.0.0, BSD-3-Clause, approved, ee4j.jaxb maven/mavencentral/javax.servlet/javax.servlet-api/3.1.0, (CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0) AND Apache-2.0, approved, CQ7248 +maven/mavencentral/javax.servlet/javax.servlet-api/4.0.1, (CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0) AND Apache-2.0, approved, CQ16125 maven/mavencentral/javax.ws.rs/javax.ws.rs-api/2.1, (CDDL-1.1 OR GPL-2.0 WITH Classpath-exception-2.0) AND Apache-2.0, approved, CQ18121 +maven/mavencentral/joda-time/joda-time/2.10.5, Apache-2.0, approved, clearlydefined maven/mavencentral/junit/junit/4.13.2, EPL-2.0, approved, CQ23636 maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.14.1, Apache-2.0, approved, #7164 -maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.14.11, Apache-2.0, approved, #7164 +maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.14.12, Apache-2.0, approved, #7164 maven/mavencentral/net.bytebuddy/byte-buddy/1.14.1, Apache-2.0 AND BSD-3-Clause, approved, #7163 maven/mavencentral/net.bytebuddy/byte-buddy/1.14.11, Apache-2.0 AND BSD-3-Clause, approved, #7163 +maven/mavencentral/net.bytebuddy/byte-buddy/1.14.12, Apache-2.0 AND BSD-3-Clause, approved, #7163 +maven/mavencentral/net.bytebuddy/byte-buddy/1.14.9, Apache-2.0 AND BSD-3-Clause, approved, #7163 maven/mavencentral/net.java.dev.jna/jna-platform/5.13.0, Apache-2.0 OR LGPL-2.1-or-later, approved, #6707 maven/mavencentral/net.java.dev.jna/jna-platform/5.6.0, Apache-2.0 OR LGPL-2.1-or-later, approved, CQ22390 maven/mavencentral/net.java.dev.jna/jna/5.13.0, Apache-2.0 AND LGPL-2.1-or-later, approved, #6709 +maven/mavencentral/net.javacrumbs.json-unit/json-unit-core/2.36.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/net.minidev/accessors-smart/2.4.7, Apache-2.0, approved, #7515 maven/mavencentral/net.minidev/accessors-smart/2.5.0, Apache-2.0, approved, clearlydefined maven/mavencentral/net.minidev/json-smart/2.4.10, Apache-2.0, approved, #3288 +maven/mavencentral/net.minidev/json-smart/2.4.7, Apache-2.0, approved, #3288 maven/mavencentral/net.minidev/json-smart/2.5.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/net.sf.saxon/Saxon-HE/12.3, MPL-2.0-no-copyleft-exception AND (LicenseRef-scancode-proprietary-license AND MPL-2.0-no-copyleft-exception) AND (MPL-2.0-no-copyleft-exception AND X11) AND (MIT AND MPL-2.0-no-copyleft-exception) AND (MPL-1.0 AND MPL-2.0-no-copyleft-exception) AND (Apache-2.0 AND MPL-2.0-no-copyleft-exception) AND MPL-1.0, restricted, #13191 -maven/mavencentral/org.antlr/antlr4-runtime/4.11.1, BSD-3-Clause, approved, clearlydefined -maven/mavencentral/org.apache.commons/commons-compress/1.25.0, Apache-2.0, approved, #11600 +maven/mavencentral/net.sf.jopt-simple/jopt-simple/5.0.4, MIT, approved, CQ13174 +maven/mavencentral/net.sf.saxon/Saxon-HE/12.4, MPL-2.0 AND (MPL-2.0 AND Apache-2.0) AND (MPL-2.0 AND LicenseRef-X11-style) AND MPL-1.0 AND W3C, approved, #12716 +maven/mavencentral/org.antlr/antlr4-runtime/4.13.1, BSD-3-Clause, approved, #10767 +maven/mavencentral/org.apache.commons/commons-compress/1.24.0, Apache-2.0 AND BSD-3-Clause AND bzip2-1.0.6 AND LicenseRef-Public-Domain, approved, #10368 +maven/mavencentral/org.apache.commons/commons-digester3/3.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.apache.commons/commons-lang3/3.10, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.commons/commons-lang3/3.11, Apache-2.0, approved, CQ22642 maven/mavencentral/org.apache.commons/commons-lang3/3.12.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.apache.commons/commons-lang3/3.13.0, Apache-2.0, approved, #9820 +maven/mavencentral/org.apache.commons/commons-lang3/3.14.0, Apache-2.0, approved, #11677 maven/mavencentral/org.apache.commons/commons-lang3/3.7, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.commons/commons-lang3/3.8.1, Apache-2.0, approved, #815 maven/mavencentral/org.apache.commons/commons-pool2/2.12.0, Apache-2.0 AND LicenseRef-Public-Domain, approved, #10843 +maven/mavencentral/org.apache.commons/commons-text/1.10.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.commons/commons-text/1.3, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.groovy/groovy-bom/4.0.16, Apache-2.0, approved, #9266 maven/mavencentral/org.apache.groovy/groovy-json/4.0.16, Apache-2.0, approved, #7411 @@ -226,175 +306,206 @@ maven/mavencentral/org.apache.maven.doxia/doxia-core/1.12.0, Apache-2.0, approve maven/mavencentral/org.apache.maven.doxia/doxia-logging-api/1.12.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.maven.doxia/doxia-module-xdoc/1.12.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.maven.doxia/doxia-sink-api/1.12.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.apache.sshd/sshd-common/2.12.0, Apache-2.0 AND ISC, approved, #12842 -maven/mavencentral/org.apache.sshd/sshd-core/2.12.0, Apache-2.0, approved, #12841 -maven/mavencentral/org.apache.sshd/sshd-sftp/2.12.0, Apache-2.0, approved, #12840 +maven/mavencentral/org.apache.sshd/sshd-common/2.12.1, Apache-2.0 AND ISC, approved, #12842 +maven/mavencentral/org.apache.sshd/sshd-core/2.12.1, Apache-2.0, approved, #12841 +maven/mavencentral/org.apache.sshd/sshd-sftp/2.12.1, Apache-2.0, approved, #12840 +maven/mavencentral/org.apache.velocity.tools/velocity-tools-generic/3.1, Apache-2.0, approved, #9331 +maven/mavencentral/org.apache.velocity/velocity-engine-core/2.3, Apache-2.0, approved, #2478 +maven/mavencentral/org.apache.velocity/velocity-engine-scripting/2.3, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.xbean/xbean-reflect/3.7, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apiguardian/apiguardian-api/1.1.2, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.assertj/assertj-core/3.25.1, Apache-2.0, approved, #12585 maven/mavencentral/org.assertj/assertj-core/3.25.3, Apache-2.0, approved, #12585 -maven/mavencentral/org.awaitility/awaitility/4.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.awaitility/awaitility/4.2.0, Apache-2.0, approved, #14178 +maven/mavencentral/org.awaitility/awaitility/4.2.1, Apache-2.0, approved, #14178 +maven/mavencentral/org.bouncycastle/bcpkix-jdk18on/1.72, MIT, approved, #3789 maven/mavencentral/org.bouncycastle/bcpkix-jdk18on/1.77, MIT, approved, #11593 +maven/mavencentral/org.bouncycastle/bcprov-jdk18on/1.72, MIT AND CC0-1.0, approved, #3538 maven/mavencentral/org.bouncycastle/bcprov-jdk18on/1.77, MIT AND CC0-1.0, approved, #11595 +maven/mavencentral/org.bouncycastle/bcutil-jdk18on/1.72, MIT, approved, #3790 maven/mavencentral/org.bouncycastle/bcutil-jdk18on/1.77, MIT, approved, #11596 maven/mavencentral/org.ccil.cowan.tagsoup/tagsoup/1.2.1, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.checkerframework/checker-qual/3.27.0, MIT, approved, clearlydefined -maven/mavencentral/org.checkerframework/checker-qual/3.31.0, MIT, approved, clearlydefined -maven/mavencentral/org.checkerframework/checker-qual/3.41.0, MIT, approved, #12032 +maven/mavencentral/org.checkerframework/checker-qual/3.12.0, MIT, approved, clearlydefined +maven/mavencentral/org.checkerframework/checker-qual/3.42.0, MIT, approved, clearlydefined maven/mavencentral/org.codehaus.plexus/plexus-classworlds/2.6.0, Apache-2.0 AND Plexus, approved, CQ22821 maven/mavencentral/org.codehaus.plexus/plexus-component-annotations/2.1.0, Apache-2.0, approved, #809 maven/mavencentral/org.codehaus.plexus/plexus-container-default/2.1.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.codehaus.plexus/plexus-utils/3.1.1, , approved, CQ16492 maven/mavencentral/org.codehaus.plexus/plexus-utils/3.3.0, , approved, CQ21066 -maven/mavencentral/org.codehaus.woodstox/stax2-api/4.2.1, BSD-2-Clause, approved, #2670 -maven/mavencentral/org.eclipse.edc/api-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/api-observability/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/asset-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/asset-index-sql/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/asset-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/auth-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/auth-tokenbased/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/autodoc-processor/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/aws-s3-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/aws-s3-test/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/azure-blob-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/azure-test/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/boot/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/callback-event-dispatcher/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/callback-http-dispatcher/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/catalog-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/catalog-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/catalog-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/configuration-filesystem/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/connector-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-agreement-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-definition-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-definition-store-sql/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-negotiation-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-negotiation-store-sql/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-api-configuration/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-aggregate-services/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-api-client-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-api-client/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/core-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/crypto-common/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-address-http-data-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-aws-s3/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-azure-storage/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-client/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-control-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-http-oauth2-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-http-oauth2/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-http-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-http/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-public-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-selector-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-selector-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-util/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-api-configuration/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-catalog-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-catalog-http-dispatcher/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-catalog-transform/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-catalog/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-http-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-http-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-negotiation-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-negotiation-http-dispatcher/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-negotiation-transform/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-negotiation/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-transfer-process-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-transfer-process-http-dispatcher/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-transfer-process-transform/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-transfer-process/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/http-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/http/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/iam-mock/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-did-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-did-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-hub-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-hub-credentials/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-hub-did-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-hub-did/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-hub-keypairs/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-hub-participants/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-hub-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-hub-store-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-issuers-configuration/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-service/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-sts-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-sts-client-configuration/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-sts-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-sts-embedded/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-sts-remote-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-sts-remote/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-sts-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-trust-transform/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jersey-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jersey-micrometer/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jersey-providers/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jetty-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jetty-micrometer/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/json-ld-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/json-ld/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/junit/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jws2020/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jwt-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jwt-verifiable-credentials/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/ldp-verifiable-credentials/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/management-api-configuration/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/management-api-test-fixtures/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/management-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/micrometer-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/oauth2-client/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/oauth2-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-definition-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-definition-store-sql/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-engine-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-engine/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-evaluator/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-model/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-monitor-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-monitor-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-monitor-store-sql/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/security/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/sql-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/sql-lease/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/sql-pool-apache-commons/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/state-machine/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/token-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/token-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-datasource-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-local/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-data-plane-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-data-plane/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-process-api/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-process-store-sql/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-pull-http-dynamic-receiver/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transform-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transform-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/util/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/validator-core/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/validator-data-address-http-data/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/validator-spi/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/vault-azure/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/vault-hashicorp/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/web-spi/0.5.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.codehaus.woodstox/stax2-api/4.2.2, BSD-2-Clause, approved, #2670 +maven/mavencentral/org.eclipse.angus/angus-activation/1.0.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.angus +maven/mavencentral/org.eclipse.edc/accesstokendata-store-sql/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/api-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/api-observability/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/asset-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/asset-index-sql/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/asset-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/auth-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/auth-tokenbased/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/autodoc-processor/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/aws-s3-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/aws-s3-test/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/azure-blob-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/azure-test/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/boot-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/boot-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/boot/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/callback-event-dispatcher/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/callback-http-dispatcher/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/catalog-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/catalog-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/configuration-filesystem/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/connector-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-agreement-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-definition-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-definition-store-sql/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-negotiation-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-negotiation-store-sql/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-api-configuration/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-aggregate-services/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-api-client-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-api-client/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-catalog/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-contract/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-policies-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-transfer/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-transform/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/core-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/crypto-common-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-address-http-data-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-aws-s3/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-azure-storage/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-client-embedded/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-control-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http-oauth2-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http-oauth2/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-public-api-v2/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-selector-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-selector-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-signaling-api-configuration/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-signaling-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-signaling-client/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-signaling-transform/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-store-sql/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-util/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-catalog-http-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-catalog-http-dispatcher/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-catalog-transform/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-catalog/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-http-api-configuration/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-http-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-http-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-negotiation-http-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-negotiation-http-dispatcher/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-negotiation-transform/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-negotiation/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-transfer-process-http-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-transfer-process-http-dispatcher/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-transfer-process-transform/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-transfer-process/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-version-http-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/edr-index-sql/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/edr-store-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/edr-store-receiver/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/edr-store-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/http-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/http-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/http/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/iam-mock/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-did-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-did-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-did-web/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-hub-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-hub-credentials/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-hub-did-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-hub-did/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-hub-keypairs/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-hub-participants/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-hub-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-hub-store-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-issuers-configuration/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-service/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-sts-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-sts-client-configuration/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-sts-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-sts-embedded/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-sts-remote-client/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-sts-remote-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-sts-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-transform/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jersey-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jersey-micrometer/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jersey-providers-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jetty-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jetty-micrometer/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/json-ld-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/json-ld-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/json-ld/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/junit-base/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/junit/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jws2020-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jwt-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jwt-verifiable-credentials/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/keys-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/keys-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/ldp-verifiable-credentials/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/management-api-configuration/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/management-api-test-fixtures/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/management-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/micrometer-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/oauth2-client/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/oauth2-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-definition-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-definition-store-sql/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-engine-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-engine-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-evaluator-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-model/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-monitor-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-monitor-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-monitor-store-sql/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/query-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/security/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/sql-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/sql-lease/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/sql-pool-apache-commons/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/state-machine-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/store-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/token-core/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/token-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-datasource-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-local/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-data-plane-signaling/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-data-plane-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-process-api/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-process-store-sql/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-pull-http-dynamic-receiver/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transform-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transform-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/util-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/validator-data-address-http-data/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/validator-lib/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/validator-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/vault-azure/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/vault-hashicorp/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/verifiable-credentials-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/verifiable-credentials/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/web-spi/0.6.1-20240405-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.jetty.toolchain/jetty-jakarta-servlet-api/5.0.2, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty.toolchain/jetty-jakarta-websocket-api/2.0.0, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-client/11.0.20, EPL-2.0 OR Apache-2.0, approved, rt.jetty @@ -417,7 +528,8 @@ maven/mavencentral/org.eclipse.jetty/jetty-servlet/11.0.20, EPL-2.0 OR Apache-2. maven/mavencentral/org.eclipse.jetty/jetty-util/11.0.20, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-webapp/11.0.20, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-xml/11.0.20, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.flywaydb/flyway-core/9.22.3, Apache-2.0, approved, #10349 +maven/mavencentral/org.flywaydb/flyway-core/10.10.0, Apache-2.0, approved, #14163 +maven/mavencentral/org.flywaydb/flyway-database-postgresql/10.10.0, Apache-2.0, approved, #14158 maven/mavencentral/org.glassfish.hk2.external/aopalliance-repackaged/3.0.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish maven/mavencentral/org.glassfish.hk2/hk2-api/3.0.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish maven/mavencentral/org.glassfish.hk2/hk2-locator/3.0.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish @@ -434,6 +546,7 @@ maven/mavencentral/org.glassfish.jersey.media/jersey-media-json-jackson/3.1.5, E maven/mavencentral/org.glassfish.jersey.media/jersey-media-multipart/3.1.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey maven/mavencentral/org.glassfish/jakarta.json/2.0.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jsonp maven/mavencentral/org.hamcrest/hamcrest-core/1.3, BSD-2-Clause, approved, CQ11429 +maven/mavencentral/org.hamcrest/hamcrest-core/2.2, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.hamcrest/hamcrest/2.1, 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 @@ -443,13 +556,14 @@ maven/mavencentral/org.jacoco/org.jacoco.core/0.8.9, EPL-2.0, approved, CQ23283 maven/mavencentral/org.jacoco/org.jacoco.report/0.8.9, EPL-2.0 AND Apache-2.0, approved, CQ23284 maven/mavencentral/org.javassist/javassist/3.28.0-GA, Apache-2.0 OR LGPL-2.1-or-later OR MPL-1.1, approved, #327 maven/mavencentral/org.javassist/javassist/3.29.2-GA, Apache-2.0 AND LGPL-2.1-or-later AND MPL-1.1, approved, #6023 -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.9.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.javassist/javassist/3.30.2-GA, Apache-2.0 AND LGPL-2.1-or-later AND MPL-1.1, approved, #12108 +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.9.10, Apache-2.0, approved, #14186 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.8.0, Apache-2.0, approved, #8807 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.8.21, Apache-2.0, approved, #8807 -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.9.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.9.10, Apache-2.0, approved, #14193 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.0, Apache-2.0, approved, #8919 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.21, Apache-2.0, approved, #8919 -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.10, Apache-2.0, approved, #14191 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.8.21, Apache-2.0, approved, #8865 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.9.10, Apache-2.0, approved, #11827 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.9.21, Apache-2.0, approved, #11827 @@ -459,10 +573,8 @@ maven/mavencentral/org.jetbrains/annotations/24.1.0, Apache-2.0, approved, clear maven/mavencentral/org.junit-pioneer/junit-pioneer/2.2.0, EPL-2.0, approved, #11857 maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.10.1, EPL-2.0, approved, #9714 maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.10.2, EPL-2.0, approved, #9714 -maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.9.3, EPL-2.0, approved, #3133 maven/mavencentral/org.junit.jupiter/junit-jupiter-engine/5.10.1, EPL-2.0, approved, #9711 maven/mavencentral/org.junit.jupiter/junit-jupiter-engine/5.10.2, EPL-2.0, approved, #9711 -maven/mavencentral/org.junit.jupiter/junit-jupiter-engine/5.9.3, EPL-2.0, approved, #3125 maven/mavencentral/org.junit.jupiter/junit-jupiter-params/5.10.1, EPL-2.0, approved, #9708 maven/mavencentral/org.junit.jupiter/junit-jupiter-params/5.10.2, EPL-2.0, approved, #9708 maven/mavencentral/org.junit.platform/junit-platform-commons/1.10.2, EPL-2.0, approved, #9715 @@ -472,94 +584,104 @@ maven/mavencentral/org.junit/junit-bom/5.10.2, EPL-2.0, approved, #9844 maven/mavencentral/org.junit/junit-bom/5.9.2, EPL-2.0, approved, #4711 maven/mavencentral/org.jvnet.mimepull/mimepull/1.9.15, CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, CQ21484 maven/mavencentral/org.latencyutils/LatencyUtils/2.0.3, BSD-2-Clause, approved, CQ17408 +maven/mavencentral/org.mock-server/mockserver-client-java/5.15.0, Apache-2.0 AND LGPL-3.0-only, approved, #9324 +maven/mavencentral/org.mock-server/mockserver-core/5.15.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.mock-server/mockserver-netty/5.15.0, Apache-2.0, approved, #9276 +maven/mavencentral/org.mockito/mockito-core/5.11.0, MIT AND (Apache-2.0 AND MIT) AND Apache-2.0, approved, #13505 maven/mavencentral/org.mockito/mockito-core/5.2.0, MIT AND (Apache-2.0 AND MIT) AND Apache-2.0, approved, #7401 -maven/mavencentral/org.mockito/mockito-core/5.9.0, MIT AND (Apache-2.0 AND MIT) AND Apache-2.0, approved, #12774 +maven/mavencentral/org.mozilla/rhino/1.7.7.2, MPL-2.0 AND BSD-3-Clause AND ISC, approved, CQ16320 maven/mavencentral/org.objenesis/objenesis/3.3, Apache-2.0, approved, clearlydefined maven/mavencentral/org.opentest4j/opentest4j/1.3.0, Apache-2.0, approved, #9713 maven/mavencentral/org.ow2.asm/asm-commons/9.5, BSD-3-Clause, approved, #7553 maven/mavencentral/org.ow2.asm/asm-commons/9.6, BSD-3-Clause, approved, #10775 maven/mavencentral/org.ow2.asm/asm-tree/9.5, BSD-3-Clause, approved, #7555 maven/mavencentral/org.ow2.asm/asm-tree/9.6, BSD-3-Clause, approved, #10773 +maven/mavencentral/org.ow2.asm/asm/9.1, BSD-3-Clause, approved, CQ23029 maven/mavencentral/org.ow2.asm/asm/9.3, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.ow2.asm/asm/9.5, BSD-3-Clause, approved, #7554 maven/mavencentral/org.ow2.asm/asm/9.6, BSD-3-Clause, approved, #10776 -maven/mavencentral/org.postgresql/postgresql/42.7.0, BSD-2-Clause AND Apache-2.0, approved, #11681 -maven/mavencentral/org.postgresql/postgresql/42.7.1, BSD-2-Clause AND Apache-2.0, approved, #11681 +maven/mavencentral/org.postgresql/postgresql/42.7.2, BSD-2-Clause AND Apache-2.0, approved, #11681 +maven/mavencentral/org.postgresql/postgresql/42.7.3, BSD-2-Clause AND Apache-2.0, approved, #11681 maven/mavencentral/org.reactivestreams/reactive-streams/1.0.4, CC0-1.0, approved, CQ16332 maven/mavencentral/org.reflections/reflections/0.10.2, Apache-2.0 AND WTFPL, approved, clearlydefined maven/mavencentral/org.rnorth.duct-tape/duct-tape/1.0.8, MIT, approved, clearlydefined maven/mavencentral/org.slf4j/jcl-over-slf4j/1.7.32, Apache-2.0, approved, CQ12843 +maven/mavencentral/org.slf4j/slf4j-api/1.7.22, MIT, approved, CQ11943 maven/mavencentral/org.slf4j/slf4j-api/1.7.25, MIT, approved, CQ13368 maven/mavencentral/org.slf4j/slf4j-api/1.7.30, MIT, approved, CQ13368 maven/mavencentral/org.slf4j/slf4j-api/1.7.32, MIT, approved, CQ13368 +maven/mavencentral/org.slf4j/slf4j-api/1.7.33, MIT, approved, CQ13368 maven/mavencentral/org.slf4j/slf4j-api/1.7.35, MIT, approved, CQ13368 maven/mavencentral/org.slf4j/slf4j-api/1.7.36, MIT, approved, CQ13368 maven/mavencentral/org.slf4j/slf4j-api/1.7.7, MIT, approved, CQ9827 maven/mavencentral/org.slf4j/slf4j-api/2.0.12, MIT, approved, #5915 +maven/mavencentral/org.slf4j/slf4j-api/2.0.6, MIT, approved, #5915 maven/mavencentral/org.slf4j/slf4j-api/2.0.9, MIT, approved, #5915 -maven/mavencentral/org.testcontainers/database-commons/1.19.4, Apache-2.0, approved, #10345 -maven/mavencentral/org.testcontainers/jdbc/1.19.4, Apache-2.0, approved, #10348 -maven/mavencentral/org.testcontainers/junit-jupiter/1.19.3, MIT, approved, #10344 -maven/mavencentral/org.testcontainers/junit-jupiter/1.19.4, MIT, approved, #10344 -maven/mavencentral/org.testcontainers/postgresql/1.19.4, MIT, approved, #10350 -maven/mavencentral/org.testcontainers/testcontainers/1.19.4, Apache-2.0 AND MIT, approved, #10347 -maven/mavencentral/org.testcontainers/vault/1.19.4, MIT, approved, #10852 -maven/mavencentral/org.xmlresolver/xmlresolver/5.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.testcontainers/database-commons/1.19.7, Apache-2.0, approved, #10345 +maven/mavencentral/org.testcontainers/jdbc/1.19.7, Apache-2.0, approved, #10348 +maven/mavencentral/org.testcontainers/junit-jupiter/1.19.6, MIT, approved, #10344 +maven/mavencentral/org.testcontainers/junit-jupiter/1.19.7, MIT, approved, #10344 +maven/mavencentral/org.testcontainers/postgresql/1.19.7, MIT, approved, #10350 +maven/mavencentral/org.testcontainers/testcontainers/1.19.7, Apache-2.0 AND MIT, approved, #10347 +maven/mavencentral/org.xmlresolver/xmlresolver/5.2.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.xmlunit/xmlunit-core/2.9.1, Apache-2.0, approved, #6272 +maven/mavencentral/org.xmlunit/xmlunit-placeholders/2.9.1, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.yaml/snakeyaml/1.33, Apache-2.0, approved, clearlydefined 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 maven/mavencentral/org.yaml/snakeyaml/2.2, 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, #10232 -maven/mavencentral/software.amazon.awssdk/annotations/2.22.9, Apache-2.0, approved, #12618 -maven/mavencentral/software.amazon.awssdk/annotations/2.23.19, Apache-2.0, approved, #13157 -maven/mavencentral/software.amazon.awssdk/apache-client/2.22.9, Apache-2.0, approved, #12633 -maven/mavencentral/software.amazon.awssdk/apache-client/2.23.19, Apache-2.0, approved, #13142 -maven/mavencentral/software.amazon.awssdk/arns/2.22.9, Apache-2.0, approved, #12647 -maven/mavencentral/software.amazon.awssdk/arns/2.23.19, Apache-2.0, approved, #13135 -maven/mavencentral/software.amazon.awssdk/auth/2.22.9, Apache-2.0, approved, #12622 -maven/mavencentral/software.amazon.awssdk/auth/2.23.19, Apache-2.0, approved, #13141 -maven/mavencentral/software.amazon.awssdk/aws-core/2.22.9, Apache-2.0, approved, #12640 -maven/mavencentral/software.amazon.awssdk/aws-core/2.23.19, Apache-2.0, approved, #13148 -maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.22.9, Apache-2.0, approved, #12645 -maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.23.19, Apache-2.0, approved, #13150 -maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.22.9, Apache-2.0, approved, #12628 -maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.23.19, Apache-2.0, approved, #13146 -maven/mavencentral/software.amazon.awssdk/checksums-spi/2.22.9, Apache-2.0, approved, #12642 -maven/mavencentral/software.amazon.awssdk/checksums-spi/2.23.19, Apache-2.0, approved, #13158 -maven/mavencentral/software.amazon.awssdk/checksums/2.22.9, Apache-2.0, approved, #12641 -maven/mavencentral/software.amazon.awssdk/checksums/2.23.19, Apache-2.0, approved, #13144 -maven/mavencentral/software.amazon.awssdk/crt-core/2.22.9, Apache-2.0, approved, #12635 -maven/mavencentral/software.amazon.awssdk/crt-core/2.23.19, Apache-2.0, approved, #13136 -maven/mavencentral/software.amazon.awssdk/endpoints-spi/2.22.9, Apache-2.0, approved, #12624 -maven/mavencentral/software.amazon.awssdk/endpoints-spi/2.23.19, Apache-2.0, approved, #13151 -maven/mavencentral/software.amazon.awssdk/http-auth-aws/2.22.9, Apache-2.0, approved, #12637 -maven/mavencentral/software.amazon.awssdk/http-auth-aws/2.23.19, Apache-2.0, approved, #13154 -maven/mavencentral/software.amazon.awssdk/http-auth-spi/2.22.9, Apache-2.0, approved, #12638 -maven/mavencentral/software.amazon.awssdk/http-auth-spi/2.23.19, Apache-2.0, approved, #13147 -maven/mavencentral/software.amazon.awssdk/http-auth/2.22.9, Apache-2.0, approved, #12626 -maven/mavencentral/software.amazon.awssdk/http-auth/2.23.19, Apache-2.0, approved, #13149 -maven/mavencentral/software.amazon.awssdk/http-client-spi/2.22.9, Apache-2.0, approved, #12627 -maven/mavencentral/software.amazon.awssdk/http-client-spi/2.23.19, Apache-2.0, approved, #13155 -maven/mavencentral/software.amazon.awssdk/iam/2.22.9, Apache-2.0, approved, clearlydefined -maven/mavencentral/software.amazon.awssdk/identity-spi/2.22.9, Apache-2.0, approved, #12636 -maven/mavencentral/software.amazon.awssdk/identity-spi/2.23.19, Apache-2.0, approved, #13143 -maven/mavencentral/software.amazon.awssdk/json-utils/2.22.9, Apache-2.0, approved, #12646 -maven/mavencentral/software.amazon.awssdk/json-utils/2.23.19, Apache-2.0, approved, #13139 -maven/mavencentral/software.amazon.awssdk/metrics-spi/2.22.9, Apache-2.0, approved, #12649 -maven/mavencentral/software.amazon.awssdk/metrics-spi/2.23.19, Apache-2.0, approved, #13153 -maven/mavencentral/software.amazon.awssdk/netty-nio-client/2.22.9, Apache-2.0, approved, #12644 -maven/mavencentral/software.amazon.awssdk/netty-nio-client/2.23.19, Apache-2.0, approved, #13134 -maven/mavencentral/software.amazon.awssdk/profiles/2.22.9, Apache-2.0, approved, #12620 -maven/mavencentral/software.amazon.awssdk/profiles/2.23.19, Apache-2.0, approved, #13132 -maven/mavencentral/software.amazon.awssdk/protocol-core/2.22.9, Apache-2.0, approved, #12648 -maven/mavencentral/software.amazon.awssdk/protocol-core/2.23.19, Apache-2.0, approved, #13133 -maven/mavencentral/software.amazon.awssdk/regions/2.22.9, Apache-2.0, approved, #12643 -maven/mavencentral/software.amazon.awssdk/regions/2.23.19, Apache-2.0, approved, #13159 -maven/mavencentral/software.amazon.awssdk/s3-transfer-manager/2.23.19, Apache-2.0, approved, #13156 -maven/mavencentral/software.amazon.awssdk/s3/2.22.9, Apache-2.0, approved, #12630 -maven/mavencentral/software.amazon.awssdk/s3/2.23.19, Apache-2.0, approved, #13152 -maven/mavencentral/software.amazon.awssdk/sdk-core/2.22.9, Apache-2.0, approved, #12639 -maven/mavencentral/software.amazon.awssdk/sdk-core/2.23.19, Apache-2.0, approved, #13138 -maven/mavencentral/software.amazon.awssdk/sts/2.22.9, Apache-2.0, approved, clearlydefined -maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.22.9, Apache-2.0, approved, #12629 -maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.23.19, Apache-2.0, approved, #13140 -maven/mavencentral/software.amazon.awssdk/utils/2.22.9, Apache-2.0, approved, #12631 -maven/mavencentral/software.amazon.awssdk/utils/2.23.19, Apache-2.0, approved, #13137 +maven/mavencentral/software.amazon.awssdk/annotations/2.24.10, Apache-2.0, approved, #13251 +maven/mavencentral/software.amazon.awssdk/annotations/2.25.21, Apache-2.0, approved, #13691 +maven/mavencentral/software.amazon.awssdk/apache-client/2.24.10, Apache-2.0, approved, #13257 +maven/mavencentral/software.amazon.awssdk/apache-client/2.25.21, Apache-2.0, approved, #13687 +maven/mavencentral/software.amazon.awssdk/arns/2.24.10, Apache-2.0, approved, #13243 +maven/mavencentral/software.amazon.awssdk/arns/2.25.21, Apache-2.0, approved, #13695 +maven/mavencentral/software.amazon.awssdk/auth/2.24.10, Apache-2.0, approved, #13256 +maven/mavencentral/software.amazon.awssdk/auth/2.25.21, Apache-2.0, approved, #13692 +maven/mavencentral/software.amazon.awssdk/aws-core/2.24.10, Apache-2.0, approved, #13240 +maven/mavencentral/software.amazon.awssdk/aws-core/2.25.21, Apache-2.0, approved, #13702 +maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.24.10, Apache-2.0, approved, #13262 +maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.25.21, Apache-2.0, approved, #13701 +maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.24.10, Apache-2.0, approved, #13247 +maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.25.21, Apache-2.0, approved, #13684 +maven/mavencentral/software.amazon.awssdk/checksums-spi/2.24.10, Apache-2.0, approved, #13245 +maven/mavencentral/software.amazon.awssdk/checksums-spi/2.25.21, Apache-2.0, approved, #13686 +maven/mavencentral/software.amazon.awssdk/checksums/2.24.10, Apache-2.0, approved, #13242 +maven/mavencentral/software.amazon.awssdk/checksums/2.25.21, Apache-2.0, approved, #13677 +maven/mavencentral/software.amazon.awssdk/crt-core/2.24.10, Apache-2.0, approved, #13252 +maven/mavencentral/software.amazon.awssdk/crt-core/2.25.21, Apache-2.0, approved, #13705 +maven/mavencentral/software.amazon.awssdk/endpoints-spi/2.24.10, Apache-2.0, approved, #13246 +maven/mavencentral/software.amazon.awssdk/endpoints-spi/2.25.21, Apache-2.0, approved, #13681 +maven/mavencentral/software.amazon.awssdk/http-auth-aws/2.24.10, Apache-2.0, approved, #13253 +maven/mavencentral/software.amazon.awssdk/http-auth-aws/2.25.21, Apache-2.0, approved, #13696 +maven/mavencentral/software.amazon.awssdk/http-auth-spi/2.24.10, Apache-2.0, approved, #13264 +maven/mavencentral/software.amazon.awssdk/http-auth-spi/2.25.21, Apache-2.0, approved, #13704 +maven/mavencentral/software.amazon.awssdk/http-auth/2.24.10, Apache-2.0, approved, #13248 +maven/mavencentral/software.amazon.awssdk/http-auth/2.25.21, Apache-2.0, approved, #13682 +maven/mavencentral/software.amazon.awssdk/http-client-spi/2.24.10, Apache-2.0, approved, #13259 +maven/mavencentral/software.amazon.awssdk/http-client-spi/2.25.21, Apache-2.0, approved, #13706 +maven/mavencentral/software.amazon.awssdk/iam/2.24.10, Apache-2.0, approved, #13444 +maven/mavencentral/software.amazon.awssdk/identity-spi/2.24.10, Apache-2.0, approved, #13244 +maven/mavencentral/software.amazon.awssdk/identity-spi/2.25.21, Apache-2.0, approved, #13685 +maven/mavencentral/software.amazon.awssdk/json-utils/2.24.10, Apache-2.0, approved, #13261 +maven/mavencentral/software.amazon.awssdk/json-utils/2.25.21, Apache-2.0, approved, #13698 +maven/mavencentral/software.amazon.awssdk/metrics-spi/2.24.10, Apache-2.0, approved, #13239 +maven/mavencentral/software.amazon.awssdk/metrics-spi/2.25.21, Apache-2.0, approved, #13680 +maven/mavencentral/software.amazon.awssdk/netty-nio-client/2.24.10, Apache-2.0, approved, #13260 +maven/mavencentral/software.amazon.awssdk/netty-nio-client/2.25.21, Apache-2.0, approved, #13693 +maven/mavencentral/software.amazon.awssdk/profiles/2.24.10, Apache-2.0, approved, #13258 +maven/mavencentral/software.amazon.awssdk/profiles/2.25.21, Apache-2.0, approved, #13697 +maven/mavencentral/software.amazon.awssdk/protocol-core/2.24.10, Apache-2.0, approved, #13241 +maven/mavencentral/software.amazon.awssdk/protocol-core/2.25.21, Apache-2.0, approved, #13679 +maven/mavencentral/software.amazon.awssdk/regions/2.24.10, Apache-2.0, approved, #13255 +maven/mavencentral/software.amazon.awssdk/regions/2.25.21, Apache-2.0, approved, #13694 +maven/mavencentral/software.amazon.awssdk/s3-transfer-manager/2.25.21, Apache-2.0, approved, #13699 +maven/mavencentral/software.amazon.awssdk/s3/2.24.10, Apache-2.0, approved, #13254 +maven/mavencentral/software.amazon.awssdk/s3/2.25.21, Apache-2.0, approved, #13688 +maven/mavencentral/software.amazon.awssdk/sdk-core/2.24.10, Apache-2.0, approved, #13265 +maven/mavencentral/software.amazon.awssdk/sdk-core/2.25.21, Apache-2.0, approved, #13700 +maven/mavencentral/software.amazon.awssdk/sts/2.24.10, Apache-2.0, approved, #13442 +maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.24.10, Apache-2.0, approved, #13249 +maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.25.21, Apache-2.0, approved, #13703 +maven/mavencentral/software.amazon.awssdk/utils/2.24.10, Apache-2.0, approved, #13250 +maven/mavencentral/software.amazon.awssdk/utils/2.25.21, Apache-2.0, approved, #13689 maven/mavencentral/software.amazon.eventstream/eventstream/1.0.1, Apache-2.0, approved, clearlydefined diff --git a/build.gradle.kts b/build.gradle.kts index b0dd494bb..6b27934d4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -98,8 +98,8 @@ allprojects { swagger { title.set((project.findProperty("apiTitle") ?: "Tractus-X REST API") as String) description = - (project.findProperty("apiDescription") - ?: "Tractus-X REST APIs - merged by OpenApiMerger") as String + (project.findProperty("apiDescription") + ?: "Tractus-X REST APIs - merged by OpenApiMerger") as String outputFilename.set(project.name) outputDirectory.set(file("${rootProject.projectDir.path}/resources/openapi/yaml")) resourcePackages = setOf("org.eclipse.tractusx.edc") @@ -138,20 +138,13 @@ allprojects { } } - // EdcRuntimeExtension uses this to determine the runtime classpath of the module to run. - tasks.register("printClasspath") { - doLast { - println(sourceSets["main"].runtimeClasspath.asPath) - } - } - } // the "dockerize" task is added to all projects that use the `shadowJar` plugin subprojects { afterEvaluate { if (project.plugins.hasPlugin("com.github.johnrengelman.shadow") && - file("${project.projectDir}/src/main/docker/Dockerfile").exists() + file("${project.projectDir}/src/main/docker/Dockerfile").exists() ) { val buildDir = project.layout.buildDirectory.get().asFile @@ -171,8 +164,8 @@ subprojects { doLast { val download = { url: String, destFile: File -> ant.invokeMethod( - "get", - mapOf("src" to url, "dest" to destFile) + "get", + mapOf("src" to url, "dest" to destFile) ) } logger.lifecycle("Downloading OpenTelemetry Agent") @@ -206,9 +199,9 @@ subprojects { } // make sure always runs after "dockerize" and after "copyOtel" dockerTask - .dependsOn(tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME)) - .dependsOn(downloadOtel) - .dependsOn(copyLegalDocs) + .dependsOn(tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME)) + .dependsOn(downloadOtel) + .dependsOn(copyLegalDocs) } } } diff --git a/charts/tractusx-connector-azure-vault/Chart.yaml b/charts/tractusx-connector-azure-vault/Chart.yaml index 74b7e2e7a..0ff024d86 100644 --- a/charts/tractusx-connector-azure-vault/Chart.yaml +++ b/charts/tractusx-connector-azure-vault/Chart.yaml @@ -41,12 +41,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.6.0 +version: 0.7.0-rc1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.6.0" +appVersion: "0.7.0-rc1" home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector sources: - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector @@ -54,6 +54,6 @@ dependencies: # PostgreSQL - name: postgresql alias: postgresql - version: 12.11.2 + version: "15.2.1" repository: https://charts.bitnami.com/bitnami condition: install.postgresql diff --git a/charts/tractusx-connector-azure-vault/README.md b/charts/tractusx-connector-azure-vault/README.md index f8d970605..f4a42d029 100644 --- a/charts/tractusx-connector-azure-vault/README.md +++ b/charts/tractusx-connector-azure-vault/README.md @@ -1,6 +1,6 @@ # tractusx-connector-azure-vault -![Version: 0.6.0](https://img.shields.io/badge/Version-0.6.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.6.0](https://img.shields.io/badge/AppVersion-0.6.0-informational?style=flat-square) +![Version: 0.7.0-rc1](https://img.shields.io/badge/Version-0.7.0--rc1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.7.0-rc1](https://img.shields.io/badge/AppVersion-0.7.0--rc1-informational?style=flat-square) A Helm chart for Tractus-X Eclipse Data Space Connector. The connector deployment consists of two runtime consists of a Control Plane and a Data Plane. Note that _no_ external dependencies such as a PostgreSQL database and Azure KeyVault are included. @@ -45,7 +45,7 @@ Combined, run this shell command to start the in-memory Tractus-X EDC runtime: ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0.6.0 \ +helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0.7.0-rc1 \ -f /tractusx-connector-azure-vault-test.yaml \ --set vault.azure.name=$AZURE_VAULT_NAME \ --set vault.azure.client=$AZURE_CLIENT_ID \ @@ -61,24 +61,24 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | Repository | Name | Version | |------------|------|---------| -| https://charts.bitnami.com/bitnami | postgresql(postgresql) | 12.11.2 | +| https://charts.bitnami.com/bitnami | postgresql(postgresql) | 15.2.1 | ## Values | Key | Type | Default | Description | |-----|------|---------|-------------| -| backendService.httpProxyTokenReceiverUrl | string | `"https://example.com"` | Specifies a backend service which will receive the EDR | | controlplane.affinity | object | `{}` | | | controlplane.autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | | controlplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | | controlplane.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | controlplane.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | controlplane.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| controlplane.bdrs.cache_validity_seconds | int | `600` | | +| controlplane.bdrs.server.url | string | `nil` | | | controlplane.businessPartnerValidation.log.agreementValidation | bool | `true` | | | controlplane.debug.enabled | bool | `false` | | | controlplane.debug.port | int | `1044` | | | controlplane.debug.suspendOnStart | bool | `false` | | -| controlplane.edr.transferProxyTokenValidity | string | `"2592000"` | | | controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"password","path":"/management","port":8081},"metrics":{"path":"/metrics","port":9090},"protocol":{"path":"/api/v1/dsp","port":8084}}` | endpoints of the control plane | | controlplane.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | | controlplane.endpoints.control.path | string | `"/control"` | path for incoming api calls | @@ -160,11 +160,6 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | controlplane.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | | controlplane.service.annotations | object | `{}` | | | controlplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | -| controlplane.ssi.miw.authorityId | string | `""` | The BPN of the issuer authority | -| controlplane.ssi.miw.url | string | `""` | MIW URL | -| controlplane.ssi.oauth.client.id | string | `""` | The client ID for KeyCloak | -| controlplane.ssi.oauth.client.secretAlias | string | `"client-secret"` | The alias under which the client secret is stored in the vault. | -| controlplane.ssi.oauth.tokenurl | string | `""` | The URL (of KeyCloak), where access tokens can be obtained | | controlplane.tolerations | list | `[]` | | | controlplane.url.protocol | string | `""` | Explicitly declared url for reaching the dsp api (e.g. if ingresses not used) | | controlplane.volumeMounts | string | `nil` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | @@ -183,8 +178,6 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | dataplane.debug.enabled | bool | `false` | | | dataplane.debug.port | int | `1044` | | | dataplane.debug.suspendOnStart | bool | `false` | | -| dataplane.endpoints.control.path | string | `"/api/dataplane/control"` | | -| dataplane.endpoints.control.port | int | `8083` | | | dataplane.endpoints.default.path | string | `"/api"` | | | dataplane.endpoints.default.port | int | `8080` | | | dataplane.endpoints.metrics.path | string | `"/metrics"` | | @@ -194,6 +187,8 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | dataplane.endpoints.proxy.port | int | `8186` | | | dataplane.endpoints.public.path | string | `"/api/public"` | | | dataplane.endpoints.public.port | int | `8081` | | +| dataplane.endpoints.signaling.path | string | `"/api/signaling"` | | +| dataplane.endpoints.signaling.port | int | `8083` | | | dataplane.env | object | `{}` | | | dataplane.envConfigMapNames | list | `[]` | | | dataplane.envSecretNames | list | `[]` | | @@ -248,14 +243,29 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | dataplane.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | | dataplane.service.port | int | `80` | | | dataplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | +| dataplane.token.refresh.expiry_seconds | int | `300` | | +| dataplane.token.refresh.expiry_tolerance_seconds | int | `10` | | +| dataplane.token.refresh.refresh_endpoint | string | `nil` | | +| dataplane.token.signer.privatekey_alias | string | `nil` | | +| dataplane.token.verifier.publickey_alias | string | `nil` | | | dataplane.tolerations | list | `[]` | | | dataplane.url.public | string | `""` | Explicitly declared url for reaching the public api (e.g. if ingresses not used) | | dataplane.volumeMounts | string | `nil` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | | dataplane.volumes | string | `nil` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | | fullnameOverride | string | `""` | | +| iatp.id | string | `"did:web:changeme"` | | +| iatp.sts.dim.url | string | `nil` | | +| iatp.sts.oauth.client.id | string | `nil` | | +| iatp.sts.oauth.client.secret_alias | string | `nil` | | +| iatp.sts.oauth.token_url | string | `nil` | | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | | install.postgresql | bool | `true` | | | nameOverride | string | `""` | | +| networkPolicy.controlplane | object | `{"from":[{"namespaceSelector":{}}]}` | Configuration of the controlplane component | +| networkPolicy.controlplane.from | list | `[{"namespaceSelector":{}}]` | Specify from rule network policy for cp (defaults to all namespaces) | +| networkPolicy.dataplane | object | `{"from":[{"namespaceSelector":{}}]}` | Configuration of the dataplane component | +| networkPolicy.dataplane.from | list | `[{"namespaceSelector":{}}]` | Specify from rule network policy for dp (defaults to all namespaces) | +| networkPolicy.enabled | bool | `false` | If `true` network policy will be created to restrict access to control- and dataplane | | participant.id | string | `"BPNLCHANGEME"` | BPN Number | | postgresql.auth.database | string | `"edc"` | | | postgresql.auth.password | string | `"password"` | | diff --git a/charts/tractusx-connector-azure-vault/templates/_helpers.tpl b/charts/tractusx-connector-azure-vault/templates/_helpers.tpl index b3c570bb1..17bcddc86 100644 --- a/charts/tractusx-connector-azure-vault/templates/_helpers.tpl +++ b/charts/tractusx-connector-azure-vault/templates/_helpers.tpl @@ -8,6 +8,7 @@ Expand the name of the chart. {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} {{- define "txdc.fullname" -}} @@ -143,10 +144,10 @@ Control Plane URL {{- end }} {{/* -Data Control URL +Data Signaling URL */}} -{{- define "txdc.dataplane.url.control" -}} -{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" . ) .Values.dataplane.endpoints.control.port .Values.dataplane.endpoints.control.path -}} +{{- define "txdc.dataplane.url.signaling" -}} +{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" . ) .Values.dataplane.endpoints.signaling.port .Values.dataplane.endpoints.signaling.path -}} {{- end }} {{/* diff --git a/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml b/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml index 18e2500c4..c0b7c5697 100644 --- a/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml @@ -146,22 +146,8 @@ spec: ######################## - name: EDC_PARTICIPANT_ID value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }} - - ########################## - # SSI / MIW CONFIGURATION - ########################## - - name: "TX_SSI_MIW_URL" - value: {{ .Values.controlplane.ssi.miw.url | quote }} - - name: "TX_SSI_MIW_AUTHORITY_ID" - value: {{ .Values.controlplane.ssi.miw.authorityId | quote }} - - name: "TX_SSI_OAUTH_TOKEN_URL" - value: {{ .Values.controlplane.ssi.oauth.tokenurl | quote }} - - name: "TX_SSI_OAUTH_CLIENT_ID" - value: {{ .Values.controlplane.ssi.oauth.client.id | quote }} - - name: "TX_SSI_OAUTH_CLIENT_SECRET_ALIAS" - value: {{ .Values.controlplane.ssi.oauth.client.secretAlias | quote }} - - name: "TX_SSI_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" .) .Values.controlplane.endpoints.protocol.path | quote }} + - name: "EDC_IAM_ISSUER_ID" + value: {{ .Values.iatp.id | required ".Values.iatp.id is required" | quote}} ####### # API # @@ -252,16 +238,6 @@ spec: - name: "EDC_DATASOURCE_TRANSFERPROCESS_URL" value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/edr-cache-sql - - name: "EDC_DATASOURCE_EDR_NAME" - value: "edr" - - name: "EDC_DATASOURCE_EDR_USER" - value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - - name: "EDC_DATASOURCE_EDR_PASSWORD" - value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - - name: "EDC_DATASOURCE_EDR_URL" - value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/policy-monitor/store/sql/policy-monitor-store-sql - name: "EDC_DATASOURCE_POLICY-MONITOR_NAME" value: "policy-monitor" @@ -282,78 +258,82 @@ spec: - name: "EDC_DATASOURCE_BPN_URL" value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/common/store/sql/edr-index-sql + - name: "EDC_DATASOURCE_EDR_NAME" + value: "edr" + - name: "EDC_DATASOURCE_EDR_USER" + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} + - name: "EDC_DATASOURCE_EDR_PASSWORD" + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} + - name: "EDC_DATASOURCE_EDR_URL" + value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + + ############################# + ## IATP / STS / DIM CONFIG ## + ############################# + - name: "EDC_IAM_STS_OAUTH_TOKEN_URL" + value: {{ .Values.iatp.sts.oauth.token_url | required ".Values.iatp.oauth.token_url is required" | quote}} + - name: "EDC_IAM_STS_OAUTH_CLIENT_ID" + value: {{ .Values.iatp.sts.oauth.client.id | required ".Values.iatp.sts.oauth.client.id is required" | quote}} + - name: "EDC_IAM_STS_OAUTH_CLIENT_SECRET_ALIAS" + value: {{ .Values.iatp.sts.oauth.client.secret_alias | required ".Values.iatp.sts.oauth.client.secret_alias is required" | quote}} + - name: "EDC_IAM_STS_DIM_URL" + value: {{ .Values.iatp.sts.dim.url | required ".Values.iatp.sts.dim.url is required" | quote}} + + ################# + ## BDRS CLIENT ## + ################# + + {{- if .Values.controlplane.bdrs.server.url }} + - name: "TX_IAM_IATP_BDRS_SERVER_URL" + value: {{ .Values.controlplane.bdrs.server.url | required ".Values.controlplane.bdrs.server.url is required" | quote }} + {{- end }} + {{- if .Values.controlplane.bdrs.cache_validity_seconds }} + - name: "TX_IAM_IATP_BDRS_CACHE_VALIDITY" + value: {{ .Values.controlplane.bdrs.cache_validity_seconds | quote}} + {{- end}} + ################ ## DATA PLANE ## ################ # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/dataplane-selector-configuration - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_URL" - value: {{ include "txdc.dataplane.url.control" . }}/transfer + value: {{ include "txdc.dataplane.url.signaling" . }}/v1/dataflows + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_TRANSFERTYPES" + value: "HttpData-PULL,AmazonS3-PUSH,AzureStorage-PUSH" - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_SOURCETYPES" - value: "HttpData,AmazonS3" + value: "HttpData,AmazonS3,AzureStorage" - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_DESTINATIONTYPES" - value: "HttpProxy,AmazonS3" + value: "HttpProxy,AmazonS3,AzureStorage" - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_PROPERTIES" value: |- {{ printf "{ \"publicApiUrl\": \"%s\" }" (include "txdc.dataplane.url.public" . ) }} - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/data-plane-transfer - - name: "EDC_TRANSFER_PROXY_ENDPOINT" - value: {{ include "txdc.dataplane.url.public" . }} - {{- if .Values.vault.secretNames.transferProxyTokenSignerPrivateKey }} - - name: "EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS" - value: {{ .Values.vault.secretNames.transferProxyTokenSignerPrivateKey | quote }} - {{- end }} - {{- if .Values.vault.secretNames.transferProxyTokenSignerPublicKey }} - - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" - value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | quote }} - {{- end }} - - name: "EDC_TRANSFER_PROXY_TOKEN_VALIDITY_SECONDS" - value: {{ .Values.controlplane.edr.transferProxyTokenValidity | required ".Values.controlplane.edr.transferProxyTokenValidity is required" | quote }} - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/transfer/transfer-pull-http-dynamic-receiver - - - name: "EDC_RECEIVER_HTTP_DYNAMIC_ENDPOINT" - value: {{ .Values.backendService.httpProxyTokenReceiverUrl | required ".Values.backendService.httpProxyTokenReceiverUrl is required" | quote }} ########### ## VAULT ## ########### - - name: "EDC_VAULT_CLIENTID" + - name: "AZURE_CLIENT_ID" value: {{ .Values.vault.azure.client | required ".Values.vault.azure.client is required" | quote }} - - name: "EDC_VAULT_TENANTID" + - name: "AZURE_TENANT_ID" value: {{ .Values.vault.azure.tenant | required ".Values.vault.azure.tenant is required" | quote }} - name: "EDC_VAULT_NAME" value: {{ .Values.vault.azure.name | required ".Values.vault.azure.name is required" | quote }} # only set the env var if config value not null {{- if .Values.vault.azure.secret }} - - name: "EDC_VAULT_CLIENTSECRET" + - name: "AZURE_CLIENT_SECRET" value: {{ .Values.vault.azure.secret | quote }} {{- end }} # only set the env var if config value not null {{- if .Values.vault.azure.certificate }} - - name: "EDC_VAULT_CERTIFICATE" + - name: "AZURE_CLIENT_CERTIFICATE_PATH" value: {{ .Values.vault.azure.certificate | quote }} {{- end }} - ##################### - ## DATA ENCRYPTION ## - ##################### - - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/data-encryption - - name: "EDC_DATA_ENCRYPTION_KEYS_ALIAS" - value: {{ .Values.vault.secretNames.transferProxyTokenEncryptionAesKey | quote }} - - name: "EDC_DATA_ENCRYPTION_ALGORITHM" - value: "AES" - - ########################### - ## AAS WRAPPER EXTENSION ## - ########################### - - name: "EDC_CP_ADAPTER_CACHE_CATALOG_EXPIRE_AFTER" - value: "0" - - name: "EDC_CP_ADAPTER_REUSE_CONTRACT_AGREEMENT" - value: "0" + ########################### ## BUSINESS PARTNER NUMBER VALIDATION EXTENSION ## diff --git a/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml b/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml index 48085d744..564ecdaa6 100644 --- a/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml @@ -1,24 +1,24 @@ ################################################################################# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# 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 -################################################################################# + # Copyright (c) 2023 ZF Friedrichshafen AG + # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH + # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + # 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 + ################################################################################# --- @@ -145,6 +145,8 @@ spec: ######################## - name: EDC_PARTICIPANT_ID value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }} + - name: "EDC_IAM_ISSUER_ID" + value: {{ .Values.iatp.id | required ".Values.iatp.id is required" | quote}} ####### # API # @@ -155,10 +157,10 @@ spec: value: {{ .Values.dataplane.endpoints.default.port | quote }} - name: "WEB_HTTP_DEFAULT_PATH" value: {{ .Values.dataplane.endpoints.default.path | quote }} - - name: "WEB_HTTP_CONTROL_PORT" - value: {{ .Values.dataplane.endpoints.control.port | quote }} - - name: "WEB_HTTP_CONTROL_PATH" - value: {{ .Values.dataplane.endpoints.control.path | quote }} + - name: "WEB_HTTP_SIGNALING_PORT" + value: {{ .Values.dataplane.endpoints.signaling.port | quote }} + - name: "WEB_HTTP_SIGNALING_PATH" + value: {{ .Values.dataplane.endpoints.signaling.path | quote }} - name: "WEB_HTTP_PUBLIC_PORT" value: {{ .Values.dataplane.endpoints.public.port | quote }} - name: "WEB_HTTP_PUBLIC_PATH" @@ -182,41 +184,108 @@ spec: value: {{ .Values.dataplane.aws.accessKeyId | quote }} {{- end }} - ############### - ## EDR CACHE ## - ############### - - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/edr-cache-sql - - name: "EDC_DATASOURCE_EDR_NAME" - value: "edr" - - name: "EDC_DATASOURCE_EDR_USER" - value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - - name: "EDC_DATASOURCE_EDR_PASSWORD" - value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - - name: "EDC_DATASOURCE_EDR_URL" - value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} - ########### ## VAULT ## ########### - - name: "EDC_VAULT_CLIENTID" + - name: "AZURE_CLIENT_ID" value: {{ .Values.vault.azure.client | quote }} - - name: "EDC_VAULT_TENANTID" + - name: "AZURE_TENANT_ID" value: {{ .Values.vault.azure.tenant | quote }} - name: "EDC_VAULT_NAME" value: {{ .Values.vault.azure.name | quote }} # only set the env var if config value not null {{- if .Values.vault.azure.secret }} - - name: "EDC_VAULT_CLIENTSECRET" + - name: "AZURE_CLIENT_SECRET" value: {{ .Values.vault.azure.secret | quote }} {{- end }} # only set the env var if config value not null {{- if .Values.vault.azure.certificate }} - - name: "EDC_VAULT_CERTIFICATE" + - name: "AZURE_CLIENT_CERTIFICATE_PATH" value: {{ .Values.vault.azure.certificate | quote }} {{- end }} + ############################# + ## IATP / STS / DIM CONFIG ## + ############################# + - name: "EDC_IAM_STS_OAUTH_TOKEN_URL" + value: {{ .Values.iatp.sts.oauth.token_url | required ".Values.iatp.oauth.token_url is required" | quote}} + - name: "EDC_IAM_STS_OAUTH_CLIENT_ID" + value: {{ .Values.iatp.sts.oauth.client.id | required ".Values.iatp.sts.oauth.client.id is required" | quote}} + - name: "EDC_IAM_STS_OAUTH_CLIENT_SECRET_ALIAS" + value: {{ .Values.iatp.sts.oauth.client.secret_alias | required ".Values.iatp.sts.oauth.client.secret_alias is required" | quote}} + - name: "EDC_IAM_STS_DIM_URL" + value: {{ .Values.iatp.sts.dim.url | required ".Values.iatp.sts.dim.url is required" | quote}} + + ################ + ## POSTGRESQL ## + ################ + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/common/store/sql/edr-index-sql + - name: "EDC_DATASOURCE_EDR_NAME" + value: "edr" + - name: "EDC_DATASOURCE_EDR_USER" + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} + - name: "EDC_DATASOURCE_EDR_PASSWORD" + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} + - name: "EDC_DATASOURCE_EDR_URL" + value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/data-plane/store/sql/data-plane-store-sql + - name: "EDC_DATASOURCE_DATAPLANE_NAME" + value: "edr" + - name: "EDC_DATASOURCE_DATAPLANE_USER" + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} + - name: "EDC_DATASOURCE_DATAPLANE_PASSWORD" + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} + - name: "EDC_DATASOURCE_DATAPLANE_URL" + value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/data-plane/store/sql/accesstokendata-store-sql + - name: "EDC_DATASOURCE_ACCESSTOKENDATA_NAME" + value: "edr" + - name: "EDC_DATASOURCE_ACCESSTOKENDATA_USER" + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} + - name: "EDC_DATASOURCE_ACCESSTOKENDATA_PASSWORD" + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} + - name: "EDC_DATASOURCE_ACCESSTOKENDATA_URL" + value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + + ######################### + ## DATA PLANE PUBLIC API + ######################## + - name: "EDC_DATAPLANE_API_PUBLIC_BASEURL" + value: {{ include "txdc.dataplane.url.public" . }} + + + ################## + ## TOKEN REFRESH + ################## + {{- if .Values.dataplane.token.refresh.expiry_seconds }} + - name: "EDC_DATAPLANE_TOKEN_EXPIRY" + value: {{ .Values.dataplane.token.refresh.expiry_seconds | quote}} + {{- end}} + + {{- if .Values.dataplane.token.refresh.expiry_tolerance_seconds }} + - name: "EDC_DATAPLANE_TOKEN_EXPIRY_TOLERANCE" + value: {{ .Values.dataplane.token.refresh.expiry_tolerance_seconds | quote }} + {{- end}} + + {{- if .Values.dataplane.token.refresh.refresh_endpoint }} + - name: "EDC_DATAPLANE_TOKEN_REFRESH_ENDPOINT" + value: {{ .Values.dataplane.token.refresh.refresh_endpoint }} + {{- else}} + - name: "EDC_DATAPLANE_TOKEN_REFRESH_ENDPOINT" + value: {{ include "txdc.dataplane.url.public" . }}/token + {{- end}} + + - name: "EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS" + value: {{ .Values.dataplane.token.signer.privatekey_alias | required ".Values.dataplane.token.signer.privatekey_alias is required" | quote}} + + - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" + value: {{ .Values.dataplane.token.verifier.publickey_alias | required ".Values.dataplane.token.verifier.publickey_alias" | quote }} + + ###################################### ## Additional environment variables ## ###################################### diff --git a/charts/tractusx-connector-azure-vault/templates/service-dataplane.yaml b/charts/tractusx-connector-azure-vault/templates/service-dataplane.yaml index 14230b9de..807eba45f 100644 --- a/charts/tractusx-connector-azure-vault/templates/service-dataplane.yaml +++ b/charts/tractusx-connector-azure-vault/templates/service-dataplane.yaml @@ -32,10 +32,10 @@ spec: targetPort: default protocol: TCP name: default - - port: {{ .Values.dataplane.endpoints.control.port }} - targetPort: control + - port: {{ .Values.dataplane.endpoints.signaling.port }} + targetPort: signaling protocol: TCP - name: control + name: signaling - port: {{ .Values.dataplane.endpoints.public.port }} targetPort: public protocol: TCP diff --git a/charts/tractusx-connector-azure-vault/values.yaml b/charts/tractusx-connector-azure-vault/values.yaml index 38a24c0e9..06d109072 100644 --- a/charts/tractusx-connector-azure-vault/values.yaml +++ b/charts/tractusx-connector-azure-vault/values.yaml @@ -40,6 +40,20 @@ participant: # -- BPN Number id: "BPNLCHANGEME" + +iatp: + # Decentralized IDentifier + id: "did:web:changeme" + sts: + dim: + url: + oauth: + token_url: + client: + id: + secret_alias: + + controlplane: image: # -- Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically @@ -117,23 +131,13 @@ controlplane: businessPartnerValidation: log: agreementValidation: true - edr: - transferProxyTokenValidity: "2592000" - # SSI configuration - ssi: - miw: - # -- MIW URL - url: "" - # -- The BPN of the issuer authority - authorityId: "" - oauth: - # -- The URL (of KeyCloak), where access tokens can be obtained - tokenurl: "" - client: - # -- The client ID for KeyCloak - id: "" - # -- The alias under which the client secret is stored in the vault. - secretAlias: "client-secret" + + bdrs: + # time that a cached BPN/DID resolution map is valid in seconds, default is 10 min + cache_validity_seconds: 600 + server: + # URL of the BPN/DID Resolution Service - required: + url: service: # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. @@ -349,9 +353,9 @@ dataplane: public: port: 8081 path: /api/public - control: + signaling: port: 8083 - path: /api/dataplane/control + path: /api/signaling proxy: port: 8186 path: /proxy @@ -359,6 +363,20 @@ dataplane: metrics: port: 9090 path: /metrics + + token: + refresh: + expiry_seconds: 300 + expiry_tolerance_seconds: 10 + # optional URL that can be provided where clients go to refresh tokens. + refresh_endpoint: + signer: + # alias under which the private key is stored in the vault (JWK or PEM format) + privatekey_alias: + verifier: + # alias under which the public key is stored in the vault, that belongs to the private key ("privatekey_alias", JWK or PEM format) + publickey_alias: + aws: endpointOverride: "" accessKeyId: "" @@ -520,9 +538,20 @@ vault: transferProxyTokenSignerPublicKey: transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key -backendService: - # -- Specifies a backend service which will receive the EDR - httpProxyTokenReceiverUrl: "https://example.com" +networkPolicy: + # -- If `true` network policy will be created to restrict access to control- and dataplane + enabled: false + # -- Configuration of the controlplane component + controlplane: + # -- Specify from rule network policy for cp (defaults to all namespaces) + from: + - namespaceSelector: {} + # -- Configuration of the dataplane component + dataplane: + # -- Specify from rule network policy for dp (defaults to all namespaces) + from: + - namespaceSelector: {} + serviceAccount: # Specifies whether a service account should be created create: true @@ -533,6 +562,7 @@ serviceAccount: name: "" # -- Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) imagePullSecrets: [] + # -- Configurations for Helm tests tests: # -- Configure the hook-delete-policy for Helm tests diff --git a/charts/tractusx-connector-memory/Chart.yaml b/charts/tractusx-connector-memory/Chart.yaml index ae59aac76..919bff1ad 100644 --- a/charts/tractusx-connector-memory/Chart.yaml +++ b/charts/tractusx-connector-memory/Chart.yaml @@ -35,12 +35,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.6.0 +version: 0.7.0-rc1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.6.0" +appVersion: "0.7.0-rc1" home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-memory sources: - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-memory diff --git a/charts/tractusx-connector-memory/README.md b/charts/tractusx-connector-memory/README.md index 48a35e060..c01e61614 100644 --- a/charts/tractusx-connector-memory/README.md +++ b/charts/tractusx-connector-memory/README.md @@ -1,6 +1,6 @@ # tractusx-connector-memory -![Version: 0.6.0](https://img.shields.io/badge/Version-0.6.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.6.0](https://img.shields.io/badge/AppVersion-0.6.0-informational?style=flat-square) +![Version: 0.7.0-rc1](https://img.shields.io/badge/Version-0.7.0--rc1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.7.0-rc1](https://img.shields.io/badge/AppVersion-0.7.0--rc1-informational?style=flat-square) A Helm chart for Tractus-X Eclipse Data Space Connector based on memory. Please only use this for development or testing purposes, never in production workloads! @@ -39,7 +39,7 @@ Combined, run this shell command to start the in-memory Tractus-X EDC runtime: ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector-memory --version 0.6.0 \ +helm install my-release tractusx-edc/tractusx-connector-memory --version 0.7.0-rc1 \ -f /tractusx-connector-memory-test.yaml \ --set vault.secrets="client-secret:$YOUR_CLIENT_SECRET" ``` @@ -56,6 +56,11 @@ helm install my-release tractusx-edc/tractusx-connector-memory --version 0.6.0 \ | customCaCerts | object | `{}` | Add custom ca certificates to the truststore | | customLabels | object | `{}` | To add some custom labels | | fullnameOverride | string | `""` | | +| iatp.id | string | `"did:web:changeme"` | | +| iatp.sts.dim.url | string | `nil` | | +| iatp.sts.oauth.client.id | string | `nil` | | +| iatp.sts.oauth.client.secret_alias | string | `nil` | | +| iatp.sts.oauth.token_url | string | `nil` | | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | | nameOverride | string | `""` | | | participant.id | string | `"BPNLCHANGEME"` | BPN Number | @@ -65,12 +70,13 @@ helm install my-release tractusx-edc/tractusx-connector-memory --version 0.6.0 \ | runtime.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | runtime.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | runtime.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| runtime.bdrs.cache_validity_seconds | int | `600` | | +| runtime.bdrs.server.url | string | `nil` | | | runtime.businessPartnerValidation.log.agreementValidation | bool | `true` | | | runtime.debug.enabled | bool | `false` | | | runtime.debug.port | int | `1044` | | | runtime.debug.suspendOnStart | bool | `false` | | -| runtime.edr.transferProxyTokenValidity | string | `"2592000"` | | -| runtime.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"password","path":"/management","port":8081},"protocol":{"path":"/api/v1/dsp","port":8084},"proxy":{"path":"/proxy","port":8186},"public":{"path":"/api/public","port":8086}}` | endpoints of the control plane | +| runtime.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"password","path":"/management","port":8081},"protocol":{"path":"/api/v1/dsp","port":8084},"proxy":{"path":"/proxy","port":8186},"public":{"path":"/api/public","port":8086},"signaling":{"path":"/api/signaling","port":8087}}` | endpoints of the control plane | | runtime.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | | runtime.endpoints.control.path | string | `"/control"` | path for incoming api calls | | runtime.endpoints.control.port | int | `8083` | port for incoming api calls | @@ -84,6 +90,8 @@ helm install my-release tractusx-edc/tractusx-connector-memory --version 0.6.0 \ | runtime.endpoints.protocol | object | `{"path":"/api/v1/dsp","port":8084}` | dsp api, used for inter connector communication and must be internet facing | | runtime.endpoints.protocol.path | string | `"/api/v1/dsp"` | path for incoming api calls | | runtime.endpoints.protocol.port | int | `8084` | port for incoming api calls | +| runtime.endpoints.signaling.path | string | `"/api/signaling"` | path for incoming api calls | +| runtime.endpoints.signaling.port | int | `8087` | port for incoming api calls | | runtime.env | object | `{}` | | | runtime.envConfigMapNames | list | `[]` | | | runtime.envSecretNames | list | `[]` | | @@ -147,11 +155,11 @@ helm install my-release tractusx-edc/tractusx-connector-memory --version 0.6.0 \ | runtime.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | | runtime.service.annotations | object | `{}` | | | runtime.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | -| runtime.ssi.miw.authorityId | string | `""` | The BPN of the issuer authority | -| runtime.ssi.miw.url | string | `""` | MIW URL | -| runtime.ssi.oauth.client.id | string | `""` | The client ID for KeyCloak | -| runtime.ssi.oauth.client.secretAlias | string | `"client-secret"` | The alias under which the client secret is stored in the vault. | -| runtime.ssi.oauth.tokenurl | string | `""` | The URL (of KeyCloak), where access tokens can be obtained | +| runtime.token.refresh.expiry_seconds | int | `300` | | +| runtime.token.refresh.expiry_tolerance_seconds | int | `10` | | +| runtime.token.refresh.refresh_endpoint | string | `nil` | | +| runtime.token.signer.privatekey_alias | string | `nil` | | +| runtime.token.verifier.publickey_alias | string | `nil` | | | runtime.tolerations | list | `[]` | | | runtime.url.protocol | string | `""` | Explicitly declared url for reaching the dsp api (e.g. if ingresses not used) | | runtime.url.public | string | `""` | | diff --git a/charts/tractusx-connector-memory/templates/_helpers.tpl b/charts/tractusx-connector-memory/templates/_helpers.tpl index c65ba8c52..84f590f08 100644 --- a/charts/tractusx-connector-memory/templates/_helpers.tpl +++ b/charts/tractusx-connector-memory/templates/_helpers.tpl @@ -118,10 +118,10 @@ Control URL {{- end }} {{/* -Data Control URL +Data Signaling URL */}} -{{- define "txdc.dataplane.url.control" -}} -{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" . ) .Values.runtime.endpoints.control.port .Values.runtime.endpoints.control.path -}} +{{- define "txdc.dataplane.url.signaling" -}} +{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" . ) .Values.runtime.endpoints.signaling.port .Values.runtime.endpoints.signaling.path -}} {{- end }} {{/* diff --git a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml index 3634e2402..de0168256 100644 --- a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml @@ -146,23 +146,8 @@ spec: ######################## - name: EDC_PARTICIPANT_ID value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }} - - ########################## - # SSI / MIW CONFIGURATION - ########################## - - name: "TX_SSI_MIW_URL" - value: {{ .Values.runtime.ssi.miw.url | quote }} - - name: "TX_SSI_MIW_AUTHORITY_ID" - value: {{ .Values.runtime.ssi.miw.authorityId | quote }} - - name: "TX_SSI_OAUTH_TOKEN_URL" - value: {{ .Values.runtime.ssi.oauth.tokenurl | quote }} - - name: "TX_SSI_OAUTH_CLIENT_ID" - value: {{ .Values.runtime.ssi.oauth.client.id | quote }} - - name: "TX_SSI_OAUTH_CLIENT_SECRET_ALIAS" - value: {{ .Values.runtime.ssi.oauth.client.secretAlias | quote }} - - name: "TX_SSI_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s" (include "txdc.runtime.url.protocol" .) .Values.runtime.endpoints.protocol.path | quote }} - + - name: "EDC_IAM_ISSUER_ID" + value: {{ .Values.iatp.id | required ".Values.iatp.id is required" | quote}} ####### # API # @@ -192,6 +177,10 @@ spec: value: {{ .Values.runtime.endpoints.control.port | quote }} - name: "WEB_HTTP_CONTROL_PATH" value: {{ .Values.runtime.endpoints.control.path | quote }} + - name: "WEB_HTTP_SIGNALING_PORT" + value: {{ .Values.runtime.endpoints.signaling.port | quote }} + - name: "WEB_HTTP_SIGNALING_PATH" + value: {{ .Values.runtime.endpoints.signaling.path | quote }} - name: "WEB_HTTP_PROTOCOL_PORT" value: {{ .Values.runtime.endpoints.protocol.port | quote }} - name: "WEB_HTTP_PROTOCOL_PATH" @@ -214,13 +203,38 @@ spec: - name: "EDC_OAUTH_ENDPOINT_AUDIENCE" value: {{ printf "%s%s" (include "txdc.runtime.url.protocol" . ) .Values.runtime.endpoints.protocol.path | quote }} + ############################# + ## IATP / STS / DIM CONFIG ## + ############################# + - name: "EDC_IAM_STS_OAUTH_TOKEN_URL" + value: {{ .Values.iatp.sts.oauth.token_url | required ".Values.iatp.oauth.token_url is required" | quote}} + - name: "EDC_IAM_STS_OAUTH_CLIENT_ID" + value: {{ .Values.iatp.sts.oauth.client.id | required ".Values.iatp.sts.oauth.client.id is required" | quote}} + - name: "EDC_IAM_STS_OAUTH_CLIENT_SECRET_ALIAS" + value: {{ .Values.iatp.sts.oauth.client.secret_alias | required ".Values.iatp.sts.oauth.client.secret_alias is required" | quote}} + - name: "EDC_IAM_STS_DIM_URL" + value: {{ .Values.iatp.sts.dim.url | required ".Values.iatp.sts.dim.url is required" | quote}} + + ################# + ## BDRS CLIENT ## + ################# + + {{- if .Values.runtime.bdrs.server.url }} + - name: "TX_IAM_IATP_BDRS_SERVER_URL" + value: {{ .Values.runtime.bdrs.server.url | required ".Values.runtime.bdrs.server.url is required" | quote }} + {{- end }} + {{- if .Values.runtime.bdrs.cache_validity_seconds }} + - name: "TX_IAM_IATP_BDRS_CACHE_VALIDITY" + value: {{ .Values.runtime.bdrs.cache_validity_seconds | quote}} + {{- end}} + ################ ## DATA PLANE ## ################ # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/dataplane-selector-configuration - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_URL" - value: {{ include "txdc.dataplane.url.control" . }}/transfer + value: {{ include "txdc.dataplane.url.signaling" . }}/v1/dataflows - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_SOURCETYPES" value: "HttpData,AmazonS3" - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_DESTINATIONTYPES" @@ -229,42 +243,47 @@ spec: value: |- {{ printf "{ \"publicApiUrl\": \"%s\" }" (include "txdc.dataplane.url.public" . ) }} - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/data-plane-transfer - - name: "EDC_TRANSFER_PROXY_ENDPOINT" + + - name: "EDC_DATAPLANE_API_PUBLIC_BASEURL" value: {{ include "txdc.dataplane.url.public" . }} - {{- if .Values.vault.secretNames.transferProxyTokenSignerPrivateKey }} + + + ################## + ## TOKEN REFRESH + ################## + {{- if .Values.runtime.token.refresh.expiry_seconds }} + - name: "EDC_DATAPLANE_TOKEN_EXPIRY" + value: {{ .Values.runtime.token.refresh.expiry_seconds | quote}} + {{- end}} + + {{- if .Values.runtime.token.refresh.expiry_tolerance_seconds }} + - name: "EDC_DATAPLANE_TOKEN_EXPIRY_TOLERANCE" + value: {{ .Values.runtime.token.refresh.expiry_tolerance_seconds | quote }} + {{- end}} + + {{- if .Values.runtime.token.refresh.refresh_endpoint }} + - name: "EDC_DATAPLANE_TOKEN_REFRESH_ENDPOINT" + value: {{ .Values.runtime.token.refresh.refresh_endpoint }} + {{- else }} + - name: "EDC_DATAPLANE_TOKEN_REFRESH_ENDPOINT" + value: {{ include "txdc.dataplane.url.public" . }}/token + {{- end}} + - name: "EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS" - value: {{ .Values.vault.secretNames.transferProxyTokenSignerPrivateKey | quote }} - {{- end }} - {{- if .Values.vault.secretNames.transferProxyTokenSignerPublicKey }} + value: {{ .Values.runtime.token.signer.privatekey_alias | required ".Values.runtime.token.signer.privatekey_alias is required" | quote}} + - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" - value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | quote }} - {{- end }} - - name: "EDC_TRANSFER_PROXY_TOKEN_VALIDITY_SECONDS" - value: {{ .Values.runtime.edr.transferProxyTokenValidity | required ".Values.runtime.edr.transferProxyTokenValidity is required" | quote }} + value: {{ .Values.runtime.token.verifier.publickey_alias | required ".Values.runtime.token.verifier.publickey_alias" | quote }} - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/http-receiver - - name: "EDC_RECEIVER_HTTP_ENDPOINT" - value: {{ .Values.backendService.httpProxyTokenReceiverUrl | required ".Values.backendService.httpProxyTokenReceiverUrl is required" | quote }} ########### ## VAULT ## ########### # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/hashicorp-vault - - name: "SECRETS" + - name: "EDC_VAULT_SECRETS" value: {{ .Values.vault.secrets | quote}} - ##################### - ## DATA ENCRYPTION ## - ##################### - - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/data-encryption - - name: "EDC_DATA_ENCRYPTION_KEYS_ALIAS" - value: {{ .Values.vault.secretNames.transferProxyTokenEncryptionAesKey | quote }} - - name: "EDC_DATA_ENCRYPTION_ALGORITHM" - value: "AES" - ########################### ## AAS WRAPPER EXTENSION ## ########################### diff --git a/charts/tractusx-connector-memory/templates/service-runtime.yaml b/charts/tractusx-connector-memory/templates/service-runtime.yaml index d6c441b85..e7536bbcb 100644 --- a/charts/tractusx-connector-memory/templates/service-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/service-runtime.yaml @@ -51,5 +51,9 @@ spec: targetPort: public protocol: TCP name: public + - port: {{ .Values.runtime.endpoints.signaling.port }} + targetPort: signaling + protocol: TCP + name: signaling selector: {{- include "txdc.runtime.selectorLabels" . | nindent 4 }} diff --git a/charts/tractusx-connector-memory/values.yaml b/charts/tractusx-connector-memory/values.yaml index ef4341779..7f3cbd2af 100644 --- a/charts/tractusx-connector-memory/values.yaml +++ b/charts/tractusx-connector-memory/values.yaml @@ -18,7 +18,6 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# - --- # Default values for eclipse-dataspace-connector. # This is a YAML-formatted file. @@ -35,6 +34,18 @@ participant: # -- BPN Number id: "BPNLCHANGEME" +iatp: + # Decentralized IDentifier + id: "did:web:changeme" + sts: + dim: + url: + oauth: + token_url: + client: + id: + secret_alias: + # -- Add custom ca certificates to the truststore customCaCerts: {} @@ -110,26 +121,34 @@ runtime: proxy: port: 8186 path: /proxy + signaling: + # -- port for incoming api calls + port: 8087 + # -- path for incoming api calls + path: /api/signaling + businessPartnerValidation: log: agreementValidation: true - edr: - transferProxyTokenValidity: "2592000" - # SSI configuration - ssi: - miw: - # -- MIW URL - url: "" - # -- The BPN of the issuer authority - authorityId: "" - oauth: - # -- The URL (of KeyCloak), where access tokens can be obtained - tokenurl: "" - client: - # -- The client ID for KeyCloak - id: "" - # -- The alias under which the client secret is stored in the vault. - secretAlias: "client-secret" + token: + refresh: + expiry_seconds: 300 + expiry_tolerance_seconds: 10 + # optional URL that can be provided where clients go to refresh tokens. + refresh_endpoint: + signer: + # alias under which the private key is stored in the vault (JWK or PEM format) + privatekey_alias: + verifier: + # alias under which the public key is stored in the vault, that belongs to the private key ("privatekey_alias", JWK or PEM format) + publickey_alias: + + bdrs: + # time that a cached BPN/DID resolution map is valid in seconds, default is 10 min + cache_validity_seconds: 600 + server: + # URL of the BPN/DID Resolution Service - required: + url: service: # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. @@ -289,7 +308,7 @@ runtime: public: "" readiness: "" vault: - # secrets can be seeded by supplying them in a comma separated list key1:secret2,key2:secret2 + # secrets can be seeded by supplying them in a semicolon separated list key1:secret2;key2:secret2 secrets: "" secretNames: transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key diff --git a/charts/tractusx-connector/Chart.yaml b/charts/tractusx-connector/Chart.yaml index cce1ff152..ed18a582e 100644 --- a/charts/tractusx-connector/Chart.yaml +++ b/charts/tractusx-connector/Chart.yaml @@ -41,12 +41,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.6.0 +version: 0.7.0-rc1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.6.0" +appVersion: "0.7.0-rc1" home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector sources: - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector @@ -54,12 +54,12 @@ dependencies: # HashiCorp Vault - name: vault alias: vault - version: 0.20.0 + version: "0.27.0" repository: https://helm.releases.hashicorp.com condition: install.vault # PostgreSQL - name: postgresql alias: postgresql - version: 12.11.2 + version: "15.2.1" repository: https://charts.bitnami.com/bitnami condition: install.postgresql diff --git a/charts/tractusx-connector/README.md b/charts/tractusx-connector/README.md index 922e1b816..a734c214a 100644 --- a/charts/tractusx-connector/README.md +++ b/charts/tractusx-connector/README.md @@ -1,6 +1,6 @@ # tractusx-connector -![Version: 0.6.0](https://img.shields.io/badge/Version-0.6.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.6.0](https://img.shields.io/badge/AppVersion-0.6.0-informational?style=flat-square) +![Version: 0.7.0-rc1](https://img.shields.io/badge/Version-0.7.0--rc1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.7.0-rc1](https://img.shields.io/badge/AppVersion-0.7.0--rc1-informational?style=flat-square) A Helm chart for Tractus-X Eclipse Data Space Connector. The connector deployment consists of two runtime consists of a Control Plane and a Data Plane. Note that _no_ external dependencies such as a PostgreSQL database and HashiCorp Vault are included. @@ -42,7 +42,7 @@ Combined, run this shell command to start the in-memory Tractus-X EDC runtime: ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector --version 0.6.0 \ +helm install my-release tractusx-edc/tractusx-connector --version 0.7.0-rc1 \ -f /tractusx-connector-test.yaml ``` @@ -54,25 +54,25 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.6.0 \ | Repository | Name | Version | |------------|------|---------| -| https://charts.bitnami.com/bitnami | postgresql(postgresql) | 12.11.2 | -| https://helm.releases.hashicorp.com | vault(vault) | 0.20.0 | +| https://charts.bitnami.com/bitnami | postgresql(postgresql) | 15.2.1 | +| https://helm.releases.hashicorp.com | vault(vault) | 0.27.0 | ## Values | Key | Type | Default | Description | |-----|------|---------|-------------| -| backendService.httpProxyTokenReceiverUrl | string | `"https://example.com"` | Specifies a backend service which will receive the EDR | | controlplane.affinity | object | `{}` | | | controlplane.autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | | controlplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | | controlplane.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | controlplane.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | controlplane.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| controlplane.bdrs.cache_validity_seconds | int | `600` | | +| controlplane.bdrs.server.url | string | `nil` | | | controlplane.businessPartnerValidation.log.agreementValidation | bool | `true` | | | controlplane.debug.enabled | bool | `false` | | | controlplane.debug.port | int | `1044` | | | controlplane.debug.suspendOnStart | bool | `false` | | -| controlplane.edr.transferProxyTokenValidity | string | `"2592000"` | | | controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"password","path":"/management","port":8081},"metrics":{"path":"/metrics","port":9090},"protocol":{"path":"/api/v1/dsp","port":8084}}` | endpoints of the control plane | | controlplane.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | | controlplane.endpoints.control.path | string | `"/control"` | path for incoming api calls | @@ -154,11 +154,6 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.6.0 \ | controlplane.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | | controlplane.service.annotations | object | `{}` | | | controlplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | -| controlplane.ssi.miw.authorityId | string | `""` | The BPN of the issuer authority | -| controlplane.ssi.miw.url | string | `""` | MIW URL | -| controlplane.ssi.oauth.client.id | string | `""` | The client ID for KeyCloak | -| controlplane.ssi.oauth.client.secretAlias | string | `"client-secret"` | The alias under which the client secret is stored in the vault. | -| controlplane.ssi.oauth.tokenurl | string | `""` | The URL (of KeyCloak), where access tokens can be obtained | | controlplane.tolerations | list | `[]` | | | controlplane.url.protocol | string | `""` | Explicitly declared url for reaching the dsp api (e.g. if ingresses not used) | | controlplane.volumeMounts | string | `nil` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | @@ -177,8 +172,6 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.6.0 \ | dataplane.debug.enabled | bool | `false` | | | dataplane.debug.port | int | `1044` | | | dataplane.debug.suspendOnStart | bool | `false` | | -| dataplane.endpoints.control.path | string | `"/api/dataplane/control"` | | -| dataplane.endpoints.control.port | int | `8083` | | | dataplane.endpoints.default.path | string | `"/api"` | | | dataplane.endpoints.default.port | int | `8080` | | | dataplane.endpoints.metrics.path | string | `"/metrics"` | | @@ -188,6 +181,8 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.6.0 \ | dataplane.endpoints.proxy.port | int | `8186` | | | dataplane.endpoints.public.path | string | `"/api/public"` | | | dataplane.endpoints.public.port | int | `8081` | | +| dataplane.endpoints.signaling.path | string | `"/api/signaling"` | | +| dataplane.endpoints.signaling.port | int | `8083` | | | dataplane.env | object | `{}` | | | dataplane.envConfigMapNames | list | `[]` | | | dataplane.envSecretNames | list | `[]` | | @@ -242,11 +237,21 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.6.0 \ | dataplane.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | | dataplane.service.port | int | `80` | | | dataplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | +| dataplane.token.refresh.expiry_seconds | int | `300` | | +| dataplane.token.refresh.expiry_tolerance_seconds | int | `10` | | +| dataplane.token.refresh.refresh_endpoint | string | `nil` | | +| dataplane.token.signer.privatekey_alias | string | `nil` | | +| dataplane.token.verifier.publickey_alias | string | `nil` | | | dataplane.tolerations | list | `[]` | | | dataplane.url.public | string | `""` | Explicitly declared url for reaching the public api (e.g. if ingresses not used) | | dataplane.volumeMounts | string | `nil` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | | dataplane.volumes | string | `nil` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | | fullnameOverride | string | `""` | | +| iatp.id | string | `"did:web:changeme"` | | +| iatp.sts.dim.url | string | `nil` | | +| iatp.sts.oauth.client.id | string | `nil` | | +| iatp.sts.oauth.client.secret_alias | string | `nil` | | +| iatp.sts.oauth.token_url | string | `nil` | | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | | install.postgresql | bool | `true` | | | install.vault | bool | `true` | | diff --git a/charts/tractusx-connector/templates/_helpers.tpl b/charts/tractusx-connector/templates/_helpers.tpl index 46bc283dc..21581f158 100644 --- a/charts/tractusx-connector/templates/_helpers.tpl +++ b/charts/tractusx-connector/templates/_helpers.tpl @@ -143,10 +143,10 @@ Control Plane URL {{- end }} {{/* -Data Control URL +Data Signaling URL */}} -{{- define "txdc.dataplane.url.control" -}} -{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" . ) .Values.dataplane.endpoints.control.port .Values.dataplane.endpoints.control.path -}} +{{- define "txdc.dataplane.url.signaling" -}} +{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" . ) .Values.dataplane.endpoints.signaling.port .Values.dataplane.endpoints.signaling.path -}} {{- end }} {{/* diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 1cc371318..9ab02e54a 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -146,22 +146,8 @@ spec: ######################## - name: EDC_PARTICIPANT_ID value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }} - - ########################## - # SSI / MIW CONFIGURATION - ########################## - - name: "TX_SSI_MIW_URL" - value: {{ .Values.controlplane.ssi.miw.url | quote }} - - name: "TX_SSI_MIW_AUTHORITY_ID" - value: {{ .Values.controlplane.ssi.miw.authorityId | quote }} - - name: "TX_SSI_OAUTH_TOKEN_URL" - value: {{ .Values.controlplane.ssi.oauth.tokenurl | quote }} - - name: "TX_SSI_OAUTH_CLIENT_ID" - value: {{ .Values.controlplane.ssi.oauth.client.id | quote }} - - name: "TX_SSI_OAUTH_CLIENT_SECRET_ALIAS" - value: {{ .Values.controlplane.ssi.oauth.client.secretAlias | quote }} - - name: "TX_SSI_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" .) .Values.controlplane.endpoints.protocol.path | quote }} + - name: "EDC_IAM_ISSUER_ID" + value: {{ .Values.iatp.id | required ".Values.iatp.id is required" | quote}} ####### # API # @@ -252,16 +238,6 @@ spec: - name: "EDC_DATASOURCE_TRANSFERPROCESS_URL" value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/edr-cache-sql - - name: "EDC_DATASOURCE_EDR_NAME" - value: "edr" - - name: "EDC_DATASOURCE_EDR_USER" - value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - - name: "EDC_DATASOURCE_EDR_PASSWORD" - value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - - name: "EDC_DATASOURCE_EDR_URL" - value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/policy-monitor/store/sql/policy-monitor-store-sql - name: "EDC_DATASOURCE_POLICY-MONITOR_NAME" value: "policy-monitor" @@ -282,39 +258,61 @@ spec: - name: "EDC_DATASOURCE_BPN_URL" value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/common/store/sql/edr-index-sql + - name: "EDC_DATASOURCE_EDR_NAME" + value: "edr" + - name: "EDC_DATASOURCE_EDR_USER" + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} + - name: "EDC_DATASOURCE_EDR_PASSWORD" + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} + - name: "EDC_DATASOURCE_EDR_URL" + value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + + ############################# + ## IATP / STS / DIM CONFIG ## + ############################# + - name: "EDC_IAM_STS_OAUTH_TOKEN_URL" + value: {{ .Values.iatp.sts.oauth.token_url | required ".Values.iatp.oauth.token_url is required" | quote}} + - name: "EDC_IAM_STS_OAUTH_CLIENT_ID" + value: {{ .Values.iatp.sts.oauth.client.id | required ".Values.iatp.sts.oauth.client.id is required" | quote}} + - name: "EDC_IAM_STS_OAUTH_CLIENT_SECRET_ALIAS" + value: {{ .Values.iatp.sts.oauth.client.secret_alias | required ".Values.iatp.sts.oauth.client.secret_alias is required" | quote}} + - name: "EDC_IAM_STS_DIM_URL" + value: {{ .Values.iatp.sts.dim.url | required ".Values.iatp.sts.dim.url is required" | quote}} + + ################# + ## BDRS CLIENT ## + ################# + + {{- if .Values.controlplane.bdrs.server.url }} + - name: "TX_IAM_IATP_BDRS_SERVER_URL" + value: {{ .Values.controlplane.bdrs.server.url | required ".Values.controlplane.bdrs.server.url is required" | quote }} + {{- end }} + {{- if .Values.controlplane.bdrs.cache_validity_seconds }} + - name: "TX_IAM_IATP_BDRS_CACHE_VALIDITY" + value: {{ .Values.controlplane.bdrs.cache_validity_seconds | quote}} + {{- end}} + ################ ## DATA PLANE ## ################ # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/dataplane-selector-configuration - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_URL" - value: {{ include "txdc.dataplane.url.control" . }}/transfer + value: {{ include "txdc.dataplane.url.signaling" . }}/v1/dataflows + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_TRANSFERTYPES" + value: "HttpData-PULL,AmazonS3-PUSH,AzureStorage-PUSH" - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_SOURCETYPES" - value: "HttpData,AmazonS3" + value: "HttpData,AmazonS3,AzureStorage" - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_DESTINATIONTYPES" - value: "HttpProxy,AmazonS3" + value: "HttpProxy,AmazonS3,AzureStorage" - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_PROPERTIES" value: |- {{ printf "{ \"publicApiUrl\": \"%s\" }" (include "txdc.dataplane.url.public" . ) }} - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/data-plane-transfer - - name: "EDC_TRANSFER_PROXY_ENDPOINT" - value: {{ include "txdc.dataplane.url.public" . }} - {{- if .Values.vault.secretNames.transferProxyTokenSignerPrivateKey }} - - name: "EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS" - value: {{ .Values.vault.secretNames.transferProxyTokenSignerPrivateKey | quote }} - {{- end }} - {{- if .Values.vault.secretNames.transferProxyTokenSignerPublicKey }} - - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" - value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | quote }} - {{- end }} - - name: "EDC_TRANSFER_PROXY_TOKEN_VALIDITY_SECONDS" - value: {{ .Values.controlplane.edr.transferProxyTokenValidity | required ".Values.controlplane.edr.transferProxyTokenValidity is required" | quote }} - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/transfer/transfer-pull-http-dynamic-receiver - - name: "EDC_RECEIVER_HTTP_DYNAMIC_ENDPOINT" - value: {{ .Values.backendService.httpProxyTokenReceiverUrl | required ".Values.backendService.httpProxyTokenReceiverUrl is required" | quote }} ########### ## VAULT ## @@ -336,24 +334,6 @@ spec: - name: "EDC_VAULT_HASHICORP_API_HEALTH_CHECK_PATH" value: {{ .Values.vault.hashicorp.paths.health | quote }} - ##################### - ## DATA ENCRYPTION ## - ##################### - - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/data-encryption - - name: "EDC_DATA_ENCRYPTION_KEYS_ALIAS" - value: {{ .Values.vault.secretNames.transferProxyTokenEncryptionAesKey | quote }} - - name: "EDC_DATA_ENCRYPTION_ALGORITHM" - value: "AES" - - ########################### - ## AAS WRAPPER EXTENSION ## - ########################### - - name: "EDC_CP_ADAPTER_CACHE_CATALOG_EXPIRE_AFTER" - value: "0" - - name: "EDC_CP_ADAPTER_REUSE_CONTRACT_AGREEMENT" - value: "0" - ########################### ## BUSINESS PARTNER NUMBER VALIDATION EXTENSION ## ########################### diff --git a/charts/tractusx-connector/templates/deployment-dataplane.yaml b/charts/tractusx-connector/templates/deployment-dataplane.yaml index 8a43fc37b..1b633269b 100644 --- a/charts/tractusx-connector/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector/templates/deployment-dataplane.yaml @@ -1,24 +1,24 @@ ################################################################################# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# 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 -################################################################################# + # Copyright (c) 2023 ZF Friedrichshafen AG + # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH + # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + # 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 + ################################################################################# --- @@ -145,6 +145,8 @@ spec: ######################## - name: EDC_PARTICIPANT_ID value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }} + - name: "EDC_IAM_ISSUER_ID" + value: {{ .Values.iatp.id | required ".Values.iatp.id is required" | quote}} ####### # API # @@ -155,10 +157,10 @@ spec: value: {{ .Values.dataplane.endpoints.default.port | quote }} - name: "WEB_HTTP_DEFAULT_PATH" value: {{ .Values.dataplane.endpoints.default.path | quote }} - - name: "WEB_HTTP_CONTROL_PORT" - value: {{ .Values.dataplane.endpoints.control.port | quote }} - - name: "WEB_HTTP_CONTROL_PATH" - value: {{ .Values.dataplane.endpoints.control.path | quote }} + - name: "WEB_HTTP_SIGNALING_PORT" + value: {{ .Values.dataplane.endpoints.signaling.port | quote }} + - name: "WEB_HTTP_SIGNALING_PATH" + value: {{ .Values.dataplane.endpoints.signaling.path | quote }} - name: "WEB_HTTP_PUBLIC_PORT" value: {{ .Values.dataplane.endpoints.public.port | quote }} - name: "WEB_HTTP_PUBLIC_PATH" @@ -182,20 +184,6 @@ spec: value: {{ .Values.dataplane.aws.accessKeyId | quote }} {{- end }} - ############### - ## EDR CACHE ## - ############### - - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/edr-cache-sql - - name: "EDC_DATASOURCE_EDR_NAME" - value: "edr" - - name: "EDC_DATASOURCE_EDR_USER" - value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - - name: "EDC_DATASOURCE_EDR_PASSWORD" - value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - - name: "EDC_DATASOURCE_EDR_URL" - value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} - ########### ## VAULT ## ########### @@ -216,6 +204,86 @@ spec: - name: "EDC_VAULT_HASHICORP_API_HEALTH_CHECK_PATH" value: {{ .Values.vault.hashicorp.paths.health | quote }} + ############################# + ## IATP / STS / DIM CONFIG ## + ############################# + - name: "EDC_IAM_STS_OAUTH_TOKEN_URL" + value: {{ .Values.iatp.sts.oauth.token_url | required ".Values.iatp.oauth.token_url is required" | quote}} + - name: "EDC_IAM_STS_OAUTH_CLIENT_ID" + value: {{ .Values.iatp.sts.oauth.client.id | required ".Values.iatp.sts.oauth.client.id is required" | quote}} + - name: "EDC_IAM_STS_OAUTH_CLIENT_SECRET_ALIAS" + value: {{ .Values.iatp.sts.oauth.client.secret_alias | required ".Values.iatp.sts.oauth.client.secret_alias is required" | quote}} + - name: "EDC_IAM_STS_DIM_URL" + value: {{ .Values.iatp.sts.dim.url | required ".Values.iatp.sts.dim.url is required" | quote}} + + ################ + ## POSTGRESQL ## + ################ + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/common/store/sql/edr-index-sql + - name: "EDC_DATASOURCE_EDR_NAME" + value: "edr" + - name: "EDC_DATASOURCE_EDR_USER" + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} + - name: "EDC_DATASOURCE_EDR_PASSWORD" + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} + - name: "EDC_DATASOURCE_EDR_URL" + value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/data-plane/store/sql/data-plane-store-sql + - name: "EDC_DATASOURCE_DATAPLANE_NAME" + value: "edr" + - name: "EDC_DATASOURCE_DATAPLANE_USER" + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} + - name: "EDC_DATASOURCE_DATAPLANE_PASSWORD" + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} + - name: "EDC_DATASOURCE_DATAPLANE_URL" + value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/data-plane/store/sql/accesstokendata-store-sql + - name: "EDC_DATASOURCE_ACCESSTOKENDATA_NAME" + value: "edr" + - name: "EDC_DATASOURCE_ACCESSTOKENDATA_USER" + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} + - name: "EDC_DATASOURCE_ACCESSTOKENDATA_PASSWORD" + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} + - name: "EDC_DATASOURCE_ACCESSTOKENDATA_URL" + value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + + ######################### + ## DATA PLANE PUBLIC API + ######################## + - name: "EDC_DATAPLANE_API_PUBLIC_BASEURL" + value: {{ include "txdc.dataplane.url.public" . }} + + + ################## + ## TOKEN REFRESH + ################## + {{- if .Values.dataplane.token.refresh.expiry_seconds }} + - name: "EDC_DATAPLANE_TOKEN_EXPIRY" + value: {{ .Values.dataplane.token.refresh.expiry_seconds | quote}} + {{- end}} + + {{- if .Values.dataplane.token.refresh.expiry_tolerance_seconds }} + - name: "EDC_DATAPLANE_TOKEN_EXPIRY_TOLERANCE" + value: {{ .Values.dataplane.token.refresh.expiry_tolerance_seconds | quote }} + {{- end}} + + {{- if .Values.dataplane.token.refresh.refresh_endpoint }} + - name: "EDC_DATAPLANE_TOKEN_REFRESH_ENDPOINT" + value: {{ .Values.dataplane.token.refresh.refresh_endpoint }} + {{- else}} + - name: "EDC_DATAPLANE_TOKEN_REFRESH_ENDPOINT" + value: {{ include "txdc.dataplane.url.public" . }}/token + {{- end}} + + - name: "EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS" + value: {{ .Values.dataplane.token.signer.privatekey_alias | required ".Values.dataplane.token.signer.privatekey_alias is required" | quote}} + + - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" + value: {{ .Values.dataplane.token.verifier.publickey_alias | required ".Values.dataplane.token.verifier.publickey_alias" | quote }} + ###################################### ## Additional environment variables ## ###################################### diff --git a/charts/tractusx-connector/templates/service-dataplane.yaml b/charts/tractusx-connector/templates/service-dataplane.yaml index 0f1fc5e8c..97e8c07ea 100644 --- a/charts/tractusx-connector/templates/service-dataplane.yaml +++ b/charts/tractusx-connector/templates/service-dataplane.yaml @@ -36,10 +36,10 @@ spec: targetPort: default protocol: TCP name: default - - port: {{ .Values.dataplane.endpoints.control.port }} - targetPort: control + - port: {{ .Values.dataplane.endpoints.signaling.port }} + targetPort: signaling protocol: TCP - name: control + name: signaling - port: {{ .Values.dataplane.endpoints.public.port }} targetPort: public protocol: TCP diff --git a/charts/tractusx-connector/values.yaml b/charts/tractusx-connector/values.yaml index 0de6875f6..160445e09 100644 --- a/charts/tractusx-connector/values.yaml +++ b/charts/tractusx-connector/values.yaml @@ -40,6 +40,20 @@ participant: # -- BPN Number id: "BPNLCHANGEME" + +iatp: + # Decentralized IDentifier + id: "did:web:changeme" + sts: + dim: + url: + oauth: + token_url: + client: + id: + secret_alias: + + controlplane: image: # -- Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically @@ -117,23 +131,13 @@ controlplane: businessPartnerValidation: log: agreementValidation: true - edr: - transferProxyTokenValidity: "2592000" - # SSI configuration - ssi: - miw: - # -- MIW URL - url: "" - # -- The BPN of the issuer authority - authorityId: "" - oauth: - # -- The URL (of KeyCloak), where access tokens can be obtained - tokenurl: "" - client: - # -- The client ID for KeyCloak - id: "" - # -- The alias under which the client secret is stored in the vault. - secretAlias: "client-secret" + + bdrs: + # time that a cached BPN/DID resolution map is valid in seconds, default is 10 min + cache_validity_seconds: 600 + server: + # URL of the BPN/DID Resolution Service - required: + url: service: # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. @@ -347,9 +351,9 @@ dataplane: public: port: 8081 path: /api/public - control: + signaling: port: 8083 - path: /api/dataplane/control + path: /api/signaling proxy: port: 8186 path: /proxy @@ -357,6 +361,20 @@ dataplane: metrics: port: 9090 path: /metrics + + token: + refresh: + expiry_seconds: 300 + expiry_tolerance_seconds: 10 + # optional URL that can be provided where clients go to refresh tokens. + refresh_endpoint: + signer: + # alias under which the private key is stored in the vault (JWK or PEM format) + privatekey_alias: + verifier: + # alias under which the public key is stored in the vault, that belongs to the private key ("privatekey_alias", JWK or PEM format) + publickey_alias: + aws: endpointOverride: "" accessKeyId: "" @@ -525,9 +543,6 @@ vault: transferProxyTokenSignerPrivateKey: transferProxyTokenSignerPublicKey: transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key -backendService: - # -- Specifies a backend service which will receive the EDR - httpProxyTokenReceiverUrl: "https://example.com" networkPolicy: # -- If `true` network policy will be created to restrict access to control- and dataplane diff --git a/edc-extensions/ssi/ssi-identity-extractor/build.gradle.kts b/core/core-utils/build.gradle.kts similarity index 77% rename from edc-extensions/ssi/ssi-identity-extractor/build.gradle.kts rename to core/core-utils/build.gradle.kts index f5049009d..5a8a90ae2 100644 --- a/edc-extensions/ssi/ssi-identity-extractor/build.gradle.kts +++ b/core/core-utils/build.gradle.kts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -19,13 +19,10 @@ plugins { `java-library` - `maven-publish` } dependencies { - implementation(project(":spi:ssi-spi")) implementation(libs.edc.spi.core) - implementation(libs.jakartaJson) - testImplementation(testFixtures(libs.edc.junit)) - testImplementation(testFixtures(project(":spi:ssi-spi"))) + implementation(libs.edc.spi.identitytrust) + implementation(libs.edc.spi.vc) } diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/utils/PathUtils.java b/core/core-utils/src/main/java/org/eclipse/tractusx/edc/core/utils/PathUtils.java similarity index 95% rename from edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/utils/PathUtils.java rename to core/core-utils/src/main/java/org/eclipse/tractusx/edc/core/utils/PathUtils.java index 7f77ad5ce..f439ab596 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/utils/PathUtils.java +++ b/core/core-utils/src/main/java/org/eclipse/tractusx/edc/core/utils/PathUtils.java @@ -17,7 +17,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.miw.utils; +package org.eclipse.tractusx.edc.core.utils; public class PathUtils { diff --git a/core/core-utils/src/main/java/org/eclipse/tractusx/edc/core/utils/RequiredConfigWarnings.java b/core/core-utils/src/main/java/org/eclipse/tractusx/edc/core/utils/RequiredConfigWarnings.java new file mode 100644 index 000000000..8a5ad0b87 --- /dev/null +++ b/core/core-utils/src/main/java/org/eclipse/tractusx/edc/core/utils/RequiredConfigWarnings.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.core.utils; + +import org.eclipse.edc.spi.monitor.Monitor; + +public class RequiredConfigWarnings { + + public static void warningNotPresent(Monitor monitor, String missingConfig) { + monitor.severe("Mandatory config value missing: '%s'. This runtime will not be fully operational! Starting with v0.7.x this will be a runtime error.".formatted(missingConfig)); + } +} diff --git a/core/core-utils/src/main/java/org/eclipse/tractusx/edc/core/utils/credentials/CredentialTypePredicate.java b/core/core-utils/src/main/java/org/eclipse/tractusx/edc/core/utils/credentials/CredentialTypePredicate.java new file mode 100644 index 000000000..5523299f5 --- /dev/null +++ b/core/core-utils/src/main/java/org/eclipse/tractusx/edc/core/utils/credentials/CredentialTypePredicate.java @@ -0,0 +1,40 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.core.utils.credentials; + + +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential; + +import java.util.function.Predicate; + +public class CredentialTypePredicate implements Predicate { + private final String credentialNamespace; + private final String credentialType; + + public CredentialTypePredicate(String credentialNamespace, String credentialType) { + this.credentialNamespace = credentialNamespace; + this.credentialType = credentialType; + } + + @Override + public boolean test(VerifiableCredential credential) { + return credential.getType().contains(credentialType) || credential.getType().contains(credentialNamespace + credentialType); + } +} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/utils/PathUtilsTest.java b/core/core-utils/src/test/java/org/eclipse/tractusx/edc/core/utils/PathUtilsTest.java similarity index 97% rename from edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/utils/PathUtilsTest.java rename to core/core-utils/src/test/java/org/eclipse/tractusx/edc/core/utils/PathUtilsTest.java index 9f8879fb1..1654d8690 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/utils/PathUtilsTest.java +++ b/core/core-utils/src/test/java/org/eclipse/tractusx/edc/core/utils/PathUtilsTest.java @@ -17,7 +17,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.miw.utils; +package org.eclipse.tractusx.edc.core.utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; diff --git a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/EdrCacheEntryPropertyLookup.java b/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/EdrCacheEntryPropertyLookup.java deleted file mode 100644 index 9cc2de91c..000000000 --- a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/EdrCacheEntryPropertyLookup.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.core.defaults; - -import org.eclipse.edc.spi.query.PropertyLookup; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; - -/** - * This class is almost a 1:1 copy of the {@code CriterionToPredicateConverterImpl} (except for the {@code property()} method) from the {@code control-plane-core} module. - * Pulling in that module is not possible, because that would pull in almost the entire Control Plane - */ -public class EdrCacheEntryPropertyLookup implements PropertyLookup { - - public static final String ASSET_ID = "assetId"; - public static final String AGREEMENT_ID = "agreementId"; - public static final String PROVIDER_ID = "providerId"; - public static final String CONTRACT_NEGOTIATION_ID = "contractNegotiationId"; - public static final String STATE = "state"; - - @Override - public Object getProperty(String key, Object object) { - return property(key, object); - } - - protected Object property(String key, Object object) { - if (object instanceof EndpointDataReferenceEntry entry) { - return switch (key) { - case ASSET_ID -> entry.getAssetId(); - case AGREEMENT_ID -> entry.getAgreementId(); - case PROVIDER_ID -> entry.getProviderId(); - case CONTRACT_NEGOTIATION_ID -> entry.getContractNegotiationId(); - case STATE -> entry.getState(); - default -> null; - }; - } - return null; - } - -} diff --git a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCache.java b/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCache.java deleted file mode 100644 index 7d9ab3376..000000000 --- a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCache.java +++ /dev/null @@ -1,241 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.core.defaults; - -import org.eclipse.edc.spi.entity.StatefulEntity; -import org.eclipse.edc.spi.persistence.Lease; -import org.eclipse.edc.spi.query.Criterion; -import org.eclipse.edc.spi.query.CriterionOperatorRegistry; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.result.StoreResult; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.util.concurrency.LockManager; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.time.Clock; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.function.Predicate; -import java.util.stream.Stream; - -import static java.lang.String.format; -import static java.util.Collections.emptyList; -import static java.util.Comparator.comparingLong; -import static java.util.stream.Collectors.toList; -import static org.eclipse.edc.spi.result.StoreResult.notFound; -import static org.eclipse.edc.spi.result.StoreResult.success; - -/** - * An in-memory, threadsafe implementation of the cache. - */ -public class InMemoryEndpointDataReferenceCache implements EndpointDataReferenceCache { - private static final long DEFAULT_LEASE_TIME_MILLIS = 60_000; - protected final CriterionOperatorRegistry criterionOperatorRegistry; - private final LockManager lockManager; - private final Map> entriesByAssetId; - - private final Map entriesByEdrId; - - private final Map edrsByTransferProcessId; - private final String lockId; - - private final Map leases; - - private final Clock clock; - - - public InMemoryEndpointDataReferenceCache(CriterionOperatorRegistry criterionOperatorRegistry) { - this(criterionOperatorRegistry, UUID.randomUUID().toString(), Clock.systemUTC(), new ConcurrentHashMap<>()); - } - - public InMemoryEndpointDataReferenceCache(CriterionOperatorRegistry criterionOperatorRegistry, String lockId, Clock clock, Map leases) { - this.criterionOperatorRegistry = criterionOperatorRegistry; - this.lockId = lockId; - lockManager = new LockManager(new ReentrantReadWriteLock()); - entriesByAssetId = new HashMap<>(); - entriesByEdrId = new ConcurrentHashMap<>(); - edrsByTransferProcessId = new HashMap<>(); - this.leases = leases; - this.clock = clock; - } - - @Override - public @Nullable EndpointDataReference resolveReference(String transferProcessId) { - return lockManager.readLock(() -> edrsByTransferProcessId.get(transferProcessId)); - } - - @Override - public StoreResult findByIdAndLease(String transferProcessId) { - return lockManager.readLock(() -> { - var edr = edrsByTransferProcessId.get(transferProcessId); - var edrEntry = entriesByEdrId.get(edr.getId()); - return edrEntry == null ? StoreResult.notFound(format("EndpointDataReferenceEntry %s not found", transferProcessId)) : - StoreResult.success(edrEntry); - }); - } - - @Override - @NotNull - public List referencesForAsset(String assetId, String providerId) { - return lockManager.readLock(() -> { - var entries = entriesByAssetId.get(assetId); - - Predicate providerIdFilter = (cached) -> - Optional.ofNullable(providerId) - .map(id -> id.equals(cached.getProviderId())) - .orElse(true); - - if (entries == null) { - return emptyList(); - } - return entries.stream() - .filter(providerIdFilter) - .filter(this::filterActive) - .map(e -> resolveReference(e.getTransferProcessId())) - .filter(Objects::nonNull) - .collect(toList()); - - }); - } - - @Override - public Stream queryForEntries(QuerySpec spec) { - return filterBy(spec.getFilterExpression()); - } - - @Override - public void save(EndpointDataReferenceEntry entry, EndpointDataReference edr) { - lockManager.writeLock(() -> { - entriesByEdrId.put(edr.getId(), entry); - var list = entriesByAssetId.computeIfAbsent(entry.getAssetId(), k -> new ArrayList<>()); - list.add(entry); - - edrsByTransferProcessId.put(entry.getTransferProcessId(), edr); - return null; - }); - } - - @Override - public void update(EndpointDataReferenceEntry entry) { - lockManager.writeLock(() -> { - acquireLease(entry.getTransferProcessId(), lockId); - var edr = edrsByTransferProcessId.get(entry.getTransferProcessId()); - entriesByEdrId.put(edr.getId(), entry); - var list = entriesByAssetId.computeIfAbsent(entry.getAssetId(), k -> new ArrayList<>()); - list.removeIf((edrEntry) -> edrEntry.getTransferProcessId().equals(entry.getTransferProcessId())); - list.add(entry); - freeLease(entry.getTransferProcessId()); - return null; - }); - } - - @Override - public StoreResult deleteByTransferProcessId(String id) { - return lockManager.writeLock(() -> { - if (isLeased(id)) { - throw new IllegalStateException("EndpointDataReferenceEntry is leased and cannot be deleted!"); - } - var edr = edrsByTransferProcessId.remove(id); - if (edr == null) { - return notFound("EDR entry not found for id: " + id); - } - var entry = entriesByEdrId.remove(edr.getId()); - var entries = entriesByAssetId.get(entry.getAssetId()); - entries.remove(entry); - if (entries.isEmpty()) { - entriesByAssetId.remove(entry.getAssetId()); - } - - return success(entry); - }); - } - - @Override - public EndpointDataReferenceEntry findById(String correlationId) { - return findByIdAndLease(correlationId).orElse(storeFailure -> null); - } - - @Override - public @NotNull List nextNotLeased(int max, Criterion... criteria) { - return leaseAndGet(max, criteria); - } - - @Override - public void save(EndpointDataReferenceEntry entity) { - throw new UnsupportedOperationException("Please use save(EndpointDataReferenceEntry, EndpointDataReference) instead!"); - } - - private @NotNull List leaseAndGet(int max, Criterion... criteria) { - return lockManager.writeLock(() -> { - var filterPredicate = Arrays.stream(criteria).map(criterionOperatorRegistry::toPredicate).reduce(x -> true, Predicate::and); - var entities = entriesByEdrId.values().stream() - .filter(filterPredicate) - .filter(e -> !isLeased(e.getId())) - .sorted(comparingLong(StatefulEntity::getStateTimestamp)) //order by state timestamp, oldest first - .limit(max) - .toList(); - entities.forEach(i -> acquireLease(i.getId(), lockId)); - return entities.stream().map(StatefulEntity::copy).collect(toList()); - }); - } - - private Stream filterBy(List criteria) { - return lockManager.readLock(() -> { - var predicate = criteria.stream() - .map(criterionOperatorRegistry::toPredicate) - .reduce(x -> true, Predicate::and); - - return entriesByEdrId.values().stream() - .filter(predicate); - }); - - } - - private void freeLease(String id) { - leases.remove(id); - } - - private void acquireLease(String id, String lockId) { - if (!isLeased(id) || isLeasedBy(id, lockId)) { - leases.put(id, new Lease(lockId, clock.millis(), DEFAULT_LEASE_TIME_MILLIS)); - } else { - throw new IllegalStateException("Cannot acquire lease, is already leased by someone else!"); - } - } - - private boolean isLeased(String id) { - return leases.containsKey(id) && !leases.get(id).isExpired(clock.millis()); - } - - private boolean isLeasedBy(String id, String lockId) { - return isLeased(id) && leases.get(id).getLeasedBy().equals(lockId); - } -} diff --git a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/PersistentCacheEntry.java b/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/PersistentCacheEntry.java deleted file mode 100644 index a14ebcf82..000000000 --- a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/PersistentCacheEntry.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.core.defaults; - -import com.fasterxml.jackson.annotation.JsonProperty; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; - -/** - * A wrapper to persist {@link EndpointDataReferenceEntry}s and {@link EndpointDataReference}s. - */ -public class PersistentCacheEntry { - private EndpointDataReferenceEntry edrEntry; - private EndpointDataReference edr; - - public PersistentCacheEntry(@JsonProperty("edrEntry") EndpointDataReferenceEntry edrEntry, @JsonProperty("edr") EndpointDataReference edr) { - this.edrEntry = edrEntry; - this.edr = edr; - } - - public EndpointDataReferenceEntry getEdrEntry() { - return edrEntry; - } - - public EndpointDataReference getEdr() { - return edr; - } -} diff --git a/core/edr-cache-core/src/test/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCacheTest.java b/core/edr-cache-core/src/test/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCacheTest.java deleted file mode 100644 index 42341777a..000000000 --- a/core/edr-cache-core/src/test/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCacheTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.core.defaults; - -import org.eclipse.edc.connector.core.store.CriterionOperatorRegistryImpl; -import org.eclipse.edc.spi.persistence.Lease; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCacheTestBase; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.junit.jupiter.api.BeforeEach; - -import java.time.Clock; -import java.time.Duration; -import java.util.HashMap; - -class InMemoryEndpointDataReferenceCacheTest extends EndpointDataReferenceCacheTestBase { - private final HashMap leases = new HashMap<>(); - private InMemoryEndpointDataReferenceCache cache; - - @BeforeEach - void setUp() { - var criterionOperatorRegistry = CriterionOperatorRegistryImpl.ofDefaults(); - criterionOperatorRegistry.registerPropertyLookup(new EdrCacheEntryPropertyLookup()); - cache = new InMemoryEndpointDataReferenceCache(criterionOperatorRegistry, CONNECTOR_NAME, Clock.systemUTC(), leases); - } - - @Override - protected EndpointDataReferenceCache getStore() { - return cache; - } - - @Override - protected void lockEntity(String negotiationId, String owner, Duration duration) { - leases.put(negotiationId, new Lease(owner, Clock.systemUTC().millis(), duration.toMillis())); - } - - @Override - protected boolean isLockedBy(String negotiationId, String owner) { - return leases.entrySet().stream().anyMatch(e -> e.getKey().equals(negotiationId) && - e.getValue().getLeasedBy().equals(owner) && - !isExpired(e.getValue())); - } - - private boolean isExpired(Lease e) { - return e.getLeasedAt() + e.getLeaseDuration() < Clock.systemUTC().millis(); - } -} diff --git a/core/edr-cache-core/src/test/java/org/eclipse/tractusx/edc/edr/core/defaults/PersistentCacheEntryTest.java b/core/edr-cache-core/src/test/java/org/eclipse/tractusx/edc/edr/core/defaults/PersistentCacheEntryTest.java deleted file mode 100644 index 4ce68d560..000000000 --- a/core/edr-cache-core/src/test/java/org/eclipse/tractusx/edc/edr/core/defaults/PersistentCacheEntryTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.core.defaults; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.junit.jupiter.api.Test; - -import static java.util.UUID.randomUUID; -import static org.assertj.core.api.Assertions.assertThat; - -class PersistentCacheEntryTest { - - @Test - void verify_serializeDeserialize() throws JsonProcessingException { - var mapper = new ObjectMapper(); - - var edr = EndpointDataReference.Builder.newInstance() - .endpoint("http://test.com") - .id(randomUUID().toString()) - .authCode("11111") - .contractId("test-contract-id") - .authKey("authentication").build(); - - var edrEntry = EndpointDataReferenceEntry.Builder.newInstance() - .assetId(randomUUID().toString()) - .agreementId(randomUUID().toString()) - .transferProcessId(randomUUID().toString()) - .providerId(randomUUID().toString()) - .build(); - - var serialized = mapper.writeValueAsString(new PersistentCacheEntry(edrEntry, edr)); - - var deserialized = mapper.readValue(serialized, PersistentCacheEntry.class); - - assertThat(deserialized.getEdrEntry()).isNotNull(); - assertThat(deserialized.getEdr()).isNotNull(); - } - - -} diff --git a/core/edr-core/build.gradle.kts b/core/edr-core/build.gradle.kts index e575979c6..8335ef370 100644 --- a/core/edr-core/build.gradle.kts +++ b/core/edr-core/build.gradle.kts @@ -22,13 +22,11 @@ plugins { } dependencies { - implementation(libs.edc.spi.core) - implementation(libs.edc.config.filesystem) - implementation(libs.edc.util) - implementation(libs.edc.spi.contract) - implementation(libs.edc.spi.controlplane) - implementation(libs.edc.statemachine) + implementation(libs.edc.lib.util) + implementation(libs.edc.spi.edrstore) + implementation(libs.edc.spi.transactionspi) + implementation(project(":spi:tokenrefresh-spi")) implementation(project(":spi:edr-spi")) implementation(project(":spi:core-spi")) diff --git a/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCoreExtension.java b/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCoreExtension.java deleted file mode 100644 index f9fb137dd..000000000 --- a/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCoreExtension.java +++ /dev/null @@ -1,134 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.core; - -import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService; -import org.eclipse.edc.connector.spi.transferprocess.TransferProcessService; -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provides; -import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.retry.ExponentialWaitStrategy; -import org.eclipse.edc.spi.system.ExecutorInstrumentation; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.telemetry.Telemetry; -import org.eclipse.tractusx.edc.edr.core.manager.EdrManagerImpl; -import org.eclipse.tractusx.edc.edr.spi.EdrManager; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; - -import java.time.Clock; -import java.time.Duration; - -/** - * Registers default services for the EDR cache. - */ -@Provides(EdrManager.class) -@Extension(value = EdrCoreExtension.NAME) -public class EdrCoreExtension implements ServiceExtension { - public static final long DEFAULT_ITERATION_WAIT = 1000; - - public static final int DEFAULT_BATCH_SIZE = 20; - - public static final int DEFAULT_SEND_RETRY_LIMIT = 7; - - public static final long DEFAULT_SEND_RETRY_BASE_DELAY = 1000L; - public static final long DEFAULT_EXPIRING_DURATION = 60; - - public static final long DEFAULT_EXPIRED_RETENTION = 60; - - protected static final String NAME = "EDR Core"; - @Setting(value = "The iteration wait time in milliseconds in the edr state machine.", type = "long", defaultValue = "" + DEFAULT_ITERATION_WAIT) - private static final String EDR_STATE_MACHINE_ITERATION_WAIT_MILLIS = "edc.edr.state-machine.iteration-wait-millis"; - @Setting(value = "The batch size in the edr negotiation state machine.", type = "int", defaultValue = "" + DEFAULT_BATCH_SIZE) - private static final String EDR_STATE_MACHINE_BATCH_SIZE = "edc.edr.state-machine.batch-size"; - @Setting(value = "The minimum duration on which the EDR token can be eligible for renewal", type = "long", defaultValue = "" + DEFAULT_EXPIRING_DURATION) - private static final String EDR_STATE_MACHINE_EXPIRING_DURATION = "edc.edr.state-machine.expiring-duration"; - - @Setting(value = "The minimum duration on with the EDR token can be eligible for deletion when it's expired.", type = "long", defaultValue = "" + DEFAULT_EXPIRED_RETENTION) - private static final String EDR_STATE_MACHINE_EXPIRED_RETENTION = "edc.edr.state-machine.expired-retention"; - - @Inject - private Monitor monitor; - - @Inject - private ContractNegotiationService contractNegotiationService; - - @Inject - private TransferProcessService transferProcessService; - @Inject - private EndpointDataReferenceCache endpointDataReferenceCache; - - @Inject - private ExecutorInstrumentation executorInstrumentation; - - @Inject - private Telemetry telemetry; - - @Inject - private Clock clock; - private EdrManagerImpl edrManager; - - @Override - public String name() { - return NAME; - } - - @Override - public void initialize(ServiceExtensionContext context) { - - var iterationWaitMillis = context.getSetting(EDR_STATE_MACHINE_ITERATION_WAIT_MILLIS, DEFAULT_ITERATION_WAIT); - - var expiringDuration = context.getSetting(EDR_STATE_MACHINE_EXPIRING_DURATION, DEFAULT_EXPIRING_DURATION); - - var expiredRetention = context.getSetting(EDR_STATE_MACHINE_EXPIRED_RETENTION, DEFAULT_EXPIRED_RETENTION); - - - edrManager = EdrManagerImpl.Builder.newInstance() - .contractNegotiationService(contractNegotiationService) - .monitor(monitor) - .waitStrategy(new ExponentialWaitStrategy(iterationWaitMillis)) - .executorInstrumentation(executorInstrumentation) - .edrCache(endpointDataReferenceCache) - .transferProcessService(transferProcessService) - .telemetry(telemetry) - .batchSize(context.getSetting(EDR_STATE_MACHINE_BATCH_SIZE, DEFAULT_BATCH_SIZE)) - .expiringDuration(Duration.ofSeconds(expiringDuration)) - .expiredRetention(Duration.ofSeconds(expiredRetention)) - .clock(clock) - .build(); - - context.registerService(EdrManager.class, edrManager); - } - - @Override - public void start() { - edrManager.start(); - } - - @Override - public void shutdown() { - if (edrManager != null) { - edrManager.stop(); - } - } - -} diff --git a/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCoreServiceExtension.java b/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCoreServiceExtension.java index 012cd65b2..8c579a4cc 100644 --- a/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCoreServiceExtension.java +++ b/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCoreServiceExtension.java @@ -19,15 +19,16 @@ package org.eclipse.tractusx.edc.edr.core; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provider; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.transaction.spi.TransactionContext; import org.eclipse.tractusx.edc.edr.core.service.EdrServiceImpl; -import org.eclipse.tractusx.edc.edr.spi.EdrManager; import org.eclipse.tractusx.edc.edr.spi.service.EdrService; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.spi.tokenrefresh.common.TokenRefreshHandler; /** * Registers default services for the EDR cache. @@ -40,19 +41,21 @@ public class EdrCoreServiceExtension implements ServiceExtension { private Monitor monitor; @Inject - private EdrManager edrManager; + private EndpointDataReferenceStore edrStore; @Inject - private EndpointDataReferenceCache endpointDataReferenceCache; + private TokenRefreshHandler tokenRefreshHandler; + + @Inject + private TransactionContext transactionContext; @Override public String name() { return NAME; } - @Provider public EdrService edrService() { - return new EdrServiceImpl(edrManager, endpointDataReferenceCache); + return new EdrServiceImpl(edrStore, tokenRefreshHandler, transactionContext, monitor); } } diff --git a/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/manager/EdrManagerImpl.java b/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/manager/EdrManagerImpl.java deleted file mode 100644 index db89aaefe..000000000 --- a/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/manager/EdrManagerImpl.java +++ /dev/null @@ -1,385 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.core.manager; - -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest; -import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService; -import org.eclipse.edc.connector.spi.transferprocess.TransferProcessService; -import org.eclipse.edc.connector.transfer.spi.types.TransferRequest; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.query.Criterion; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.response.ResponseStatus; -import org.eclipse.edc.spi.response.StatusResult; -import org.eclipse.edc.spi.retry.ExponentialWaitStrategy; -import org.eclipse.edc.spi.retry.WaitStrategy; -import org.eclipse.edc.spi.system.ExecutorInstrumentation; -import org.eclipse.edc.spi.telemetry.Telemetry; -import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; -import org.eclipse.edc.statemachine.ProcessorImpl; -import org.eclipse.edc.statemachine.StateMachineManager; -import org.eclipse.edc.statemachine.retry.EntityRetryProcessConfiguration; -import org.eclipse.edc.statemachine.retry.EntityRetryProcessFactory; -import org.eclipse.tractusx.edc.edr.spi.EdrManager; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates; -import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; -import org.jetbrains.annotations.NotNull; - -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.time.ZoneOffset; -import java.util.Collection; -import java.util.Objects; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static java.lang.String.format; -import static org.eclipse.edc.spi.persistence.StateEntityStore.hasState; -import static org.eclipse.tractusx.edc.edr.core.EdrCoreExtension.DEFAULT_BATCH_SIZE; -import static org.eclipse.tractusx.edc.edr.core.EdrCoreExtension.DEFAULT_EXPIRED_RETENTION; -import static org.eclipse.tractusx.edc.edr.core.EdrCoreExtension.DEFAULT_EXPIRING_DURATION; -import static org.eclipse.tractusx.edc.edr.core.EdrCoreExtension.DEFAULT_ITERATION_WAIT; -import static org.eclipse.tractusx.edc.edr.core.EdrCoreExtension.DEFAULT_SEND_RETRY_BASE_DELAY; -import static org.eclipse.tractusx.edc.edr.core.EdrCoreExtension.DEFAULT_SEND_RETRY_LIMIT; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.DELETING; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.EXPIRED; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.NEGOTIATED; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.from; - -/** - * Manages the EDR negotiation and lifecycle. - **/ -public class EdrManagerImpl implements EdrManager { - - public static final String LOCAL_ADAPTER_URI = "local://adapter"; - public static final Set LOCAL_EVENTS = Set.of("contract.negotiation", "transfer.process"); - public static final CallbackAddress LOCAL_CALLBACK = CallbackAddress.Builder.newInstance() - .transactional(true) - .uri(LOCAL_ADAPTER_URI) - .events(LOCAL_EVENTS) - .build(); - protected Monitor monitor; - protected ExecutorInstrumentation executorInstrumentation = ExecutorInstrumentation.noop(); - protected WaitStrategy waitStrategy = () -> DEFAULT_ITERATION_WAIT; - - protected int batchSize = DEFAULT_BATCH_SIZE; - protected EntityRetryProcessFactory entityRetryProcessFactory; - protected EntityRetryProcessConfiguration entityRetryProcessConfiguration = defaultEntityRetryProcessConfiguration(); - private ContractNegotiationService contractNegotiationService; - - private TransferProcessService transferProcessService; - private StateMachineManager stateMachineManager; - private EndpointDataReferenceCache edrCache; - private Telemetry telemetry = new Telemetry(); - private Clock clock; - private Duration expiringDuration; - - private Duration expiredRetention; - - - private EdrManagerImpl() { - expiringDuration = Duration.ofSeconds(DEFAULT_EXPIRING_DURATION); - expiredRetention = Duration.ofSeconds(DEFAULT_EXPIRED_RETENTION); - } - - @Override - public StatusResult initiateEdrNegotiation(NegotiateEdrRequest request) { - var negotiation = contractNegotiationService.initiateNegotiation(createContractRequest(request)); - return StatusResult.success(negotiation); - } - - public void start() { - stateMachineManager = StateMachineManager.Builder.newInstance("edr-manager", monitor, executorInstrumentation, waitStrategy) - .processor(processEdrInState(NEGOTIATED, this::processNegotiated)) - .processor(processEdrInState(EXPIRED, this::processExpired)) - .processor(processDeletingEdr(this::processDeleting)) - .build(); - - stateMachineManager.start(); - } - - public void stop() { - if (stateMachineManager != null) { - stateMachineManager.stop(); - } - } - - protected void transitionToRefreshing(EndpointDataReferenceEntry edrEntry) { - edrEntry.transitionToRefreshing(); - update(edrEntry); - } - - protected void transitionToNegotiated(EndpointDataReferenceEntry edrEntry) { - edrEntry.transitionToNegotiated(); - update(edrEntry); - } - - protected void transitionToError(EndpointDataReferenceEntry edrEntry, String message) { - edrEntry.setErrorDetail(message); - edrEntry.transitionError(); - update(edrEntry); - } - - protected void transitionToDeleting(EndpointDataReferenceEntry edrEntry) { - edrEntry.transitionToDeleting(); - update(edrEntry); - } - - private void update(EndpointDataReferenceEntry edrEntry) { - edrCache.update(edrEntry); - monitor.debug(format("Edr entry %s is now in state %s.", edrEntry.getId(), from(edrEntry.getState()))); - } - - - private ProcessorImpl processEdrInState(EndpointDataReferenceEntryStates state, Function function) { - var filter = new Criterion[]{ hasState(state.code()) }; - return processor(() -> edrCache.nextNotLeased(batchSize, filter), telemetry.contextPropagationMiddleware(function)); - } - - private ProcessorImpl processor(Supplier> o, Function telemetryPropagationFunction) { - return ProcessorImpl.Builder.newInstance(o) - .process(telemetryPropagationFunction) - .build(); - } - - - private ProcessorImpl processDeletingEdr(Function function) { - var query = QuerySpec.Builder.newInstance() - .filter(hasState(DELETING.code())) - .limit(batchSize) - .build(); - - return processor(() -> edrCache.queryForEntries(query).collect(Collectors.toList()), telemetry.contextPropagationMiddleware(function)); - } - - private ContractRequest createContractRequest(NegotiateEdrRequest request) { - var callbacks = Stream.concat(request.getCallbackAddresses().stream(), Stream.of(LOCAL_CALLBACK)).collect(Collectors.toList()); - - return ContractRequest.Builder.newInstance() - .counterPartyAddress(request.getConnectorAddress()) - .contractOffer(request.getOffer()) - .protocol(request.getProtocol()) - .providerId(request.getConnectorId()) - .callbackAddresses(callbacks).build(); - } - - private boolean processNegotiated(EndpointDataReferenceEntry edrEntry) { - if (isAboutToExpire(edrEntry)) { - return entityRetryProcessFactory.doSyncProcess(edrEntry, () -> fireTransferProcess(edrEntry)) - .onDelay(this::breakLease) - .onSuccess((n, result) -> transitionToRefreshing(n)) - .onFailure((n, throwable) -> transitionToNegotiated(n)) - .onFatalError((n, failure) -> transitionToError(n, failure.getFailureDetail())) - .onRetryExhausted((n, failure) -> transitionToError(n, format("Failed renew EDR token: %s", failure.getFailureDetail()))) - .execute("Start an EDR token renewal"); - } else { - breakLease(edrEntry); - return false; - } - } - - private boolean processExpired(EndpointDataReferenceEntry edrEntry) { - return entityRetryProcessFactory.doSyncProcess(edrEntry, () -> checkExpiration(edrEntry)) - .onDelay(this::breakLease) - .execute("Start EDR token deletion check"); - - } - - - private boolean processDeleting(EndpointDataReferenceEntry edrEntry) { - return entityRetryProcessFactory.doSyncProcess(edrEntry, () -> deleteEntry(edrEntry)) - .onDelay(this::breakLease) - .onSuccess((n, result) -> { - }) - .onFailure((n, throwable) -> transitionToDeleting(n)) - .onFatalError((n, failure) -> transitionToError(n, failure.getFailureDetail())) - .onRetryExhausted((n, failure) -> transitionToError(n, format("Failed deleted EDR token: %s", failure.getFailureDetail()))) - .execute("Start EDR token deletion"); - } - - private StatusResult checkExpiration(EndpointDataReferenceEntry entry) { - if (shouldBeRemoved(entry)) { - transitionToDeleting(entry); - return StatusResult.success(); - } else { - breakLease(entry); - return StatusResult.success(); - } - } - - private StatusResult deleteEntry(EndpointDataReferenceEntry entry) { - var result = edrCache.deleteByTransferProcessId(entry.getTransferProcessId()); - if (result.succeeded()) { - monitor.debug(format("Deleted EDR cached entry for transfer process id %s", entry.getTransferProcessId())); - return StatusResult.success(); - } else { - return StatusResult.failure(ResponseStatus.FATAL_ERROR, format("Failed to delete EDR for transfer process id %s, error: %s", entry.getTransferProcessId(), result.getFailureDetail())); - } - } - - private StatusResult fireTransferProcess(EndpointDataReferenceEntry entry) { - - var transferProcess = transferProcessService.findById(entry.getTransferProcessId()); - - if (transferProcess == null) { - return StatusResult.failure(ResponseStatus.FATAL_ERROR, format("Failed to find transfer process %s", entry.getTransferProcessId())); - } - var dataRequest = transferProcess.getDataRequest(); - - var transferRequest = TransferRequest.Builder.newInstance() - .assetId(dataRequest.getAssetId()) - .contractId(dataRequest.getContractId()) - .protocol(dataRequest.getProtocol()) - .counterPartyAddress(dataRequest.getConnectorAddress()) - .dataDestination(dataRequest.getDataDestination()) - .callbackAddresses(transferProcess.getCallbackAddresses()) - .build(); - - var result = transferProcessService.initiateTransfer(transferRequest); - if (result.failed()) { - var msg = format("Failed to initiate a transfer for contract %s and asset %s, error: %s", dataRequest.getContractId(), dataRequest.getAssetId(), result.getFailureDetail()); - monitor.severe(msg); - return StatusResult.failure(ResponseStatus.ERROR_RETRY, result.getFailureDetail()); - } - monitor.debug(format("Transfer with id %s initiated", result.getContent())); - return StatusResult.success(); - } - - @NotNull - private EntityRetryProcessConfiguration defaultEntityRetryProcessConfiguration() { - return new EntityRetryProcessConfiguration(DEFAULT_SEND_RETRY_LIMIT, () -> new ExponentialWaitStrategy(DEFAULT_SEND_RETRY_BASE_DELAY)); - } - - private boolean isAboutToExpire(EndpointDataReferenceEntry entry) { - if (entry.getExpirationTimestamp() == null) { - return false; - } - var expiration = Instant.ofEpochMilli(entry.getExpirationTimestamp()).atOffset(ZoneOffset.UTC).toInstant(); - var now = clock.instant().atOffset(ZoneOffset.UTC).toInstant(); - var duration = Duration.between(now, expiration); - return expiringDuration.compareTo(duration) > 0; - } - - private boolean shouldBeRemoved(EndpointDataReferenceEntry entry) { - if (entry.getExpirationTimestamp() == null) { - return false; - } - var expiration = Instant.ofEpochMilli(entry.getExpirationTimestamp()).atOffset(ZoneOffset.UTC).toInstant(); - var now = clock.instant().atOffset(ZoneOffset.UTC).toInstant(); - var duration = Duration.between(now, expiration).abs(); - return expiredRetention.compareTo(duration) <= 0; - } - - private void breakLease(EndpointDataReferenceEntry edrEntry) { - edrCache.update(edrEntry); - } - - public static class Builder { - - private final EdrManagerImpl edrManager; - - private Builder() { - edrManager = new EdrManagerImpl(); - } - - public static Builder newInstance() { - return new Builder(); - } - - public Builder contractNegotiationService(ContractNegotiationService negotiationService) { - edrManager.contractNegotiationService = negotiationService; - return this; - } - - public Builder transferProcessService(TransferProcessService transferProcessService) { - edrManager.transferProcessService = transferProcessService; - return this; - } - - public Builder monitor(Monitor monitor) { - edrManager.monitor = monitor; - return this; - } - - public Builder waitStrategy(WaitStrategy waitStrategy) { - edrManager.waitStrategy = waitStrategy; - return this; - } - - public Builder executorInstrumentation(ExecutorInstrumentation executorInstrumentation) { - edrManager.executorInstrumentation = executorInstrumentation; - return this; - } - - public Builder telemetry(Telemetry telemetry) { - edrManager.telemetry = telemetry; - return this; - } - - public Builder clock(Clock clock) { - edrManager.clock = clock; - return this; - } - - public Builder expiringDuration(Duration duration) { - edrManager.expiringDuration = duration; - return this; - } - - public Builder expiredRetention(Duration duration) { - edrManager.expiredRetention = duration; - return this; - } - - public Builder edrCache(EndpointDataReferenceCache edrCache) { - edrManager.edrCache = edrCache; - return this; - } - - public Builder batchSize(int batchSize) { - edrManager.batchSize = batchSize; - return this; - } - - public EdrManagerImpl build() { - Objects.requireNonNull(edrManager.contractNegotiationService); - Objects.requireNonNull(edrManager.monitor); - Objects.requireNonNull(edrManager.waitStrategy); - Objects.requireNonNull(edrManager.executorInstrumentation); - Objects.requireNonNull(edrManager.edrCache); - Objects.requireNonNull(edrManager.telemetry); - Objects.requireNonNull(edrManager.transferProcessService); - Objects.requireNonNull(edrManager.clock); - Objects.requireNonNull(edrManager.expiringDuration); - Objects.requireNonNull(edrManager.expiredRetention); - - edrManager.entityRetryProcessFactory = new EntityRetryProcessFactory(edrManager.monitor, edrManager.clock, edrManager.entityRetryProcessConfiguration); - - return edrManager; - } - } -} diff --git a/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImpl.java b/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImpl.java index 968611e59..17eb5adf6 100644 --- a/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImpl.java +++ b/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImpl.java @@ -19,61 +19,99 @@ package org.eclipse.tractusx.edc.edr.core.service; -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.result.ServiceResult; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.spi.EdrManager; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.transaction.spi.TransactionContext; import org.eclipse.tractusx.edc.edr.spi.service.EdrService; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; +import org.eclipse.tractusx.edc.edr.spi.types.RefreshMode; +import org.eclipse.tractusx.edc.spi.tokenrefresh.common.TokenRefreshHandler; +import java.time.Instant; import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import static java.lang.String.format; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_EXPIRES_IN; public class EdrServiceImpl implements EdrService { - private final EdrManager edrManager; + private final EndpointDataReferenceStore edrStore; + private final TokenRefreshHandler tokenRefreshHandler; + private final TransactionContext transactionContext; + private final Monitor monitor; - private final EndpointDataReferenceCache endpointDataReferenceCache; - - public EdrServiceImpl(EdrManager edrManager, EndpointDataReferenceCache endpointDataReferenceCache) { - this.edrManager = edrManager; - this.endpointDataReferenceCache = endpointDataReferenceCache; + public EdrServiceImpl(EndpointDataReferenceStore edrStore, TokenRefreshHandler tokenRefreshHandler, TransactionContext transactionContext, Monitor monitor) { + this.edrStore = edrStore; + this.tokenRefreshHandler = tokenRefreshHandler; + this.transactionContext = transactionContext; + this.monitor = monitor; } @Override - public ServiceResult initiateEdrNegotiation(NegotiateEdrRequest request) { - var contractNegotiation = edrManager.initiateEdrNegotiation(request); - if (contractNegotiation.succeeded()) { - return ServiceResult.success(contractNegotiation.getContent()); - } else { - return ServiceResult.badRequest(contractNegotiation.getFailureMessages()); - } + public ServiceResult resolveByTransferProcess(String transferProcessId, RefreshMode mode) { + return transactionContext.execute(() -> edrStore.resolveByTransferProcess(transferProcessId) + .flatMap(ServiceResult::from) + .compose(edr -> handleRefresh(transferProcessId, edr, mode))); + } @Override - public ServiceResult findByTransferProcessId(String transferProcessId) { - var edr = endpointDataReferenceCache.resolveReference(transferProcessId); - return Optional.ofNullable(edr) - .map(ServiceResult::success) - .orElse(ServiceResult.notFound(format("No Edr found associated to the transfer process with id: %s", transferProcessId))); + public ServiceResult> query(QuerySpec query) { + return transactionContext.execute(() -> edrStore.query(query).flatMap(ServiceResult::from)); } - @Override - public ServiceResult> findBy(QuerySpec querySpec) { - var results = endpointDataReferenceCache.queryForEntries(querySpec).collect(Collectors.toList()); - return ServiceResult.success(results); + private ServiceResult handleRefresh(String id, DataAddress edr, RefreshMode mode) { + return switch (mode) { + case NO_REFRESH -> ServiceResult.success(edr); + case AUTO_REFRESH, FORCE_REFRESH -> autoRefresh(id, edr, mode); + }; } - @Override - public ServiceResult deleteByTransferProcessId(String transferProcessId) { - var deleted = endpointDataReferenceCache.deleteByTransferProcessId(transferProcessId); - return ServiceResult.from(deleted); + private ServiceResult autoRefresh(String id, DataAddress edr, RefreshMode mode) { + var edrEntry = edrStore.findById(id); + if (edrEntry == null) { + return ServiceResult.notFound("An EndpointDataReferenceEntry with ID '%s' does not exist".formatted(id)); + } + if (isExpired(edr, edrEntry) || mode.equals(RefreshMode.FORCE_REFRESH)) { + monitor.debug("Token expired, need to refresh."); + return tokenRefreshHandler.refreshToken(id, edr) + .compose(updated -> updateEdr(edrEntry, updated)); + } + return ServiceResult.success(edr); + } + + private ServiceResult updateEdr(EndpointDataReferenceEntry entry, DataAddress dataAddress) { + var newEntry = EndpointDataReferenceEntry.Builder.newInstance() + .assetId(entry.getAssetId()) + .agreementId(entry.getAgreementId()) + .providerId(entry.getProviderId()) + .transferProcessId(entry.getTransferProcessId()) + .contractNegotiationId(entry.getContractNegotiationId()) + .agreementId(entry.getAgreementId()) + .build(); + + var updateResult = edrStore.save(newEntry, dataAddress); + + if (updateResult.failed()) { + return ServiceResult.fromFailure(updateResult); + } + return ServiceResult.success(dataAddress); + } + + private boolean isExpired(DataAddress edr, EndpointDataReferenceEntry metadata) { + var expiresInString = edr.getStringProperty(EDR_PROPERTY_EXPIRES_IN); + if (expiresInString == null) { + return false; + } + + var expiresIn = Long.parseLong(expiresInString); + // createdAt is in millis, expires-in is in seconds + var expiresAt = metadata.getCreatedAt() / 1000L + expiresIn; + var expiresAtInstant = Instant.ofEpochSecond(expiresAt); + + return expiresAtInstant.isBefore(Instant.now()); } } diff --git a/core/edr-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/core/edr-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index e5f833dc9..bddcb77ff 100644 --- a/core/edr-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/core/edr-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -17,5 +17,4 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# -org.eclipse.tractusx.edc.edr.core.EdrCoreExtension org.eclipse.tractusx.edc.edr.core.EdrCoreServiceExtension diff --git a/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/EdrCoreServiceExtensionTest.java b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/EdrCoreServiceExtensionTest.java index 9b416ad66..214289be5 100644 --- a/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/EdrCoreServiceExtensionTest.java +++ b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/EdrCoreServiceExtensionTest.java @@ -22,24 +22,14 @@ import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.tractusx.edc.edr.core.service.EdrServiceImpl; -import org.eclipse.tractusx.edc.edr.spi.EdrManager; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; @ExtendWith(DependencyInjectionExtension.class) public class EdrCoreServiceExtensionTest { - - @BeforeEach - void setUp(ServiceExtensionContext context) { - context.registerService(EdrManager.class, mock(EdrManager.class)); - context.registerService(EndpointDataReferenceCache.class, mock(EndpointDataReferenceCache.class)); - } - + @Test void shouldInitializeTheExtension(ServiceExtensionContext context, EdrCoreServiceExtension extension) { extension.initialize(context); diff --git a/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/fixtures/TestFunctions.java b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/fixtures/TestFunctions.java index 9e300bb22..76f959163 100644 --- a/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/fixtures/TestFunctions.java +++ b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/fixtures/TestFunctions.java @@ -19,37 +19,6 @@ package org.eclipse.tractusx.edc.edr.core.fixtures; -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; -import org.eclipse.edc.policy.model.Policy; -import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; -import org.eclipse.edc.spi.types.domain.offer.ContractOffer; -import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; - -import java.util.List; -import java.util.Set; - public class TestFunctions { - - - public static NegotiateEdrRequest getNegotiateEdrRequest() { - return NegotiateEdrRequest.Builder.newInstance() - .protocol("protocol") - .connectorAddress("http://test") - .callbackAddresses(List.of(CallbackAddress.Builder.newInstance().uri("test").events(Set.of("test")).build())) - .offer(ContractOffer.Builder.newInstance() - .id("id") - .assetId("assetId") - .policy(Policy.Builder.newInstance().build()) - .build()) - .build(); - } - - public static ContractNegotiation getContractNegotiation() { - return ContractNegotiation.Builder.newInstance() - .id("id") - .counterPartyAddress("http://test") - .counterPartyId("provider") - .protocol("protocol") - .build(); - } + } diff --git a/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/manager/EdrManagerImplTest.java b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/manager/EdrManagerImplTest.java deleted file mode 100644 index b781c299e..000000000 --- a/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/manager/EdrManagerImplTest.java +++ /dev/null @@ -1,283 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.core.manager; - -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest; -import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService; -import org.eclipse.edc.connector.spi.transferprocess.TransferProcessService; -import org.eclipse.edc.connector.transfer.spi.types.DataRequest; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionedResourceSet; -import org.eclipse.edc.connector.transfer.spi.types.TransferProcess; -import org.eclipse.edc.connector.transfer.spi.types.TransferProcessStates; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.query.Criterion; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.result.ServiceResult; -import org.eclipse.edc.spi.result.StoreResult; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.time.ZoneOffset; -import java.util.List; -import java.util.UUID; -import java.util.stream.Stream; - -import static java.util.Collections.emptyList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; -import static org.eclipse.edc.connector.transfer.spi.types.TransferProcess.Type.CONSUMER; -import static org.eclipse.edc.spi.persistence.StateEntityStore.hasState; -import static org.eclipse.tractusx.edc.edr.core.EdrCoreExtension.DEFAULT_BATCH_SIZE; -import static org.eclipse.tractusx.edc.edr.core.EdrCoreExtension.DEFAULT_EXPIRING_DURATION; -import static org.eclipse.tractusx.edc.edr.core.fixtures.TestFunctions.getContractNegotiation; -import static org.eclipse.tractusx.edc.edr.core.fixtures.TestFunctions.getNegotiateEdrRequest; -import static org.eclipse.tractusx.edc.edr.core.manager.EdrManagerImpl.LOCAL_CALLBACK; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.DELETING; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.ERROR; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.EXPIRED; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.NEGOTIATED; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.REFRESHING; -import static org.mockito.AdditionalMatchers.aryEq; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class EdrManagerImplTest { - - private final EndpointDataReferenceCache edrCache = mock(EndpointDataReferenceCache.class); - private final ContractNegotiationService negotiationService = mock(ContractNegotiationService.class); - private final TransferProcessService transferProcessService = mock(TransferProcessService.class); - private EdrManagerImpl edrManager; - - @BeforeEach - void setup() { - edrManager = EdrManagerImpl.Builder.newInstance() - .contractNegotiationService(negotiationService) - .transferProcessService(transferProcessService) - .edrCache(edrCache) - .monitor(mock(Monitor.class)) - .expiredRetention(Duration.ofSeconds(1)) - .clock(Clock.systemUTC()) - .build(); - } - - @Test - @DisplayName("Verify that EDR negotiation is initiated") - void initEdrNegotiation() { - - var captor = ArgumentCaptor.forClass(ContractRequest.class); - - when(negotiationService.initiateNegotiation(any())).thenReturn(getContractNegotiation()); - - var negotiateEdrRequest = getNegotiateEdrRequest(); - - var result = edrManager.initiateEdrNegotiation(negotiateEdrRequest); - - assertThat(result.succeeded()).isTrue(); - assertThat(result.getContent()).isNotNull(); - - verify(negotiationService).initiateNegotiation(captor.capture()); - - var msg = captor.getValue(); - - assertThat(msg.getCallbackAddresses()).usingRecursiveFieldByFieldElementComparator().containsAll(negotiateEdrRequest.getCallbackAddresses()); - assertThat(msg.getCallbackAddresses()).usingRecursiveFieldByFieldElementComparator().contains(LOCAL_CALLBACK); - assertThat(msg.getContractOffer()).usingRecursiveComparison().isEqualTo(negotiateEdrRequest.getOffer()); - assertThat(msg.getProtocol()).isEqualTo(negotiateEdrRequest.getProtocol()); - assertThat(msg.getCounterPartyAddress()).isEqualTo(negotiateEdrRequest.getConnectorAddress()); - - } - - @Test - @DisplayName("Verify that EDR state should transition to REFRESHING") - void initial_shouldTransitionRequesting() { - var edrEntry = edrEntryBuilder().state(NEGOTIATED.code()).build(); - var transferProcess = createTransferProcessBuilder().build(); - when(edrCache.nextNotLeased(anyInt(), stateIs(NEGOTIATED.code()))).thenReturn(List.of(edrEntry)).thenReturn(emptyList()); - when(edrCache.findByIdAndLease(edrEntry.getTransferProcessId())).thenReturn(StoreResult.success(edrEntry)); - when(transferProcessService.findById(edrEntry.getTransferProcessId())).thenReturn(transferProcess); - when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(transferProcess)); - - edrManager.start(); - - await().untilAsserted(() -> verify(edrCache).update(argThat(p -> p.getState() == REFRESHING.code()))); - } - - @Test - @DisplayName("Verify that EDR state should not transition to REFRESHING when the token it's not expired") - void initial_shouldNotTransitionToRefreshing_WhenNotExpired() { - var expiration = Instant.now().atOffset(ZoneOffset.UTC).toInstant().plusSeconds(DEFAULT_EXPIRING_DURATION + 10); - var edrEntry = edrEntryBuilder().expirationTimestamp(expiration.toEpochMilli()).state(NEGOTIATED.code()).build(); - var transferProcess = createTransferProcessBuilder().build(); - when(edrCache.nextNotLeased(anyInt(), stateIs(NEGOTIATED.code()))) - .thenReturn(List.of(edrEntry)) - .thenReturn(List.of(edrEntry)) - .thenReturn(emptyList()); - - when(edrCache.findByIdAndLease(edrEntry.getTransferProcessId())).thenReturn(StoreResult.success(edrEntry)); - when(transferProcessService.findById(edrEntry.getTransferProcessId())).thenReturn(transferProcess); - when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(transferProcess)); - - edrManager.start(); - - await().untilAsserted(() -> { - verify(edrCache, atLeast(2)).nextNotLeased(anyInt(), stateIs(NEGOTIATED.code())); - verify(edrCache, times(0)).update(argThat(p -> p.getState() == REFRESHING.code())); - }); - } - - - @Test - @DisplayName("Verify that EDR state should transition to ERROR the transfer process is not found") - void initial_shouldTransitionError_whenTransferProcessNotFound() { - var edrEntry = edrEntryBuilder().state(NEGOTIATED.code()).build(); - when(edrCache.nextNotLeased(anyInt(), stateIs(NEGOTIATED.code()))) - .thenReturn(List.of(edrEntry)) - .thenReturn(emptyList()); - - when(edrCache.findByIdAndLease(edrEntry.getTransferProcessId())).thenReturn(StoreResult.success(edrEntry)); - when(transferProcessService.findById(edrEntry.getTransferProcessId())).thenReturn(null); - - edrManager.start(); - - await().untilAsserted(() -> verify(edrCache).update(argThat(p -> p.getState() == ERROR.code()))); - } - - - @Test - @DisplayName("Verify that EDR state should not transition to ERROR on transient errors") - void initial_shouldNotTransitionError_whenInitiatedTransferFailsOnce() { - var edrEntry = edrEntryBuilder().state(NEGOTIATED.code()).build(); - var transferProcess = createTransferProcessBuilder().build(); - - when(edrCache.nextNotLeased(anyInt(), stateIs(NEGOTIATED.code()))) - .thenReturn(List.of(edrEntry)) - .thenReturn(List.of(edrEntry.copy())) - .thenReturn(emptyList()); - - when(edrCache.findByIdAndLease(edrEntry.getTransferProcessId())).thenReturn(StoreResult.success(edrEntry)); - when(transferProcessService.findById(edrEntry.getTransferProcessId())).thenReturn(transferProcess); - when(transferProcessService.initiateTransfer(any())) - .thenReturn(ServiceResult.badRequest("bad")) - .thenReturn(ServiceResult.success(transferProcess)); - - - edrManager.start(); - - await().untilAsserted(() -> { - var captor = ArgumentCaptor.forClass(EndpointDataReferenceEntry.class); - verify(edrCache, times(2)).update(captor.capture()); - var states = captor.getAllValues().stream().map(EndpointDataReferenceEntry::getState).toList(); - assertThat(states).containsExactly(NEGOTIATED.code(), REFRESHING.code()); - }); - } - - @Test - @DisplayName("Verify that EDR state should transition to deleting when the retention period is over") - void initial_shouldTransitionToDeleting_whenTheRetentionPeriodIsOver() { - var expiration = Instant.now().atOffset(ZoneOffset.UTC).toInstant().minusSeconds(DEFAULT_EXPIRING_DURATION + 10); - var edrEntry = edrEntryBuilder().state(EXPIRED.code()).expirationTimestamp(expiration.toEpochMilli()).build(); - - when(edrCache.nextNotLeased(anyInt(), stateIs(EXPIRED.code()))) - .thenReturn(List.of(edrEntry)) - .thenReturn(emptyList()); - - edrManager.start(); - - await().untilAsserted(() -> verify(edrCache).update(argThat(p -> p.getState() == DELETING.code()))); - } - - @Test - @DisplayName("Verify that EDR is deleted when state is DELETING") - void initial_shouldDeleteTheEntry_whenTheRetentionPeriodIsOver() { - var expiration = Instant.now().atOffset(ZoneOffset.UTC).toInstant().minusSeconds(DEFAULT_EXPIRING_DURATION + 10); - var edrEntry = edrEntryBuilder().state(DELETING.code()).expirationTimestamp(expiration.toEpochMilli()).build(); - - var query = QuerySpec.Builder.newInstance() - .filter(hasState(DELETING.code())) - .limit(DEFAULT_BATCH_SIZE) - .build(); - - when(edrCache.queryForEntries(query)) - .thenReturn(Stream.of(edrEntry)) - .thenReturn(Stream.empty()); - - - when(edrCache.deleteByTransferProcessId(edrEntry.getTransferProcessId())).thenReturn(StoreResult.success()); - - edrManager.start(); - - await().untilAsserted(() -> { - verify(edrCache, times(1)).deleteByTransferProcessId(edrEntry.getTransferProcessId()); - }); - } - - - private EndpointDataReferenceEntry.Builder edrEntryBuilder() { - return EndpointDataReferenceEntry.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .assetId(UUID.randomUUID().toString()) - .agreementId(UUID.randomUUID().toString()) - .transferProcessId(UUID.randomUUID().toString()) - .expirationTimestamp(Instant.now().toEpochMilli()) - .stateTimestamp(Instant.now().toEpochMilli()); - } - - private TransferProcess.Builder createTransferProcessBuilder() { - var processId = UUID.randomUUID().toString(); - var dataRequest = createDataRequestBuilder() - .processId(processId) - .protocol("protocol") - .connectorAddress("http://an/address") - .build(); - - return TransferProcess.Builder.newInstance() - .provisionedResourceSet(ProvisionedResourceSet.Builder.newInstance().build()) - .type(CONSUMER) - .id("test-process-" + processId) - .state(TransferProcessStates.COMPLETED.code()) - .dataRequest(dataRequest); - } - - private DataRequest.Builder createDataRequestBuilder() { - return DataRequest.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .contractId(UUID.randomUUID().toString()) - .assetId(UUID.randomUUID().toString()) - .destinationType("test-type"); - } - - private Criterion[] stateIs(int state) { - return aryEq(new Criterion[] {hasState(state)}); - } - -} diff --git a/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImplTest.java b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImplTest.java index d19b5e180..a99877af6 100644 --- a/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImplTest.java +++ b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImplTest.java @@ -19,157 +19,162 @@ package org.eclipse.tractusx.edc.edr.core.service; -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; -import org.eclipse.edc.policy.model.Policy; +import org.assertj.core.api.Assertions; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.response.StatusResult; -import org.eclipse.edc.spi.result.ServiceFailure; import org.eclipse.edc.spi.result.ServiceResult; import org.eclipse.edc.spi.result.StoreResult; -import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.spi.types.domain.offer.ContractOffer; -import org.eclipse.tractusx.edc.edr.spi.EdrManager; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.transaction.spi.NoopTransactionContext; +import org.eclipse.tractusx.edc.spi.tokenrefresh.common.TokenRefreshHandler; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import java.util.List; -import java.util.Set; -import java.util.stream.Stream; +import java.util.UUID; -import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_AUTH_NS; +import static org.eclipse.tractusx.edc.edr.spi.types.RefreshMode.AUTO_REFRESH; +import static org.eclipse.tractusx.edc.edr.spi.types.RefreshMode.FORCE_REFRESH; +import static org.eclipse.tractusx.edc.edr.spi.types.RefreshMode.NO_REFRESH; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; public class EdrServiceImplTest { - EdrManager edrManager = mock(EdrManager.class); - - EndpointDataReferenceCache endpointDataReferenceCache = mock(EndpointDataReferenceCache.class); - - EdrServiceImpl transferService; + private final TokenRefreshHandler tokenRefreshHandler = mock(); + private final EndpointDataReferenceStore edrStore = mock(); + private EdrServiceImpl edrService; @BeforeEach void setup() { - transferService = new EdrServiceImpl(edrManager, endpointDataReferenceCache); + edrService = new EdrServiceImpl(edrStore, tokenRefreshHandler, new NoopTransactionContext(), mock()); + } + + @Test + void query() { + + when(edrStore.query(any())).thenReturn(StoreResult.success(List.of())); + + assertThat(edrService.query(QuerySpec.max())).isSucceeded().satisfies(results -> { + Assertions.assertThat(results).isEmpty(); + }); } @Test - void initEdrNegotiation_shouldFireContractNegotiation_WhenUsingCallbacks() { + void resolveByTransferProcess_whenNoRefresh() { - when(edrManager.initiateEdrNegotiation(any())).thenReturn(StatusResult.success(getContractNegotiation())); + var transferProcess = "tp"; + when(edrStore.resolveByTransferProcess(transferProcess)).thenReturn(StoreResult.success(edr())); - var negotiateEdrRequest = getNegotiateEdrRequest(); + var result = edrService.resolveByTransferProcess(transferProcess, NO_REFRESH); - var result = transferService.initiateEdrNegotiation(negotiateEdrRequest); + assertThat(result).isSucceeded(); - assertThat(result.succeeded()).isTrue(); - assertThat(result.getContent()).isNotNull(); + verify(edrStore).resolveByTransferProcess(transferProcess); + verifyNoMoreInteractions(edrStore); + verifyNoInteractions(tokenRefreshHandler); } @Test - void findByTransferProcessId_shouldReturnTheEdr_whenFoundInCache() { + void resolveByTransferProcess_whenRefreshNotExpired() { - var transferProcessId = "tpId"; + var transferProcess = "tp"; + var assetId = "assetId"; + when(edrStore.resolveByTransferProcess(transferProcess)).thenReturn(StoreResult.success(edr("1000"))); + when(edrStore.findById(transferProcess)).thenReturn(edrEntry(assetId, transferProcess)); - when(endpointDataReferenceCache.resolveReference(eq(transferProcessId))) - .thenReturn(EndpointDataReference.Builder.newInstance() - .id("test-id") - .contractId("test-contract") - .endpoint("test") - .build()); + var result = edrService.resolveByTransferProcess(transferProcess, AUTO_REFRESH); - var result = transferService.findByTransferProcessId(transferProcessId); + assertThat(result).isSucceeded(); - assertThat(result) - .isNotNull() - .extracting(ServiceResult::getContent) - .isNotNull(); - } + verify(edrStore).resolveByTransferProcess(transferProcess); + verify(edrStore).findById(transferProcess); + verifyNoMoreInteractions(edrStore); + verifyNoInteractions(tokenRefreshHandler); + } @Test - void findByTransferProcessId_shouldNotFound_whenNotPresentInCache() { - var transferProcessId = "tpId"; + void resolveByTransferProcess_whenRefreshExpired() { - when(endpointDataReferenceCache.resolveReference(transferProcessId)).thenReturn(null); + var transferProcess = "tp"; + var assetId = "assetId"; + var entry = edrEntry(assetId, transferProcess); + var refreshedEdr = edr(); - var result = transferService.findByTransferProcessId(transferProcessId); + when(edrStore.resolveByTransferProcess(transferProcess)).thenReturn(StoreResult.success(edr("-1000"))); + when(edrStore.findById(transferProcess)).thenReturn(entry); + when(tokenRefreshHandler.refreshToken(eq(transferProcess), any())).thenReturn(ServiceResult.success(refreshedEdr)); + when(edrStore.save(any(), eq(refreshedEdr))).thenReturn(StoreResult.success()); - assertThat(result) - .isNotNull() - .extracting(ServiceResult::getFailure) - .extracting(ServiceFailure::getReason) - .isEqualTo(ServiceFailure.Reason.NOT_FOUND); - } + var result = edrService.resolveByTransferProcess(transferProcess, AUTO_REFRESH); - @Test - void deleteByTransferProcessId() { - var transferProcessId = "tpId"; + assertThat(result).isSucceeded(); - when(endpointDataReferenceCache.deleteByTransferProcessId(transferProcessId)).thenReturn(StoreResult.success(null)); + var captor = ArgumentCaptor.forClass(EndpointDataReferenceEntry.class); + verify(edrStore).resolveByTransferProcess(transferProcess); + verify(edrStore).findById(transferProcess); + verify(edrStore).save(captor.capture(), eq(refreshedEdr)); + verify(tokenRefreshHandler).refreshToken(eq(transferProcess), any()); - var result = transferService.deleteByTransferProcessId(transferProcessId); + verifyNoMoreInteractions(edrStore, tokenRefreshHandler); - assertThat(result) - .isNotNull() - .extracting(ServiceResult::succeeded) - .isEqualTo(true); + Assertions.assertThat(captor.getValue()).usingRecursiveComparison().ignoringFields("createdAt").isEqualTo(entry); } @Test - void deleteByTransferProcessId_shouldNotFound_whenNotPresentInCache() { - var transferProcessId = "tpId"; + void resolveByTransferProcess_forceRefresh() { - when(endpointDataReferenceCache.deleteByTransferProcessId(eq(transferProcessId))).thenReturn(StoreResult.notFound("")); + var transferProcess = "tp"; + var assetId = "assetId"; + var entry = edrEntry(assetId, transferProcess); + var refreshedEdr = edr(); + when(edrStore.resolveByTransferProcess(transferProcess)).thenReturn(StoreResult.success(edr("1000"))); + when(edrStore.findById(transferProcess)).thenReturn(entry); + when(tokenRefreshHandler.refreshToken(eq(transferProcess), any())).thenReturn(ServiceResult.success(refreshedEdr)); + when(edrStore.save(any(), eq(refreshedEdr))).thenReturn(StoreResult.success()); - var result = transferService.deleteByTransferProcessId(transferProcessId); + var result = edrService.resolveByTransferProcess(transferProcess, FORCE_REFRESH); - assertThat(result) - .isNotNull() - .extracting(ServiceResult::getFailure) - .extracting(ServiceFailure::getReason) - .isEqualTo(ServiceFailure.Reason.NOT_FOUND); - } + assertThat(result).isSucceeded(); - @Test - void queryEdrs() { - when(endpointDataReferenceCache.queryForEntries(any())).thenReturn(Stream.empty()); + var captor = ArgumentCaptor.forClass(EndpointDataReferenceEntry.class); + verify(edrStore).resolveByTransferProcess(transferProcess); + verify(edrStore).findById(transferProcess); + verify(edrStore).save(captor.capture(), eq(refreshedEdr)); + verify(tokenRefreshHandler).refreshToken(eq(transferProcess), any()); - var result = transferService.findBy(QuerySpec.Builder.newInstance().build()); + verifyNoMoreInteractions(edrStore, tokenRefreshHandler); - assertThat(result) - .isNotNull() - .extracting(ServiceResult::getContent) - .extracting(List::size) - .isEqualTo(0); + Assertions.assertThat(captor.getValue()).usingRecursiveComparison().ignoringFields("createdAt").isEqualTo(entry); } - private NegotiateEdrRequest getNegotiateEdrRequest() { - return NegotiateEdrRequest.Builder.newInstance() - .protocol("protocol") - .connectorAddress("http://test") - .callbackAddresses(List.of(CallbackAddress.Builder.newInstance().uri("test").events(Set.of("test")).build())) - .offer(ContractOffer.Builder.newInstance() - .id("id") - .assetId("assetId") - .policy(Policy.Builder.newInstance().build()) - .build()) - .build(); + private DataAddress edr(String expireIn) { + return DataAddress.Builder.newInstance().type("test").property(TX_AUTH_NS + "expiresIn", expireIn).build(); + } + + private DataAddress edr() { + return edr(null); } - private ContractNegotiation getContractNegotiation() { - return ContractNegotiation.Builder.newInstance() - .id("id") - .counterPartyAddress("http://test") - .counterPartyId("provider") - .protocol("protocol") + private EndpointDataReferenceEntry edrEntry(String assetId, String transferProcessId) { + return EndpointDataReferenceEntry.Builder.newInstance() + .assetId(assetId) + .transferProcessId(transferProcessId) + .contractNegotiationId(UUID.randomUUID().toString()) + .agreementId(UUID.randomUUID().toString()) + .providerId(UUID.randomUUID().toString()) .build(); } } diff --git a/core/json-ld-core/src/main/java/org/eclipse/tractusx/edc/jsonld/JsonLdExtension.java b/core/json-ld-core/src/main/java/org/eclipse/tractusx/edc/jsonld/JsonLdExtension.java index a4b9e4038..43cdefc81 100644 --- a/core/json-ld-core/src/main/java/org/eclipse/tractusx/edc/jsonld/JsonLdExtension.java +++ b/core/json-ld-core/src/main/java/org/eclipse/tractusx/edc/jsonld/JsonLdExtension.java @@ -35,6 +35,8 @@ import static java.lang.String.format; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDC_CONTEXT; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_AUTH_NS; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_AUTH_PREFIX; import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_CONTEXT; import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_PREFIX; @@ -66,6 +68,7 @@ public class JsonLdExtension implements ServiceExtension { @Override public void initialize(ServiceExtensionContext context) { jsonLdService.registerNamespace(TX_PREFIX, TX_NAMESPACE); + jsonLdService.registerNamespace(TX_AUTH_PREFIX, TX_AUTH_NS); FILES.entrySet().stream().map(this::mapToFile) .forEach(result -> result.onSuccess(entry -> jsonLdService.registerCachedDocument(entry.getKey(), entry.getValue().toURI())) .onFailure(failure -> monitor.warning("Failed to register cached json-ld document: " + failure.getFailureDetail()))); diff --git a/core/json-ld-core/src/main/resources/document/dspace.jsonld b/core/json-ld-core/src/main/resources/document/dspace.jsonld new file mode 100644 index 000000000..f475b2350 --- /dev/null +++ b/core/json-ld-core/src/main/resources/document/dspace.jsonld @@ -0,0 +1,62 @@ +{ + "@context": { + "odrl": "http://www.w3.org/ns/odrl/2/", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "cred": "https://www.w3.org/2018/credentials#", + "sec": "https://w3id.org/security#", + "foaf": "http://xmlns.com/foaf/0.1/", + "cc": "http://creativecommons.org/ns#", + "dct": "http://purl.org/dc/terms/", + "dcat": "http://www.w3.org/ns/dcat#", + "dspace": "https://w3id.org/dspace/2024/1/", + + "dct:title": { "@language": "en" }, + "dct:creator": { "@type": "@id" }, + "dct:description": { "@container": "@set" }, + "dct:issued": { "@type": "xsd:dateTime" }, + "dct:modified": { "@type": "xsd:dateTime" }, + + "dcat:byteSize": { "@type": "xsd:decimal" }, + "dcat:distribution": { "@container": "@set" }, + "dcat:theme": { "@type": "@id" }, + "dcat:conformsTo": { "@type": "@id" }, + "dcat:dataset": { "@container": "@set" }, + "dcat:endpointURL": { "@type": "xsd:anyURI" }, + "dcat:endpointDescription": { "@type": "xsd:anyURI" }, + "dcat:keyword": { "@container": "@set" }, + "dcat:servesDataset": {"@container": "@set" }, + "dcat:service": { "@container": "@set" }, + "dcat:accessService": { "@container": "@set" }, + + "dspace:agreementId": { "@type": "@id" }, + "dspace:dataset": { "@type": "@id" }, + "dspace:transportType": { "@type": "@id" }, + "dspace:state": { "@type": "@id" }, + "dspace:providerId": { "@type": "@id" }, + "dspace:consumerId": { "@type": "@id" }, + "dspace:participantId": { "@type": "@id" }, + "dspace:reason": { "@container": "@set" }, + "dspace:catalog": { "@container": "@set" }, + "dspace:filter": { "@container": "@set" }, + "dspace:timestamp": { "@type": "xsd:dateTime" }, + "dspace:callbackAddress": { "@type": "xsd:anyURI" }, + "dspace:endpointProperties": { "@container": "@set" }, + + "foaf:homepage": { "@type": "xsd:anyURI" }, + + "odrl:hasPolicy": { "@container": "@set" }, + "odrl:permission": { "@container": "@set" }, + "odrl:prohibition": { "@container": "@set" }, + "odrl:obligation": { "@container": "@set" }, + "odrl:duty": { "@container": "@set" }, + "odrl:constraint": { "@container": "@set" }, + "odrl:action": { "@type": "@id" }, + "odrl:target": { "@type": "@id" }, + "odrl:leftOperand": { "@type": "@id" }, + "odrl:operator": { "@type": "@id" }, + "odrl:rightOperandReference": { "@type": "@id" }, + "odrl:profile": { "@container": "@set" } + "odrl:assigner": { "@type": "@id" }, + "odrl:assignee": { "@type": "@id" } + } +} diff --git a/dast/docker-compose.yaml b/dast/docker-compose.yaml new file mode 100644 index 000000000..e8b3ddef7 --- /dev/null +++ b/dast/docker-compose.yaml @@ -0,0 +1,50 @@ +################################################################################# +# 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 +################################################################################# + + +version: '3' + +services: + edc-runtime: + image: edc-runtime-memory:latest + environment: + TX_SSI_OAUTH_TOKEN_URL: http://keycloak:8080/realms/miw_test/protocol/openid-connect/token + TX_SSI_OAUTH_CLIENT_ID: miw_private_client + TX_SSI_OAUTH_CLIENT_SECRET_ALIAS: client-alias + EDC_VAULT_SECRETS: "client-alias:miw_private_client" + TX_SSI_MIW_URL: http://miw:8000 + TX_SSI_MIW_AUTHORITY_ID: BPNL000000000000 + TX_SSI_MIW_AUTHORITY_ISSUER: did:web:localhost%3A8000:BPNL000000000000 + TX_SSI_ENDPOINT_AUDIENCE: "http://test" + EDC_DATAPLANE_TOKEN_VALIDATION_ENDPOINT: "http://validate" + EDC_API_AUTH_KEY: password + + networks: + - miw-net + ports: + - "8282:8282" + - "8181:8181" + +volumes: + postgres_data: + driver: local + +networks: + miw-net: + external: true diff --git a/dast/fetch-token.sh b/dast/fetch-token.sh new file mode 100755 index 000000000..1ca198dab --- /dev/null +++ b/dast/fetch-token.sh @@ -0,0 +1,37 @@ +################################################################################# +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# +# 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 +################################################################################# + +#!/bin/bash + +response=$(curl -X POST -d 'client_id=miw_private_client&grant_type=client_credentials&client_secret=miw_private_client&scope=openid' http://localhost:8080/realms/miw_test/protocol/openid-connect/token) +token=$(echo "$response" | jq -r '.access_token') + +credentials=$(curl --url 'http://localhost:8000/api/credentials?type=SummaryCredential' --header "Authorization: Bearer $token" --header 'Content-Type: application/json' | jq -r '.content') + + +vp_token=$(curl --request POST \ + --url 'http://localhost:8000/api/presentations?asJwt=true&audience=http://test' \ + --header "Authorization: Bearer $token" \ + --header 'Content-Type: application/json' \ + --data "{ \"verifiableCredentials\": $credentials }" \ + | jq -r '.vp') + +echo "VP_TOKEN=$vp_token" >> "$GITHUB_ENV" + + diff --git a/docs/development/dataplane-signaling/AutomaticRefresh.drawio b/docs/development/dataplane-signaling/AutomaticRefresh.drawio new file mode 100644 index 000000000..2c71d36ee --- /dev/null +++ b/docs/development/dataplane-signaling/AutomaticRefresh.drawio @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/development/dataplane-signaling/AutomaticRefresh.drawio.png b/docs/development/dataplane-signaling/AutomaticRefresh.drawio.png new file mode 100644 index 000000000..4cb9e69cd Binary files /dev/null and b/docs/development/dataplane-signaling/AutomaticRefresh.drawio.png differ diff --git a/docs/development/dataplane-signaling/AutomaticRefreshEdrApi.drawio b/docs/development/dataplane-signaling/AutomaticRefreshEdrApi.drawio new file mode 100644 index 000000000..7d9697e53 --- /dev/null +++ b/docs/development/dataplane-signaling/AutomaticRefreshEdrApi.drawio @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/development/dataplane-signaling/AutomaticRefreshEdrApi.drawio.png b/docs/development/dataplane-signaling/AutomaticRefreshEdrApi.drawio.png new file mode 100644 index 000000000..fc68d7e81 Binary files /dev/null and b/docs/development/dataplane-signaling/AutomaticRefreshEdrApi.drawio.png differ diff --git a/docs/development/dataplane-signaling/ManualRefresh.drawio b/docs/development/dataplane-signaling/ManualRefresh.drawio new file mode 100644 index 000000000..9f5bfc112 --- /dev/null +++ b/docs/development/dataplane-signaling/ManualRefresh.drawio @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/development/dataplane-signaling/ManualRefresh.drawio.png b/docs/development/dataplane-signaling/ManualRefresh.drawio.png new file mode 100644 index 000000000..b0ff1db7f Binary files /dev/null and b/docs/development/dataplane-signaling/ManualRefresh.drawio.png differ diff --git a/docs/development/dataplane-signaling/TxSignaling.drawio b/docs/development/dataplane-signaling/TxSignaling.drawio new file mode 100644 index 000000000..0bcc372a4 --- /dev/null +++ b/docs/development/dataplane-signaling/TxSignaling.drawio @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/development/dataplane-signaling/TxSignaling.drawio.png b/docs/development/dataplane-signaling/TxSignaling.drawio.png new file mode 100644 index 000000000..338559c31 Binary files /dev/null and b/docs/development/dataplane-signaling/TxSignaling.drawio.png differ diff --git a/docs/development/dataplane-signaling/tx-signaling.extensions.md b/docs/development/dataplane-signaling/tx-signaling.extensions.md new file mode 100644 index 000000000..71f898a05 --- /dev/null +++ b/docs/development/dataplane-signaling/tx-signaling.extensions.md @@ -0,0 +1,108 @@ +# Tractus-X EDC Extensions for DataPlane Signaling Token Refresh + +This document details the architecture and implementation of the token refresh mechanism +that extends the DataPlane Signaling framework. + +## Overview + +The provider DataPlane exposes a new public facing API called the "Refresh API". Its purpose is to accept a refresh +token and an authentication token (see documentation [here]()), perform validity checks and then respond with a new +refresh token similar to this: + +```json +{ + "access_token": "BWjcyMzY3ZDhiNmJkNTY...", + "refresh_token": "Srq2NjM5NzA2OWJjuE7c...", + "token_type": "Bearer", + "expires": 3600 +} +``` + +A complete sequence including `TransferRequestMessage` is shown here: + +- `(1)`Consumer send `TransferRequestMessage` +- `(2)` Provider sends `DataFlowStartMessage` to its own DataPlane via the Signaling API +- `(3)` Provider's DataPlane creates an `EndPointDataReference` ( + see [here](https://github.com/eclipse-edc/Connector/blob/main/docs/developer/data-plane-signaling/data-plane-signaling-token-handling.md#2-updates-to-thedataaddress-format) + for an example) +- `(4)` Provider's ControlPlane receives `EndpointDataReference` (= EDR) +- `(5)` Provider sends `TransferStartMessage` that contains the `EndpointDataReference` to the Consumer via DSP. + Consumer stores EDR for subsequent use. +- `(6)` Consumer makes data requests against the Provider's public API (= data transfer). Those requests must carry the + `token` from the EDR in the authorization header. +- `(7)` Provider authorizes the data request + +_TOKEN EXPIRES_ + +- `(8)` The `TokenRefreshHandler` module creates the `authentication_token` (see [documentation]()) +- `(9)` The `TokenRefreshHandler` module sends token refresh request to provider's public Refresh API +- `(10)` Provider performs authentication checks, creates a new `access_token` and a new `refresh_token`, updates + internal records and sends the response back. + +![](./TxSignaling.drawio.png) + +## Consumer: execute the token refresh + +There are three possibilities for how the consumer performs the token refresh. Which one is suitable for a particular +customer will largely depend on the participant's deployment setup. These are not mutually excluding, they are merely +different approaches that are all supported by Tractus-X EDC. + +### 1. Automatic refresh using the (consumer) DataPlane + +This is suitable for deployments that elect to use a data plane on the consumer side, effectively acting as HTTP client. +Data requests are made by the consumer's data plane. Upon receiving an HTTP error code indicating an authentication +failure (HTTP 4xx), the consumer data plane refreshes the token using the `TokenRefreshHandler` and retries the request. +This is called "lazy refresh". + +![](./AutomaticRefresh.drawio.png) + +- `(1)`: Consumer data plane receives HTTP 401 indicating an auth failure +- `(2)`: The `TokenRefreshHandler` module creates the `authentication_token` (see [documentation]()) +- `(3)`: The `TokenRefreshHandler` module sends token refresh request to provider's public Refresh API. + +Note that if the token-refresh call also fails with an HTTP 4xx error code, the token must be regarded as invalid and +not authorized. An expired contract agreement or an unsatisfied policy could be reasons for that ( +see [decision record](https://github.com/eclipse-edc/Connector/tree/main/docs/developer/decision-records/2023-09-07-policy-monitor) +and [documentation](https://github.com/eclipse-edc/Connector/blob/main/docs/developer/policy-monitor.md)). + +Alternatively, implementations of the `TokenRefreshHandler` could choose to proactively refresh the token if nearing +expiry instead of "letting it fail" first. _This is transparent to the client application._ + +### 2. Automatic refresh using the `/edrs` API + +In this scenario it is the client application making the HTTP request against the provider's data plane. To do that, it +first has to use the `/edrs` API to obtain the access token. The `/edrs` API inspects the token and performs a refresh +if +required, then returns back a (possibly refreshed) access token to the client application: + +![](./AutomaticRefreshEdrApi.drawio.png) + +- `(1)`: client application obtains token from EDR API +- `(2)`: EDR API (or a related component) checks if the token requires renewal +- `(3)`: EDR API triggers `TokenRefreshHandler` to make the refresh request +- `(4)`: `TokenRefreshHandler` calls refresh endpoint of provider data plane +- `(3)/(5)`: (refreshed) token is returned to client application + +### 3. Manual refresh by the client application + +Like in the previous section, this scenario outlines a deployment without a consumer data plane. The difference is, that +in this variant the access token is _not automatically renewed_ by the EDR API. This is suitable for client applications +that need to maintain control over the refresh process, e.g. due to some backoffice user permission system. +For those deployments Tractus-X EDC offers an endpoint in the management API `POST /v1/token/refresh` that client +applications can use to trigger the token renewal. + +![](./ManualRefresh.drawio.png) + +- `(1)`: the client application requests the access token using the EDR API +- `(2)`: the client application makes the data request against the provider's data plane +- `(3)`: that request is answered with a HTTP 4xx indicating an auth error +- `(4)`: client application triggers the token refresh via the control plane Management API +- `(5)` The `TokenRefreshHandler` module creates the `authentication_token` ( + see [documentation](https://github.com/eclipse-tractusx/tractusx-profiles/blob/main/tx/refresh/refresh.token.grant.profile.md#31-client-authentication)) +- `(6)` The `TokenRefreshHandler` module sends token refresh request to provider's public Refresh API. Note that + the `TokenRefreshHandler` module is the same as before, only that it is contained in the ControlPlane + +### 4. Token refresh is handled completely out-of-band + +Consumer's control plane and data plane are not involved. This is a very specific use case, and is only recommended if +neither of the other scenarios are viable. Note that Tractus-X EDC will provide no support for this scenario. diff --git a/docs/development/decision-records/2024-03-05_token_refresh/README.md b/docs/development/decision-records/2024-03-05_token_refresh/README.md new file mode 100644 index 000000000..d3f13985f --- /dev/null +++ b/docs/development/decision-records/2024-03-05_token_refresh/README.md @@ -0,0 +1,66 @@ +# Tractus-X Token Refresh + +## Decision + +Implement token refresh for the Tractus-X EDC Data Plane according to the [Tractus-X Refresh Token Grant Profile](https://github.com/eclipse-tractusx/tractusx-profiles/blob/main/tx/refresh/refresh.token.grant.profile.md). +This enables provider data planes to implement refresh tokens in an interoperable way for client applications using the +Tractus-X EDC Connector. + +Note refresh token support will be done almost exclusively in Tractus-X EDC, except for a few minor updates to upstream +EDC. + +## Rationale + +Data providers may use short-lived tokens for HTTP client pull data transfers to enhance security. Refresh tokens allow +data access to be maintained using short-lived tokens without starting a new transfer process. + +## Approach + +The upstream EDC `DefaultDataPlaneAccessTokenServiceImpl` will be replaced to create a refresh token pinned to the +client's DID. This TX implementation will use the following upstream EDC enhancements: + +- An upstream EDC decorator that adds the client DID as an extension property to the signaling request. +- Upgrade the `AccessTokenData` type to allow for extensible properties to enable the `AccessTokenDataStore` to persist + the renewal token. + +Tractus-X EDC will implement token refresh based on +the [Trasctus-X Refresh Token Grant Profile](https://github.com/eclipse-tractusx/tractusx-profiles/blob/main/tx/refresh/refresh.token.grant.profile.md) + +### Provider Data Plane + +#### TX DataPlaneAccessTokenService + +An implementation of the `DataPlaneAccessTokenService` will be provided that overrides the +default `DefaultDataPlaneAccessTokenServiceImpl` implementation. The TX implementation will do the following: + +- Create an expiring access token and set the `expiresIn` attribute of the `DataAddress.` +- Create a refresh token that is returned in the `refreshToken` property of the `DataAddress`. +- Provide a configuration option to set the `refreshEndpoint` property of the `DataAddress.` If not set, + the `refreshEndpoint` property will not be included. + +It will be possible for distributions not to include the TX implementation of the `DataPlaneAccessTokenService` and +substitute another or revert to the EDC default implementation. + +The refresh token will be sender-constrained to the client DID passed as part of the `DataFlowStartMessage`. + +#### OAuth 2 Refresh API + +The provider data plane will be extended to add a public API that implements the OAuth2 `refresh_token` grant. The +refresh token will be verified according to the steps laid out in the _Tractus-X Refresh Token Grant Profile._ When a +refresh request is successfully made, a new access token and refresh token will be generated and returned to the client. +If a refresh token request fails due to a validation error, the associated access token and refresh token will be +invalidated. + +### Client-side Token Refresh + +A client will be able to initiate a token refresh request in three ways: + +- Request data through a client data plane instance, which will check the token expiration and issue a refresh request + if required. This behavior will be transparent to the client. +- Request data directly and check if the access token is expired. If the token is expired, the client can call a client + data plane management API to renew the access token. +- Request data directly and handle access token renewal using OAuth 2 directly. + +To support refresh token renewal, a DIM specific extension will be added that requests the authentication JWT to be +signed using a key managed by DIM. This extension can use the same DIM API as the IATP extension. + diff --git a/docs/usage/management-api-walkthrough/02_policies.md b/docs/usage/management-api-walkthrough/02_policies.md index f58ebf77f..f7c8650ec 100644 --- a/docs/usage/management-api-walkthrough/02_policies.md +++ b/docs/usage/management-api-walkthrough/02_policies.md @@ -1,7 +1,31 @@ -# Creating a Policy Definition +# Policies -A policy is a declaration of a Data Consumer's rights and duties. Policies themselves make no statements about the -object that they may grant access and usage permission to. They are created at the EDC like this: +## Policies in Catena-X + +In the EDC, policies are pure [ODRL (Open Digital Rights Language)](https://www.w3.org/TR/odrl-model/). +Like the payloads of the [Dataspace Protocol](README.md), they are written in **JSON-LD**. Even if the +user only has rudimentary knowledge of [JSON-LD](https://json-ld.org/), the [**policy playground +**](https://eclipse-tractusx.github.io/tutorial-resources/policy-playground/) will provide a good starting point to +start +writing policies. It is important to keep in mind that the extensive ODRL-context (that the EDC is aware of) allows for +ergonomic reuse of the vocabulary in individual policies. + +### Policies & Verifiable Credentials (VC) + +#### General Information + +Catena-X uses policies to determine access to and use of data. The policies refer to verifiable credentials (VC) that +are stored in the Wallets. Catena-X uses the principle of self-sovereign identity (SSI). + +The key architectural principle underlying this specification is that policy definitions must be decoupled from their +corresponding VC schema. Namely, the specific **constraints** ( +see [ODRL-classes](#odrl-information-model-classes-excerpt)) +and shape of the VC schema must not be reflected in the policy definition. This allows VC schemas to be altered without +impacting policy definitions. + +### Creating a Policy Definition + +Policies can be created in the EDC as follows: ```http POST /v3/assets HTTP/1.1 @@ -13,10 +37,191 @@ Content-Type: application/json ```json { "@context": { + "@vocab": "https://w3id.org/edc/v0.0.1/ns/", "odrl": "http://www.w3.org/ns/odrl/2/" }, - "@type": "PolicyDefinitionRequestDto", - "@id": "", + "@type": "PolicyDefinitionRequest", + "@id": "{{POLICY_ID}}", + "policy": { + "@type": "odrl:Set", + "odrl:permission": [ + { + "odrl:action": { + "@id": "odrl:use" + }, + "odrl:constraint": [ + { + "odrl:leftOperand": { + "@value": "BusinessPartnerNumber" + }, + "odrl:operator": { + "@id": "odrl:eq" + }, + "odrl:rightOperand": "" + } + ] + } + ] + } +} +``` + +| Variable | Content | +|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `@context` | In JSON-LD, `@context` is a fundamental concept used to define the mapping of terms used within the JSON-LD document to specific IRIs (Internationalized Resource Identifiers). It provides a way to establish a shared understanding of the vocabulary used in a JSON-LD document, making it possible to create structured and semantically rich data that can be easily integrated with other data sources on the web. | +| `@context`.`odrl:` | Prefixes allow you to define short aliases for longer IRIs. For example, instead of repeatedly using the full IRI [http://www.w3.org/ns/odrl/2/](http://www.w3.org/ns/odrl/2/), you can define a prefix like "odrl" and append a segment/fragment to identify the resource in the namespace. | +| `@id` | A Policy MUST have one uid property value (of type IRI) to identify the Policy. Note: The `@id` is on the upper level. It is a database policy definition which wraps the ODRL policy. | +| `policy`.`@type` | A Set Policy is the default Policy subclass. The Set is aimed at scenarios where there is an open criteria for the semantics of the policy expressions and typically refined by other systems/profiles that process the information at a later time. No privileges are granted to any Party (if defined). More detailed information about the possible policy subclasses can be found [here](https://w3c.github.io/poe/model/#infoModel). | +| `policy`.`permission` | A Policy MUST have at least one permission, prohibition, or obligation property values. | +| `policy`.`permission`.`action` | "use" the target asset (under a specific permission), currently only the action "use" is used by Catena-X | +| `policy`.`permission`.`constraint` | A boolean/logical expression that refines an Action and Party/Asset collection or the conditions applicable to a Rule. The leftOperand instances MUST clearly be defined to indicate the semantics of the Constraint. Catena-X will use the **left operand** of a *constraint* to associate a specific verifiable credential (VC). As most are use-case-agreements, [this notation](https://github.com/eclipse-tractusx/tractusx-profiles/blob/main/cx/policy/specs/policy.mapping.md) is useful. **Right Operand:** The rightOperand is the value of the Constraint that is to be compared to the leftOperand. | + +Please note that in JSON-LD, structures that may look different may actually have the same meaning. They may be expanded +or compacted, define additional `@context` objects, refer to a predefined outside `context` or others. Using a parser +or the [json-ld playground](https://json-ld.org/playground/) helps to be consistent. + +If the creation of the `policy-definition` was successful, the Management-API will return HTTP 200. + +#### Catena-X specific `constraints` + +This implementation (`tractusx-edc`) contains extensions that trigger specific behavior when encountering specific +policies. + +1. **Checks against the use-case**: The [cx-policy extension](https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx) is responsible to resolve a use-case-specific + leftOperand against a VC. The list of use-case credentials can be found [here](https://github.com/eclipse-tractusx/tractusx-profiles/tree/main/cx/credentials/samples). +2. **Checks against the BPN**: The [BPN-validation extension](https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/bpn-validation) allows to define either a single Business Partner + authorized to pass the constraint or define a group of BPNs that may pass and can be extended at runtime. +3. **Checks for temporal validity**: If a usage policy is defined against a HTTP-based asset accessible via EDR-tokens, + the Data Provider can prohibit issuance of new tokens by defining a specific constraint based on the + [contract validity check extension](https://github.com/eclipse-edc/Connector/blob/main/docs/developer/contract-duration/contract-validity-check.md) + +### Access & Usage Policies + +In Catena-X, a distinction is made between **Access** and **Usage** Policies. + +- **access policy:** determines whether a particular consumer is offered an asset or not. For example, we may want to + restrict certain assets such that only consumers within a particular geography can see them. Consumers outside that + geography wouldn't even have them in their catalog. +- **usage policy or contract policy:** determines the conditions for initiating a contract negotiation for a particular + asset. Note that does not automatically guarantee the successful creation of a contract, it merely expresses the + eligibility to start the negotiation. The terms "usage policy" and "contract policy" are used synonymously! + +**The Access and Usage Policies are not distinguished by any special semantics, but rather by the time at which they are +checked.** + +Whether a policy is used as access or usage policy is determined during [contract definition](03_contractdefinitions.md). + +### Exemplary scenarios + +For the following Scenarios, we assume there is a **Partner 1 (provider)** who wants to provide Data for **Partner 2 +(consumer)** + +- Partner 1 (provider) has the Business-Partner-Number BPN12345. +- Partner 2 (consumer) has the Business-Partner-Number BPN6789. + +Partner 2 (consumer) signed the **Traceability Framework Agreement** and followed all the necessary steps that the +Credential appears within Partner 2s identity. + +So for this example, if Partner 2 does a catalog request to Partner 1, the following credential is provided to +Partner 1: + +```json +{ + "@context": [ + "https://w3id.org/2023/catenax/credentials/summary/v1" + ], + "id": "", + "type": [ + "VerifiableCredential", + "SummaryCredential" + ], + "issuer": "", + "issuanceDate": "2023-06-02T12:00:00Z", + "expirationDate": "2022-06-16T18:56:59Z", + "credentialSubject": { + "id": "", + "holderIdentifier": "BPN6789", + "items": [ + "MembershipCredential", + "TraceabilityCredential" + ], + "contractTemplates": "https://public.catena-x.org/contracts/" + } +} +``` +> Note: This specific credential schema will not be supported in upcoming releases but serves the purpose to illustrate +> what such a credential may look like. + +#### Scenario 1 + +Partner 1 wants to create an Access Policy, that Partner 2 can only receive the Contract offer if its BPN matches AND if +it has the Traceability Credential (Traceability Contract Signed). If one of those is missing, Partner 2 won't receive a +Contract Offer. + +##### Partner 1 - Access Policy Example + +```json + { + "@context": { + "odrl": "http://www.w3.org/ns/odrl/2/" + }, + "@type": "PolicyDefinitionRequest", + "@id": "{{POLICY_ID}}", + "policy": { + "@type": "Policy", + "odrl:permission": [ + { + "odrl:action": "use", + "odrl:constraint": { + "@type": "LogicalConstraint", + "odrl:and": [ + { + "@type": "Constraint", + "odrl:leftOperand": "BusinessPartnerNumber", + "odrl:operator": { + "@id": "odrl:eq" + }, + "odrl:rightOperand": "{{CONSUMERS_BPN}}" + }, + { + "@type": "Constraint", + "odrl:leftOperand": "FrameworkAgreement.traceability", + "odrl:operator": { + "@id": "odrl:eq" + }, + "odrl:rightOperand": "active" + } + ] + } + } + ] + } +} +``` + +Each `leftOperand` is resolved by the EDC against a property in the participant's Verfiable Credential. Thus the exact +data structure may not always match between the VC and the policy. + +##### Desired Outcome + +Partner 2 receives the Contract Offer because the BPNs are matching and he owns the Traceability Credential. + +#### Scenario 2 + +Partner 1 wants to create an Access Policy, that Partner 2 can receive the Contract Offer if its BPN matches. But a +Contract Agreement should only be created if Partner 2 also signed the Traceability Framework Agreement. So in this +case, Partner 2 should receive the Contract Offer in the first place, regardless if it signed the Traceability Framework +Agreement. The signing of the Agreement should be checked at the time of contract negotiation. + +##### Partner 1 - Access Policy Example (Scenario 2) + +```json +{ + "@context": { + "odrl": "http://www.w3.org/ns/odrl/2/" + }, + "@type": "PolicyDefinitionRequest", + "@id": "{{POLICY_ID}}", "policy": { "@type": "Policy", "odrl:permission": [ @@ -28,29 +233,185 @@ Content-Type: application/json "odrl:operator": { "@id": "odrl:eq" }, - "odrl:rightOperand": "" + "odrl:rightOperand": "{{BPN6789}}" + } + } + ] + } +} +``` + +##### Partner 1 - Usage/Contract Policy Example (Scenario 2) + +```json + { + "@context": { + "odrl": "http://www.w3.org/ns/odrl/2/" + }, + "@type": "PolicyDefinitionRequest", + "@id": "{{POLICY_ID}}", + "policy": { + "@type": "Policy", + "odrl:permission": [ + { + "odrl:action": "use", + "odrl:constraint": { + "@type": "Constraint", + "odrl:leftOperand": "FrameworkAgreement.traceability", + "odrl:operator": { + "@id": "odrl:eq" + }, + "odrl:rightOperand": "active" } } ] } } +``` +##### Desired Outcome (Scenario 2) + +Partner 2 receives the Contract Offer and is able to negotiate the contract because he owns the Traceability Credential. + +#### Scenario 3 + +Partner 1 wants to create an Access Policy that Partner 2 can receive the Contract Offer if the BPN is matching AND +Partner 2 is identified as a Dismantler (owns the "DismantlerCredential"). + +##### Partner 1 - Access Policy Example (Scenario 3) + +```json +{ + "@context": { + "odrl": "http://www.w3.org/ns/odrl/2/" + }, + "@type": "PolicyDefinitionRequest", + "@id": "{{POLICY_ID}}", + "policy": { + "@type": "Policy", + "odrl:permission": [ + { + "odrl:action": "use", + "odrl:constraint": { + "@type": "LogicalConstraint", + "odrl:and": [ + { + "@type": "Constraint", + "odrl:leftOperand": "BusinessPartnerNumber", + "odrl:operator": { + "@id": "odrl:eq" + }, + "odrl:rightOperand": "{{BPN6789}}" + }, + { + "@type": "Constraint", + "odrl:leftOperand": "Dismantler", + "odrl:operator": { + "@id": "odrl:eq" + }, + "odrl:rightOperand": "active" + } + ] + } + } + ] + } +} ``` -In the EDC, policies are pure [ODRL (Open Digital Rights Language)](https://www.w3.org/TR/odrl-model/). -Like the payloads of the [Dataspace Protocol](1-management-api-overview), they are written in JSON-LD. Even if the user -only has rudimentary knowledge of JSON-LD, the [policy playground](https://eclipse-tractusx.github.io/tutorial-resources/policy-playground/) -will provide a good starting point to start writing policies. It is important to keep in mind that the extensive ODRL- -context (that the EDC is aware of) allows for ergonomic reuse of the vocabulary in individual policies. +##### Desired Outcome (Scenario 3) + +Partner 2 does not receive the Contract Offer as he does not own the Dismantler Credential and is therefore not able to +negotiate the contract or request the data. + +#### Scenario 4 + +Partner 1 wants to create an Access Policy that Partner 2 can receive the Contract Offer if the BPN is matching. +Further, Partner 1 only wants to create an Agreement if Partner 2 is identified as a Dismantler (owns the +"DismantlerCredential"). Otherwise the Negotiation has to fail. + +##### Partner 1 - Access Policy Example (Scenario 4) + +```json +{ + "@context": { + "odrl": "http://www.w3.org/ns/odrl/2/" + }, + "@type": "PolicyDefinitionRequest", + "@id": "{{POLICY_ID}}", + "policy": { + "@type": "Policy", + "odrl:permission": [ + { + "odrl:action": "use", + "odrl:constraint": { + "@type": "LogicalConstraint", + "odrl:and": [ + { + "@type": "Constraint", + "odrl:leftOperand": "BusinessPartnerNumber", + "odrl:operator": { + "@id": "odrl:eq" + }, + "odrl:rightOperand": "{{BPN6789}}" + } + ] + } + } + ] + } +} +``` + +##### Partner 1 - Usage/Contract Policy Example (Scenario 4) + +```json +{ + "@context": { + "odrl": "http://www.w3.org/ns/odrl/2/" + }, + "@type": "PolicyDefinitionRequest", + "@id": "{{POLICY_ID}}", + "policy": { + "@type": "Policy", + "odrl:permission": [ + { + "odrl:action": "use", + "odrl:constraint": { + "@type": "LogicalConstraint", + "odrl:and": [ + { + "@type": "Constraint", + "odrl:leftOperand": "Dismantler", + "odrl:operator": { + "@id": "odrl:eq" + }, + "odrl:rightOperand": "active" + } + ] + } + } + ] + } +} +``` + +##### Desired Outcome (Scenario 4) + +Partner 2 receives the Contract Offer in the first place. -## Writing Policies for the EDC +The contract negotiation, started by Partner 2 fails because he has not been identified as Dismantler and therefore does +not own the Dismantler Credential. -ODRL's model and expressiveness surpass the EDC's current ability to interpret the policies and derive behavior from -them. This must be kept in mind even when Data Offers based on policies are not yet published to the Dataspace. Here again, -configuring the wrong policies is a risk for unsafe and non-compliant behavior. This is exacerbated by the fact that -the EDC interprets policies it can't evaluate as true by default. A couple of examples: +#### Writing Policies for the EDC + +ℹ️ ODRL's model and expressiveness surpass the EDC's current ability to interpret the policies and derive behavior from +them. This must be kept in mind even when Data Offers based on policies are not yet published to the Dataspace. Here +again, configuring the wrong policies is a risk for unsafe and non-compliant behavior. This is exacerbated by the fact +that the EDC interprets policies it can't evaluate as true by default. A couple of examples: + +#### Let all pass -### Let all pass ```json { "@context": { @@ -69,12 +430,12 @@ the EDC interprets policies it can't evaluate as true by default. A couple of ex } ``` -### Only let a Business Partner Group pass +#### Only let a Business Partner Group pass A Business Partner Group is a group of BPNs that are allowed to pass this constraint. A BPN can be added to a group even after a Contract Offer for a certain BPN-Group was published. The groups are persisted and maintained in the Provider's Control Plane. The EDC-Management-API's `/business-partner-groups` endpoint offers CRUD-operations for -it. +it. ```json { @@ -103,7 +464,7 @@ it. ``` -### Chaining Constraints +#### Chaining Constraints Constraints can be chained together via logical constraints. This is currently implemented for `odrl:and`, `odrl:or` and `odrl:xone` (exactly one constraint evaluates to `true`). @@ -146,22 +507,75 @@ and `odrl:xone` (exactly one constraint evaluates to `true`). } ``` -Some permission-constraints trigger specific behavior in the EDC. That should be kept in mind when designing policies -and requires an understanding of how the EDC evaluates and acts upon them. +## Additional Information about Policies + +ℹ️ All explanations in this chapter "General Information about Policies" were taken from the +following [source](https://w3c.github.io/poe/model/). + +### Introduction + +Several business scenarios require expressing what are the permitted and prohibited actions over resources. These +permitted/prohibited actions are usually expressed under the form of policies, i.e., expressions that indicate those +uses and re-uses of the content which are conformant with existing regulations or to the constraints assigned by the +owner. Policies may also be enriched with additional information, i.e., who are the entities in charge of the definition +of such Policy and those who are required to conform to it, what are the additional constrains to be associated with the +Permissions, Prohibitions and Duties expressed by the Policy. The ability to express these concepts and relationships is +important both for the producers of content, i.e., they may state in a clear way what are the permitted and the +prohibited actions to prevent misuse, and for the consumers, i.e., they may know precisely what resources they are +allowed to use and re-use to avoid breaking any rules, laws or the owner's constraints. This specification describes a +common approach to expressing these policy concepts. + +### Semantic Model + +The ODRL Information Model defines the underlying semantic model for permission, prohibition, and obligation statements +describing content usage. The information model covers the core concepts, entities and relationships that provide the +foundational model for content usage statements. These machine-readable policies may be linked directly with the content +they are associated to with the aim to allow consumers to easily retrieve this information. + +#### ODRL Information Model classes (excerpt) -| `leftOperand` | `rightOperand` | usage in
[Contract Definition](03_contractdefinitions.md) | description | -|--------------------------------------------------------------|--------------------------------------------------|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `BusinessPartnerNumber` | a BPNL | access or contract | _This function is deprecated._
The leftOperand "BusinessPartnerNumber" will trigger a check against the property in a Consumer's Verfifiable Credential (VC) that holds said BPNL. | -| `https://w3id.org/tractusx/v0.0.1/ns/BusinessPartnerGroup` | a Business Partner Group | access or contract | see [above](#only-let-a-business-partner-group-pass). The `leftOperand` is in this case not queried from the Consumer's VC but acts as a signal to check the Consumer's BPN for membership in the designated Business Partner Group. | -| `https://w3id.org/edc/v0.0.1/ns/InForceDate` | json-object with properties `@value` and `@type` | contract | If the negotiation via either [Contract Negotiation](05_contractnegotiations.md) or the [EDR process](07_edrs.md) is successful, the EDC will only renew short-lived Data-Plane tokens for a contract if the contract is still valid (in force). Start and end dates can be set with absolute timestamps or relative to the time of the contract agreement. For exact syntax, visit the [playground](https://eclipse-tractusx.github.io/tutorial-resources/policy-playground/). | -| `https://w3id.org/tractusx/v0.0.1/ns/FrameworkAgreement.pcf` | "active" | access or contract | Framework agreements in Catena-X are legal documents signed by a Business Partner to participate in a Business Scenario. In return, her credential is enhanced with a reference to the corresponding framework agreement - like in this case `pcf`. A complete list of framework agreements is maintained by the Catena-X association in standards CX-0049 and -0050. | +| Class | Description | +|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Policy` | A non-empty group of Permissions (via the permission property) and/or Prohibitions (via the prohibition property) and/or Duties (via the obligation property). | +| `Set` | Subclass of `Policy`:Supports expressing generic Rules | +| `Action` | An operation on an Asset | +| `Rule` | An abstract concept that represents the common characteristics of Permissions, Prohibitions, and Duties. | +| `Prohibition` | Subclass of `Rule`: The ability to exercise an Action over an Asset. The Permission MAY also have the duty property that expresses an agreed Action that MUST be exercised (as a pre-condition to be granted the Permission). | +| `Permission` | Subclass of `Rule`: The inability to exercise an Action over an Asset. | +| `Duty` | Subclass of `Rule`: The obligation to exercise an Action. | +| `Constraint/LogicalConstraint` | A boolean/logical expression that refines an Action and Party/Asset collection or the conditions applicable to a Rule. | -For more on the integration of Verifiable Credentials and the EDC in Catena-X, see the [specification of the Identity -and Trust Protocol (IATP)](https://github.com/eclipse-tractusx/identity-trust). +#### The `Policy` Class (excerpt) -## Notice +The Policy class has the following properties (see example below): + +- A Policy MUST have one uid property value (of type IRI [rfc3987]) to identify the Policy. +- A Policy MUST have at least one permission, prohibition, or obligation property values of type Rule. (See the + Permission, Prohibition, and Obligation sections for more details.) + +#### The `Set` Class + +An ODRL Policy of subclass `Set` represents any combination of Rules. The `Set` Policy subclass is also the **default** +subclass of Policy (if none is specified). + +Example: + +```json +{ + "@context": "http://www.w3.org/ns/odrl.jsonld", + "@type": "Set", + "@id": "", + "target": "", + "permission": [ + { + "action": "use" + } + ] +} +``` -This work is licensed under the [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/legalcode). +ℹ️ For the examples in this document, the ODRL Policy subclasses are mapped to the JSON-LD `@type` tokens. The above +example could have also used `Policy` type instead of `Set` type (**as they are equivalent**). - SPDX-License-Identifier: CC-BY-4.0 - SPDX-FileCopyrightText: 2023 Contributors of the Eclipse Foundation diff --git a/docs/usage/management-api-walkthrough/07_edrs.md b/docs/usage/management-api-walkthrough/07_edrs.md index b3e720603..9eb53c81a 100644 --- a/docs/usage/management-api-walkthrough/07_edrs.md +++ b/docs/usage/management-api-walkthrough/07_edrs.md @@ -178,7 +178,7 @@ Content-Type: application/json ## Consumer Data Plane Proxy The Consumer Data Plane Proxy is an extension available in `tractusx-edc` that will use the EDR store to simplify -the data request on Consumer side. The documentation is available [here](../../edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/README.md). +the data request on Consumer side. The documentation is available [here](../../../edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/README.md). The API fetches the data according to the input body. The body should contain the `assetId` plus `providerId` or the `transferProcessId` which identifies the EDR to use for fetching data and an `endpointUrl` which is the [provider gateway](../../edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/README.md) diff --git a/edc-controlplane/edc-controlplane-base/build.gradle.kts b/edc-controlplane/edc-controlplane-base/build.gradle.kts index fd7a31c80..c3ef42249 100644 --- a/edc-controlplane/edc-controlplane-base/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-base/build.gradle.kts @@ -23,25 +23,34 @@ plugins { } dependencies { - runtimeOnly(project(":core:edr-cache-core")) runtimeOnly(project(":core:edr-core")) - runtimeOnly(project(":edc-extensions:dataplane-selector-configuration")) - runtimeOnly(project(":edc-extensions:data-encryption")) + runtimeOnly(project(":edc-extensions:dataplane:dataplane-selector-configuration")) runtimeOnly(project(":edc-extensions:provision-additional-headers")) - runtimeOnly(project(":edc-extensions:edr:edr-api")) + runtimeOnly(project(":edc-extensions:edr:edr-api-v2")) runtimeOnly(project(":edc-extensions:edr:edr-callback")) + runtimeOnly(project(":edc-extensions:tokenrefresh-handler")) + runtimeOnly(libs.edc.core.edrstore) + runtimeOnly(libs.edc.edr.store.receiver) + runtimeOnly(libs.edc.dpf.transfer.signaling) + // needed for BPN validation runtimeOnly(project(":edc-extensions:bpn-validation")) - // needed for SSI integration + // needed for IATP integration runtimeOnly(project(":core:json-ld-core")) - runtimeOnly(project(":edc-extensions:ssi:ssi-identity-core")) - runtimeOnly(project(":edc-extensions:ssi:ssi-miw-credential-client")) - runtimeOnly(project(":edc-extensions:ssi:ssi-identity-extractor")) - runtimeOnly(project(":edc-extensions:cx-policy")) + runtimeOnly(libs.edc.core.did) + runtimeOnly(libs.edc.identity.did.web) + runtimeOnly(libs.edc.core.identitytrust) + runtimeOnly(libs.edc.identity.trust.transform) + runtimeOnly(libs.edc.identity.trust.issuers.configuration) + runtimeOnly(project(":edc-extensions:iatp:tx-iatp")) + runtimeOnly(project(":edc-extensions:iatp:tx-iatp-sts-dim")) + runtimeOnly(project(":edc-extensions:bdrs-client")) + runtimeOnly(project(":edc-extensions:data-flow-properties-provider")) + runtimeOnly(libs.edc.core.connector) runtimeOnly(libs.edc.core.controlplane) runtimeOnly(libs.edc.core.policy.monitor) runtimeOnly(libs.edc.config.filesystem) diff --git a/edc-controlplane/edc-controlplane-postgresql-azure-vault/build.gradle.kts b/edc-controlplane/edc-controlplane-postgresql-azure-vault/build.gradle.kts index 78a4c2022..0d86a03a9 100644 --- a/edc-controlplane/edc-controlplane-postgresql-azure-vault/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql-azure-vault/build.gradle.kts @@ -28,15 +28,13 @@ plugins { dependencies { runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) - runtimeOnly(project(":edc-extensions:postgresql-migration")) - runtimeOnly(project(":edc-extensions:edr:edr-cache-sql")) + runtimeOnly(project(":edc-extensions:migrations::control-plane-migration")) runtimeOnly(project(":edc-extensions:bpn-validation:business-partner-store-sql")) runtimeOnly(libs.edc.azure.vault) runtimeOnly(libs.bundles.edc.sqlstores) runtimeOnly(libs.edc.transaction.local) runtimeOnly(libs.edc.sql.pool) runtimeOnly(libs.edc.core.controlplane) - runtimeOnly(libs.edc.dpf.transfer) runtimeOnly(libs.postgres) } diff --git a/edc-controlplane/edc-controlplane-postgresql-azure-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-azure-vault/src/main/docker/Dockerfile index 472d1ec68..1bbb81d21 100644 --- a/edc-controlplane/edc-controlplane-postgresql-azure-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql-azure-vault/src/main/docker/Dockerfile @@ -19,7 +19,7 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# -FROM eclipse-temurin:21.0.2_13-jre-alpine +FROM eclipse-temurin:22_36-jre-alpine ARG JAR ARG OTEL_JAR ARG ADDITIONAL_FILES diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts index 8c8c7cefa..778df1b0a 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts @@ -28,15 +28,13 @@ plugins { dependencies { runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) - runtimeOnly(project(":edc-extensions:postgresql-migration")) - runtimeOnly(project(":edc-extensions:edr:edr-cache-sql")) + runtimeOnly(project(":edc-extensions:migrations::control-plane-migration")) runtimeOnly(project(":edc-extensions:bpn-validation:business-partner-store-sql")) runtimeOnly(libs.edc.vault.hashicorp) runtimeOnly(libs.bundles.edc.sqlstores) runtimeOnly(libs.edc.transaction.local) runtimeOnly(libs.edc.sql.pool) runtimeOnly(libs.edc.core.controlplane) - runtimeOnly(libs.edc.dpf.transfer) runtimeOnly(libs.postgres) } diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile index 9a5000861..c66d04b48 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile @@ -20,7 +20,7 @@ ################################################################################# -FROM eclipse-temurin:21.0.2_13-jre-alpine +FROM eclipse-temurin:22_36-jre-alpine ARG JAR ARG OTEL_JAR ARG ADDITIONAL_FILES diff --git a/edc-controlplane/edc-runtime-memory/README.md b/edc-controlplane/edc-runtime-memory/README.md index 6ba6cdc5a..4283877d4 100644 --- a/edc-controlplane/edc-runtime-memory/README.md +++ b/edc-controlplane/edc-runtime-memory/README.md @@ -30,7 +30,7 @@ extension. When in doubt, check the extensions' README that will likely be in [t ```shell docker run \ - -e SECRETS="key1:secret1,key2:secret2" \ + -e EDC_VAULT_SECRETS="key1:secret1;key2:secret2" \ -p 8080:8080 -p 8181:8181 -p 8282:8282 -p 9090:9090 -p 9999:9999 \ -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ diff --git a/edc-controlplane/edc-runtime-memory/build.gradle.kts b/edc-controlplane/edc-runtime-memory/build.gradle.kts index c5491e8b3..b3b9dc9a4 100644 --- a/edc-controlplane/edc-runtime-memory/build.gradle.kts +++ b/edc-controlplane/edc-runtime-memory/build.gradle.kts @@ -25,12 +25,11 @@ plugins { dependencies { implementation(libs.edc.spi.core) - runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) { - exclude(module = "data-encryption") - } + runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) runtimeOnly(project(":edc-dataplane:edc-dataplane-base")) runtimeOnly(libs.edc.core.controlplane) testImplementation(libs.edc.junit) + testImplementation(libs.edc.lib.boot) } tasks.withType { diff --git a/edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile index 136f0f4df..281f7baaf 100644 --- a/edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile @@ -47,6 +47,5 @@ COPY ${ADDITIONAL_FILES} ./ HEALTHCHECK NONE -# need the sh -c syntax so that the SECRETS variable gets expanded # use the "exec" syntax so that SIGINT reaches the JVM -> graceful termination -CMD ["sh", "-c", "exec java -Dedc.fs.config=/app/configuration.properties -Dedc.vault.secrets=\"${SECRETS}\" -Djava.util.logging.config.file=/app/logging.properties -Djava.security.egd=file:/dev/urandom -jar edc-controlplane.jar"] +CMD ["sh", "-c", "exec java -Dedc.fs.config=/app/configuration.properties -Djava.util.logging.config.file=/app/logging.properties -Djava.security.egd=file:/dev/urandom -jar edc-controlplane.jar"] diff --git a/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultSeedExtensionTest.java b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultSeedExtensionTest.java index 2eb407ce0..d01e055d3 100644 --- a/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultSeedExtensionTest.java +++ b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultSeedExtensionTest.java @@ -19,7 +19,7 @@ package org.eclipse.tractusx.edc.vault.memory; -import org.eclipse.edc.connector.core.vault.InMemoryVault; +import org.eclipse.edc.boot.vault.InMemoryVault; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.security.Vault; diff --git a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts index 0172e73d0..c9bb8619f 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts @@ -25,6 +25,7 @@ plugins { dependencies { implementation(project(":edc-dataplane:edc-dataplane-base")) + runtimeOnly(project(":edc-extensions:migrations::data-plane-migration")) implementation(libs.edc.azure.vault) constraints { implementation("net.minidev:json-smart:2.5.0") { @@ -32,10 +33,12 @@ dependencies { } } implementation(libs.edc.azure.identity) - implementation("com.azure:azure-security-keyvault-secrets:4.7.3") - runtimeOnly(project(":edc-extensions:edr:edr-cache-sql")) + implementation("com.azure:azure-security-keyvault-secrets:4.8.1") runtimeOnly(libs.edc.transaction.local) runtimeOnly(libs.edc.sql.pool) + runtimeOnly(libs.edc.sql.accesstokendata) + runtimeOnly(libs.edc.sql.edrindex) + runtimeOnly(libs.edc.sql.dataplane) runtimeOnly(libs.postgres) } diff --git a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile index ca47ef4a6..e106fe2a3 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -19,7 +19,7 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# -FROM eclipse-temurin:21.0.2_13-jre-alpine +FROM eclipse-temurin:22_36-jre-alpine ARG JAR ARG OTEL_JAR ARG ADDITIONAL_FILES diff --git a/edc-dataplane/edc-dataplane-base/build.gradle.kts b/edc-dataplane/edc-dataplane-base/build.gradle.kts index f7b498f74..e7285164f 100644 --- a/edc-dataplane/edc-dataplane-base/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-base/build.gradle.kts @@ -23,11 +23,16 @@ plugins { } dependencies { - runtimeOnly(project(":core:edr-cache-core")) - runtimeOnly(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-consumer-api")) - runtimeOnly(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-api")) - runtimeOnly(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-core")) + runtimeOnly(project(":core:edr-core")) + runtimeOnly(project(":edc-extensions:tokenrefresh-handler")) + runtimeOnly(project(":edc-extensions:iatp:tx-iatp-sts-dim")) + runtimeOnly(project(":edc-extensions:dataplane:dataplane-proxy:edc-dataplane-proxy-consumer-api")) + runtimeOnly(project(":edc-extensions:dataplane:dataplane-token-refresh:token-refresh-core")) + runtimeOnly(project(":edc-extensions:dataplane:dataplane-token-refresh:token-refresh-api")) + runtimeOnly(libs.edc.jsonld) // needed by the DataPlaneSignalingApi + runtimeOnly(libs.edc.core.did) // for the DID Public Key Resolver + runtimeOnly(libs.edc.identity.did.web) runtimeOnly(libs.edc.config.filesystem) runtimeOnly(libs.edc.auth.tokenbased) runtimeOnly(libs.edc.dpf.awss3) @@ -39,9 +44,12 @@ dependencies { runtimeOnly(libs.edc.controlplane.apiclient) runtimeOnly(libs.edc.dpf.api.control) - runtimeOnly(libs.edc.dpf.api.public) + runtimeOnly(libs.edc.dpf.api.signaling) + + runtimeOnly(libs.edc.dpf.api.public.v2) runtimeOnly(libs.edc.core.connector) runtimeOnly(libs.edc.boot) + runtimeOnly(libs.edc.core.edrstore) runtimeOnly(libs.bundles.edc.monitoring) runtimeOnly(libs.edc.ext.http) diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts index 63063b271..16a144279 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts @@ -25,10 +25,13 @@ plugins { dependencies { implementation(project(":edc-dataplane:edc-dataplane-base")) + runtimeOnly(project(":edc-extensions:migrations::data-plane-migration")) runtimeOnly(libs.edc.vault.hashicorp) - runtimeOnly(project(":edc-extensions:edr:edr-cache-sql")) runtimeOnly(libs.edc.transaction.local) runtimeOnly(libs.edc.sql.pool) + runtimeOnly(libs.edc.sql.accesstokendata) + runtimeOnly(libs.edc.sql.edrindex) + runtimeOnly(libs.edc.sql.dataplane) runtimeOnly(libs.postgres) } diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile index 2d0e41b11..6540e9606 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -20,7 +20,7 @@ ################################################################################# -FROM eclipse-temurin:21.0.2_13-jre-alpine +FROM eclipse-temurin:22_36-jre-alpine ARG JAR ARG OTEL_JAR ARG ADDITIONAL_FILES diff --git a/edc-extensions/README.md b/edc-extensions/README.md deleted file mode 100644 index e2c2855f9..000000000 --- a/edc-extensions/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# EDC Extensions Overview - -The following extensions provide additional functionality to the core EDC. -They are currently only available in Tractus-X EDC. - -## Business Partner Validation - -This extension allows for validation of business partners within the access policy. - -## Control Plane EDR APIs - -The goal of this extension is to simplify the process of retrieving data out of EDC. -It returns `EndpointDataReference` object, hiding all the communication details for contract offers, -contract negotiation, transfer process and retrieving the underlying data through the data-planes. - -## Data Encryption - -The EDC encrypts sensitive information inside a token it sends to other applications (potentially cross-company). -This extension implements the encryption of this data and should be used with secure keys and algorithms at all times. - -## Data Plane Selector - -This control plane extension makes it possible to configure one or more data plane instances. -During a transfer the control plane will look for an instance with matching capabilities to transfer data. - -## Hashicorp Vault - -This extension allows for usage of Hashicorp Vault for secret storage. -It is the default used in Tractus-X EDC. - -## PostrgreSQL Migration - -While the core EDC is able to interact with PostgreSQL databases, -it does not automate migrations between schema versions. -This extension adds that functionality. - -## Transfer Process SFTP - -This extension allows for the use of SFTP backends for the data plane (but is not included in the provided control- and data plane). - -## NOTICE - -This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). - -- SPDX-License-Identifier: Apache-2.0 -- SPDX-FileCopyrightText: 2021,2022,2023 Contributors to the Eclipse Foundation -- Source URL: diff --git a/edc-extensions/ssi/ssi-identity-core/build.gradle.kts b/edc-extensions/bdrs-client/build.gradle.kts similarity index 74% rename from edc-extensions/ssi/ssi-identity-core/build.gradle.kts rename to edc-extensions/bdrs-client/build.gradle.kts index 3b9a4bda9..30e1401e8 100644 --- a/edc-extensions/ssi/ssi-identity-core/build.gradle.kts +++ b/edc-extensions/bdrs-client/build.gradle.kts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -18,16 +18,17 @@ ********************************************************************************/ plugins { - `java-library` `maven-publish` + `java-library` } dependencies { - implementation(project(":spi:ssi-spi")) + implementation(project(":core:core-utils")) + implementation(project(":spi:bdrs-client-spi")) implementation(libs.edc.spi.core) - implementation(libs.edc.spi.jwt) - implementation(libs.edc.spi.token) - implementation(libs.edc.token.core) - implementation(libs.nimbus.jwt) - testImplementation(testFixtures(libs.edc.junit)) + implementation(libs.edc.spi.http) + + testImplementation(libs.netty.mockserver) + testImplementation(libs.edc.junit) + testImplementation(libs.awaitility) } diff --git a/edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientAudienceMapper.java b/edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientAudienceMapper.java new file mode 100644 index 000000000..fbfa0b434 --- /dev/null +++ b/edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientAudienceMapper.java @@ -0,0 +1,42 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.identity.mapper; + +import org.eclipse.edc.spi.iam.AudienceResolver; +import org.eclipse.edc.spi.types.domain.message.RemoteMessage; +import org.eclipse.tractusx.edc.spi.identity.mapper.BdrsClient; + +/** + * An incoming {@link RemoteMessage} is mapped to a DID by calling {@link BdrsClient#resolve(String)} with the {@link RemoteMessage#getCounterPartyId()} + */ +class BdrsClientAudienceMapper implements AudienceResolver { + + private final BdrsClient client; + + BdrsClientAudienceMapper(BdrsClient client) { + this.client = client; + } + + @Override + public String resolve(RemoteMessage remoteMessage) { + return client.resolve(remoteMessage.getCounterPartyId()); + } + +} diff --git a/edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientExtension.java b/edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientExtension.java new file mode 100644 index 000000000..f89cdde5e --- /dev/null +++ b/edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientExtension.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.identity.mapper; + +import org.eclipse.edc.http.spi.EdcHttpClient; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.tractusx.edc.core.utils.RequiredConfigWarnings; +import org.eclipse.tractusx.edc.spi.identity.mapper.BdrsClient; + +import static org.eclipse.tractusx.edc.identity.mapper.BdrsClientExtension.NAME; + +@Extension(value = NAME) +public class BdrsClientExtension implements ServiceExtension { + public static final String NAME = "BPN/DID Resolution Service Client Extension"; + + public static final int DEFAULT_BDRS_CACHE_VALIDITY = 15 * 60; // 15 minutes + @Setting(value = "Base URL of the BDRS service", required = true) + public static final String BDRS_SERVER_URL_PROPERTY = "tx.iam.iatp.bdrs.server.url"; + + @Setting(value = "Validity period in seconds for the cached BPN/DID mappings. After this period a new resolution request will hit the server.", defaultValue = DEFAULT_BDRS_CACHE_VALIDITY + "") + public static final String BDRS_SERVER_CACHE_VALIDITY_PERIOD = "tx.iam.iatp.bdrs.cache.validity"; + + @Inject + private EdcHttpClient httpClient; + @Inject + private TypeManager typeManager; + + @Override + public String name() { + return NAME; + } + + @Provider + public BdrsClient getBdrsClient(ServiceExtensionContext context) { + var baseUrl = context.getConfig().getString(BDRS_SERVER_URL_PROPERTY, null); + if (baseUrl == null) { + RequiredConfigWarnings.warningNotPresent(context.getMonitor(), BDRS_SERVER_URL_PROPERTY); + } + var cacheValidity = context.getConfig().getInteger(BDRS_SERVER_CACHE_VALIDITY_PERIOD, DEFAULT_BDRS_CACHE_VALIDITY); + return new BdrsClientImpl(baseUrl, cacheValidity, httpClient, context.getMonitor(), typeManager.getMapper()); + } + +} diff --git a/edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientImpl.java b/edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientImpl.java new file mode 100644 index 000000000..e9c5fac5f --- /dev/null +++ b/edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientImpl.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.identity.mapper; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.Request; +import org.eclipse.edc.http.spi.EdcHttpClient; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.tractusx.edc.spi.identity.mapper.BdrsClient; + +import java.io.IOException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.zip.GZIPInputStream; + +/** + * Holds a local cache of BPN-to-DID mapping entries. + *

+ * The local cache expires after a configurable time, at which point {@link BdrsClientImpl#resolve(String)}} requests will hit the server again. + */ +class BdrsClientImpl implements BdrsClient { + private static final TypeReference> MAP_REF = new TypeReference<>() { + }; + private final String serverUrl; + private final int cacheValidity; + private final EdcHttpClient httpClient; + private final Monitor monitor; + private final ObjectMapper mapper; + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + private Map cache = new HashMap<>(); + private Instant lastCacheUpdate; + + BdrsClientImpl(String baseUrl, int cacheValidity, EdcHttpClient httpClient, Monitor monitor, ObjectMapper mapper) { + this.serverUrl = baseUrl; + this.cacheValidity = cacheValidity; + this.httpClient = httpClient; + this.monitor = monitor; + this.mapper = mapper; + } + + @Override + public String resolve(String bpn) { + String value; + lock.readLock().lock(); + try { + if (isCacheExpired()) { + lock.readLock().unlock(); // unlock read, acquire write -> "upgrade" lock + lock.writeLock().lock(); + try { + if (isCacheExpired()) { + updateCache(); + } + } finally { + lock.readLock().lock(); // downgrade lock + lock.writeLock().unlock(); + } + } + + value = cache.get(bpn); + } finally { + lock.readLock().unlock(); + } + return value; + } + + private boolean isCacheExpired() { + return lastCacheUpdate == null || lastCacheUpdate.plus(cacheValidity, ChronoUnit.SECONDS).isBefore(Instant.now()); + } + + private void updateCache() { + var request = new Request.Builder() + //.addHeader("Authorization", createMembershipPresentation()) //todo: add MembershipCredential as JWT-VP to the auth header + .header("Accept-Encoding", "gzip") + .url(serverUrl + "/bpn-directory") + .get() + .build(); + try (var response = httpClient.execute(request)) { + if (response.isSuccessful() && response.body() != null) { + var body = response.body().byteStream(); + try (var gz = new GZIPInputStream(body)) { + var bytes = gz.readAllBytes(); + cache = mapper.readValue(bytes, MAP_REF); + lastCacheUpdate = Instant.now(); + } + } else { + var msg = "Could not obtain data from BDRS server: code: %d, message: %s".formatted(response.code(), response.message()); + throw new EdcException(msg); + } + } catch (IOException e) { + monitor.severe("Error fetching BDRS data", e); + throw new EdcException(e); + } + } + +} diff --git a/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java b/edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientMapperExtension.java similarity index 57% rename from edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java rename to edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientMapperExtension.java index 8be09e430..2f5f5a19c 100644 --- a/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java +++ b/edc-extensions/bdrs-client/src/main/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientMapperExtension.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,22 +17,31 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.lifecycle; +package org.eclipse.tractusx.edc.identity.mapper; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.spi.iam.AudienceResolver; import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.web.spi.WebService; +import org.eclipse.tractusx.edc.spi.identity.mapper.BdrsClient; -public class ConsumerServicesExtension implements ServiceExtension { +import static org.eclipse.tractusx.edc.identity.mapper.BdrsClientExtension.NAME; +@Extension(value = NAME) +public class BdrsClientMapperExtension implements ServiceExtension { @Inject - private WebService webService; + private BdrsClient bdrsClient; @Override - public void initialize(ServiceExtensionContext context) { - webService.registerResource("default", new ConsumerEdrHandlerController(context.getMonitor())); + public String name() { + return NAME; + } + + @Provider + public AudienceResolver getBdrsAudienceResolver() { + return new BdrsClientAudienceMapper(bdrsClient); } } diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/bdrs-client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 81% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/bdrs-client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index c3031b319..656f01271 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/bdrs-client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -1,5 +1,5 @@ ################################################################################# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. @@ -17,4 +17,5 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# -org.eclipse.tractusx.edc.dataplane.proxy.provider.core.ProxyProviderCoreExtension +org.eclipse.tractusx.edc.identity.mapper.BdrsClientExtension +org.eclipse.tractusx.edc.identity.mapper.BdrsClientMapperExtension \ No newline at end of file diff --git a/edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientAudienceMapperTest.java b/edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientAudienceMapperTest.java new file mode 100644 index 000000000..4f6cc2649 --- /dev/null +++ b/edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientAudienceMapperTest.java @@ -0,0 +1,74 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.identity.mapper; + +import org.eclipse.edc.spi.types.domain.message.RemoteMessage; +import org.eclipse.tractusx.edc.spi.identity.mapper.BdrsClient; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class BdrsClientAudienceMapperTest { + + private final BdrsClient client = mock(); + + private final BdrsClientAudienceMapper clientAudienceMapper = new BdrsClientAudienceMapper(client); + + @Test + void resolve() { + + when(client.resolve("bpn1")).thenReturn("did:web:did1"); + + var did = clientAudienceMapper.resolve(new TestMessage("bpn1")); + + assertThat(did).isEqualTo("did:web:did1"); + + } + + @Test + void resolve_notFound() { + + when(client.resolve("bpn1")).thenReturn(null); + + var did = clientAudienceMapper.resolve(new TestMessage("bpn1")); + + assertThat(did).isNull(); + + } + + private record TestMessage(String bpn) implements RemoteMessage { + @Override + public String getProtocol() { + return "test-proto"; + } + + @Override + public String getCounterPartyAddress() { + return "http://bpn1"; + } + + @Override + public String getCounterPartyId() { + return bpn; + } + } +} \ No newline at end of file diff --git a/edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityServiceExtensionTest.java b/edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientImplExtensionTest.java similarity index 54% rename from edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityServiceExtensionTest.java rename to edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientImplExtensionTest.java index 6b9a73a1a..8e3b4246a 100644 --- a/edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityServiceExtensionTest.java +++ b/edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientImplExtensionTest.java @@ -1,5 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -15,46 +15,43 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + */ -package org.eclipse.tractusx.edc.iam.ssi.identity; +package org.eclipse.tractusx.edc.identity.mapper; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; -import org.eclipse.edc.spi.iam.IdentityService; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.spi.system.configuration.Config; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiCredentialClient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.iam.ssi.identity.SsiIdentityServiceExtension.ENDPOINT_AUDIENCE; +import static org.eclipse.tractusx.edc.identity.mapper.BdrsClientExtension.BDRS_SERVER_URL_PROPERTY; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(DependencyInjectionExtension.class) -public class SsiIdentityServiceExtensionTest { +class BdrsClientImplExtensionTest { - SsiIdentityServiceExtension extension; + private final Monitor monitor = mock(); @BeforeEach void setup(ServiceExtensionContext context) { - context.registerService(SsiCredentialClient.class, mock(SsiCredentialClient.class)); + context.registerService(Monitor.class, monitor); } @Test - void initialize(ServiceExtensionContext context, SsiIdentityServiceExtension extension) { + void createClient_whenUrlMissing_expectLogError(ServiceExtensionContext context, BdrsClientExtension extension) { var cfg = mock(Config.class); when(context.getConfig()).thenReturn(cfg); - when(cfg.getString(ENDPOINT_AUDIENCE)).thenReturn("test"); + when(cfg.getString(eq(BDRS_SERVER_URL_PROPERTY), isNull())).thenReturn(null); - extension.initialize(context); - - assertThat(context.getService(IdentityService.class)).isNotNull().isInstanceOf(SsiIdentityService.class); - - verify(cfg).getString(ENDPOINT_AUDIENCE); + extension.getBdrsClient(context); + verify(monitor).severe(eq("Mandatory config value missing: 'tx.iam.iatp.bdrs.server.url'. This runtime will not be fully operational! Starting with v0.7.x this will be a runtime error.")); } -} +} \ No newline at end of file diff --git a/edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientImplTest.java b/edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientImplTest.java new file mode 100644 index 000000000..4cb5f3c0d --- /dev/null +++ b/edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientImplTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.identity.mapper; + +import com.fasterxml.jackson.databind.ObjectMapper; +import dev.failsafe.RetryPolicy; +import okhttp3.OkHttpClient; +import org.eclipse.edc.http.client.EdcHttpClientImpl; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.model.HttpResponse; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Map; +import java.util.zip.GZIPOutputStream; + +import static java.time.Duration.ofSeconds; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.awaitility.Awaitility.await; +import static org.eclipse.edc.util.io.Ports.getFreePort; +import static org.mockito.Mockito.mock; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.verify.VerificationTimes.exactly; + +class BdrsClientImplTest { + + private final Monitor monitor = mock(); + private final ObjectMapper mapper = new ObjectMapper(); + private BdrsClientImpl client; + private ClientAndServer bdrsServer; + + @BeforeEach + void setup() { + bdrsServer = ClientAndServer.startClientAndServer(getFreePort()); + bdrsServer.when(request() + .withMethod("GET") + .withPath("/api/bpn-directory")) + .respond(HttpResponse.response() + .withHeader("Content-Encoding", "gzip") + .withBody(createGzipStream()) + .withStatusCode(200)); + + client = new BdrsClientImpl("http://localhost:%d/api".formatted(bdrsServer.getPort()), 1, new EdcHttpClientImpl(new OkHttpClient(), RetryPolicy.ofDefaults(), monitor), monitor, mapper); + } + + @AfterEach + void teardown() { + bdrsServer.stop(); + } + + @Test + void getData_whenCacheCold_shouldHitServer() { + var did = client.resolve("bpn1"); + assertThat(did).isEqualTo("did:web:did1"); + + bdrsServer.verify(request() + .withMethod("GET") + .withPath("/api/bpn-directory") + .withHeader("Accept-Encoding", "gzip"), + exactly(1)); + } + + @Test + void getData_whenCacheHot_shouldNotHitServer() { + var did1 = client.resolve("bpn1"); + var did2 = client.resolve("bpn2"); + assertThat(did1).isEqualTo("did:web:did1"); + assertThat(did2).isEqualTo("did:web:did2"); + + bdrsServer.verify(request() + .withMethod("GET") + .withPath("/api/bpn-directory") + .withHeader("Accept-Encoding", "gzip"), + exactly(1)); + } + + @Test + void getData_whenCacheExpired_shouldHitServer() { + var did1 = client.resolve("bpn1"); // hits server + assertThat(did1).isEqualTo("did:web:did1"); + + await().pollDelay(ofSeconds(2)) + .atMost(ofSeconds(3)) //cache expires + .untilAsserted(() -> { + var did2 = client.resolve("bpn2"); // hits server as well, b/c cache is expired + assertThat(did2).isEqualTo("did:web:did2"); + + bdrsServer.verify(request() + .withHeader("Accept-Encoding", "gzip") + .withMethod("GET") + .withPath("/api/bpn-directory"), + exactly(2)); + }); + + } + + @Test + void getData_whenNotFound() { + var did = client.resolve("bpn-notexist"); + assertThat(did).isNull(); + bdrsServer.verify(request() + .withMethod("GET") + .withPath("/api/bpn-directory") + .withHeader("Accept-Encoding", "gzip"), + exactly(1)); + } + + @ParameterizedTest(name = "HTTP Status {0}") + @ValueSource(ints = { 400, 401, 403, 404, 405 }) + void getData_bdrsReturnsError(int code) { + bdrsServer.reset(); + bdrsServer.when(request().withPath("/api/bpn-directory").withMethod("GET")) + .respond(HttpResponse.response().withStatusCode(code)); + assertThatThrownBy(() -> client.resolve("bpn1")).isInstanceOf(EdcException.class); + } + + private byte[] createGzipStream() { + var data = Map.of("bpn1", "did:web:did1", + "bpn2", "did:web:did2", + "bpn3", "did:web:did3"); + + var bas = new ByteArrayOutputStream(); + try (var gzip = new GZIPOutputStream(bas)) { + gzip.write(mapper.writeValueAsBytes(data)); + } catch (IOException e) { + throw new RuntimeException(e); + } + return bas.toByteArray(); + } + +} \ No newline at end of file diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwCredentialClientExtensionTest.java b/edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientMapperExtensionTest.java similarity index 69% rename from edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwCredentialClientExtensionTest.java rename to edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientMapperExtensionTest.java index 45f6fda9d..45b6feb50 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwCredentialClientExtensionTest.java +++ b/edc-extensions/bdrs-client/src/test/java/org/eclipse/tractusx/edc/identity/mapper/BdrsClientMapperExtensionTest.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,12 +17,11 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.miw; +package org.eclipse.tractusx.edc.identity.mapper; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClient; -import org.eclipse.tractusx.edc.iam.ssi.miw.credentials.SsiMiwCredentialClient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -31,18 +30,17 @@ import static org.mockito.Mockito.mock; @ExtendWith(DependencyInjectionExtension.class) -public class SsiMiwCredentialClientExtensionTest { +class BdrsClientMapperExtensionTest { - SsiMiwCredentialClientExtension extension; + private final Monitor monitor = mock(); @BeforeEach void setup(ServiceExtensionContext context) { - context.registerService(MiwApiClient.class, mock(MiwApiClient.class)); + context.registerService(Monitor.class, monitor); } @Test - void initialize(SsiMiwCredentialClientExtension extension) { - assertThat(extension.credentialVerifier()).isInstanceOf(SsiMiwCredentialClient.class); + void createMapper(BdrsClientMapperExtension extension) { + assertThat(extension.getBdrsAudienceResolver()).isInstanceOf(BdrsClientAudienceMapper.class); } - -} +} \ No newline at end of file diff --git a/edc-extensions/bpn-validation/README.md b/edc-extensions/bpn-validation/README.md index 02abad765..b947e75f5 100644 --- a/edc-extensions/bpn-validation/README.md +++ b/edc-extensions/bpn-validation/README.md @@ -28,8 +28,15 @@ Both previously mentioned evaluation functions are bound to the following scopes This policy states, that a certain BPN must, may or must not be member of a certain group. Groups may be represented as scalar, or as comma-separated lists. For semantic expression, the following ODRL operators are -supported: `eq`, `neq`, `in`, `isAllOf`, `isAnyOf`, `isNoneOf`. The following example demonstrates a full JSON-LD -structure in expanded form, containing such a constraint. +supported: +- `eq` +- `neq` +- `isPartOf` +- `isAllOf` +- `isAnyOf` +- `isNoneOf` + +The following example demonstrates a full JSON-LD structure in expanded form, containing such a constraint. ### Example diff --git a/edc-extensions/bpn-validation/bpn-validation-api/src/main/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiExtension.java b/edc-extensions/bpn-validation/bpn-validation-api/src/main/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiExtension.java index 868719218..f6bef816f 100644 --- a/edc-extensions/bpn-validation/bpn-validation-api/src/main/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiExtension.java +++ b/edc-extensions/bpn-validation/bpn-validation-api/src/main/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiExtension.java @@ -20,7 +20,6 @@ package org.eclipse.tractusx.edc.api.bpn; import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration; -import org.eclipse.edc.connector.api.management.configuration.transform.ManagementApiTypeTransformerRegistry; import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; @@ -40,8 +39,6 @@ public class BusinessPartnerGroupApiExtension implements ServiceExtension { @Inject private ManagementApiConfiguration apiConfiguration; @Inject - private ManagementApiTypeTransformerRegistry transformerRegistry; - @Inject private JsonLd jsonLdService; @Inject private BusinessPartnerStore businessPartnerStore; diff --git a/edc-extensions/bpn-validation/bpn-validation-api/src/test/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiControllerTest.java b/edc-extensions/bpn-validation/bpn-validation-api/src/test/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiControllerTest.java index 4c067d033..f2a9033b8 100644 --- a/edc-extensions/bpn-validation/bpn-validation-api/src/test/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiControllerTest.java +++ b/edc-extensions/bpn-validation/bpn-validation-api/src/test/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiControllerTest.java @@ -22,7 +22,6 @@ import io.restassured.specification.RequestSpecification; import jakarta.json.Json; import jakarta.json.JsonObject; -import org.eclipse.edc.connector.api.management.configuration.transform.ManagementApiTypeTransformerRegistry; import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.junit.annotations.ApiTest; @@ -38,7 +37,7 @@ import static io.restassured.http.ContentType.JSON; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_PREFIX; import static org.hamcrest.Matchers.notNullValue; @@ -52,7 +51,6 @@ class BusinessPartnerGroupApiControllerTest extends RestControllerTestBase { private final JsonLd jsonLdService = new TitaniumJsonLd(mock()); private final BusinessPartnerStore businessPartnerStore = mock(); - private final ManagementApiTypeTransformerRegistry transformerRegistry = mock(); @BeforeEach void setUp() { diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerNumberValidationExtension.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerNumberValidationExtension.java new file mode 100644 index 000000000..4ecebe5c4 --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerNumberValidationExtension.java @@ -0,0 +1,92 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.validation.businesspartner; + +import org.eclipse.edc.policy.engine.spi.PolicyEngine; +import org.eclipse.edc.policy.engine.spi.RuleBindingRegistry; +import org.eclipse.edc.policy.model.Permission; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerNumberPermissionFunction; + +import static org.eclipse.edc.connector.controlplane.contract.spi.offer.ContractDefinitionResolver.CATALOGING_SCOPE; +import static org.eclipse.edc.connector.controlplane.contract.spi.validation.ContractValidationService.NEGOTIATION_SCOPE; +import static org.eclipse.edc.connector.controlplane.contract.spi.validation.ContractValidationService.TRANSFER_SCOPE; +import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_SCHEMA; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; +import static org.eclipse.tractusx.edc.validation.businesspartner.BusinessPartnerNumberValidationExtension.NAME; + +/** + * Business partner number evaluation function. + */ +@Extension(NAME) +public class BusinessPartnerNumberValidationExtension implements ServiceExtension { + + /** + * The key for business partner numbers constraints. Must be used as left operand when declaring constraints. + *

Example: + * + *

+     * {
+     *     "constraint": {
+     *         "leftOperand": "BusinessPartnerNumber",
+     *         "operator": "EQ",
+     *         "rightOperand": "BPNLCDQ90000X42KU"
+     *     }
+     * }
+     * 
+ */ + public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartnerNumber"; + public static final String TX_BUSINESS_PARTNER_CONSTRAINT_KEY = TX_NAMESPACE + BUSINESS_PARTNER_CONSTRAINT_KEY; + protected static final String NAME = "Business Partner Validation Extension"; + @Inject + private RuleBindingRegistry ruleBindingRegistry; + @Inject + private PolicyEngine policyEngine; + + @Override + public String name() { + return NAME; + } + + @Override + public void initialize(ServiceExtensionContext context) { + + var permissionFunction = new BusinessPartnerNumberPermissionFunction(); + + bindToScope(permissionFunction, TRANSFER_SCOPE); + bindToScope(permissionFunction, NEGOTIATION_SCOPE); + bindToScope(permissionFunction, CATALOGING_SCOPE); + + } + + private void bindToScope(BusinessPartnerNumberPermissionFunction permissionFunction, String scope) { + ruleBindingRegistry.bind("USE", scope); + ruleBindingRegistry.bind(ODRL_SCHEMA + "use", scope); + ruleBindingRegistry.bind(BUSINESS_PARTNER_CONSTRAINT_KEY, scope); + ruleBindingRegistry.bind(TX_BUSINESS_PARTNER_CONSTRAINT_KEY, scope); + + policyEngine.registerFunction(scope, Permission.class, BUSINESS_PARTNER_CONSTRAINT_KEY, permissionFunction); + policyEngine.registerFunction(scope, Permission.class, TX_BUSINESS_PARTNER_CONSTRAINT_KEY, permissionFunction); + } + +} diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java index d7938a777..322620ddc 100644 --- a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java @@ -29,17 +29,17 @@ import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerGroupFunction; import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; -import static org.eclipse.edc.connector.contract.spi.offer.ContractDefinitionResolver.CATALOGING_SCOPE; -import static org.eclipse.edc.connector.contract.spi.validation.ContractValidationService.NEGOTIATION_SCOPE; -import static org.eclipse.edc.connector.contract.spi.validation.ContractValidationService.TRANSFER_SCOPE; +import static org.eclipse.edc.connector.controlplane.contract.spi.offer.ContractDefinitionResolver.CATALOGING_SCOPE; +import static org.eclipse.edc.connector.controlplane.contract.spi.validation.ContractValidationService.NEGOTIATION_SCOPE; +import static org.eclipse.edc.connector.controlplane.contract.spi.validation.ContractValidationService.TRANSFER_SCOPE; import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_SCHEMA; /** * Registers a {@link org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerGroupFunction} for the following scopes: *
    - *
  • {@link org.eclipse.edc.connector.contract.spi.offer.ContractDefinitionResolver#CATALOGING_SCOPE}
  • - *
  • {@link org.eclipse.edc.connector.contract.spi.validation.ContractValidationService#NEGOTIATION_SCOPE}
  • - *
  • {@link org.eclipse.edc.connector.contract.spi.validation.ContractValidationService#TRANSFER_SCOPE}
  • + *
  • {@code catalog}
  • + *
  • {@code contract.negotiation}
  • + *
  • {@code transfer.process}
  • *
* The rule to which the function is bound is {@link BusinessPartnerGroupFunction#BUSINESS_PARTNER_CONSTRAINT_KEY}. That means, that policies that are bound to these scopes look * like this: @@ -57,7 +57,6 @@ */ @Extension(value = "Registers a function to evaluate whether a BPN number is covered by a certain policy or not", categories = { "policy", "contract" }) public class BusinessPartnerValidationExtension implements ServiceExtension { - private static final String USE = "USE"; @Inject private RuleBindingRegistry ruleBindingRegistry; diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/LegacyBusinessPartnerValidationExtension.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/LegacyBusinessPartnerValidationExtension.java deleted file mode 100644 index 0d6cfbe2a..000000000 --- a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/LegacyBusinessPartnerValidationExtension.java +++ /dev/null @@ -1,125 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.validation.businesspartner; - -import org.eclipse.edc.policy.engine.spi.PolicyEngine; -import org.eclipse.edc.policy.engine.spi.RuleBindingRegistry; -import org.eclipse.edc.policy.model.Duty; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.policy.model.Prohibition; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.validation.businesspartner.functions.legacy.BusinessPartnerDutyFunction; -import org.eclipse.tractusx.edc.validation.businesspartner.functions.legacy.BusinessPartnerPermissionFunction; -import org.eclipse.tractusx.edc.validation.businesspartner.functions.legacy.BusinessPartnerProhibitionFunction; - -import static org.eclipse.edc.connector.contract.spi.offer.ContractDefinitionResolver.CATALOGING_SCOPE; -import static org.eclipse.edc.connector.contract.spi.validation.ContractValidationService.NEGOTIATION_SCOPE; -import static org.eclipse.edc.connector.contract.spi.validation.ContractValidationService.TRANSFER_SCOPE; -import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; - -/** - * Business partner number evaluation function. - * - * @deprecated Please use {@code BusinessPartnerEvaluationExtension} instead. - */ -@Deprecated(forRemoval = true, since = "0.5.0") -public class LegacyBusinessPartnerValidationExtension implements ServiceExtension { - - /** - * The key for business partner numbers constraints. Must be used as left operand when declaring constraints. - *

Example: - * - *

-     * {
-     *     "constraint": {
-     *         "leftOperand": "BusinessPartnerNumber",
-     *         "operator": "EQ",
-     *         "rightOperand": "BPNLCDQ90000X42KU"
-     *     }
-     * }
-     * 
- */ - public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartnerNumber"; - - public static final String TX_BUSINESS_PARTNER_CONSTRAINT_KEY = TX_NAMESPACE + BUSINESS_PARTNER_CONSTRAINT_KEY; - - - public static final String DEFAULT_LOG_AGREEMENT_EVALUATION = "true"; - - - @Setting(value = "Enable logging when evaluating the business partner constraints in the agreement validation", type = "boolean", defaultValue = DEFAULT_LOG_AGREEMENT_EVALUATION) - public static final String BUSINESS_PARTNER_VALIDATION_LOG_AGREEMENT_VALIDATION = "tractusx.businesspartnervalidation.log.agreement.validation"; - @Inject - private RuleBindingRegistry ruleBindingRegistry; - @Inject - private PolicyEngine policyEngine; - - public LegacyBusinessPartnerValidationExtension() { - } - - public LegacyBusinessPartnerValidationExtension( - final RuleBindingRegistry ruleBindingRegistry, final PolicyEngine policyEngine) { - this.ruleBindingRegistry = ruleBindingRegistry; - this.policyEngine = policyEngine; - } - - @Override - public String name() { - return "Business Partner Validation Extension"; - } - - @Override - public void initialize(ServiceExtensionContext context) { - - var monitor = context.getMonitor(); - - var logAgreementEvaluation = logAgreementEvaluationSetting(context); - - var dutyFunction = new BusinessPartnerDutyFunction(monitor, logAgreementEvaluation); - var permissionFunction = new BusinessPartnerPermissionFunction(monitor, logAgreementEvaluation); - var prohibitionFunction = new BusinessPartnerProhibitionFunction(monitor, logAgreementEvaluation); - - bindToScope(dutyFunction, permissionFunction, prohibitionFunction, TRANSFER_SCOPE); - bindToScope(dutyFunction, permissionFunction, prohibitionFunction, NEGOTIATION_SCOPE); - bindToScope(dutyFunction, permissionFunction, prohibitionFunction, CATALOGING_SCOPE); - - monitor.warning("This extension was deprecated and is scheduled for removal in version 0.6.0 of Tractus-X EDC"); - } - - private void bindToScope(BusinessPartnerDutyFunction dutyFunction, BusinessPartnerPermissionFunction permissionFunction, BusinessPartnerProhibitionFunction prohibitionFunction, String scope) { - ruleBindingRegistry.bind("USE", scope); - ruleBindingRegistry.bind(BUSINESS_PARTNER_CONSTRAINT_KEY, scope); - ruleBindingRegistry.bind(TX_BUSINESS_PARTNER_CONSTRAINT_KEY, scope); - - - policyEngine.registerFunction(scope, Duty.class, BUSINESS_PARTNER_CONSTRAINT_KEY, dutyFunction); - policyEngine.registerFunction(scope, Permission.class, BUSINESS_PARTNER_CONSTRAINT_KEY, permissionFunction); - policyEngine.registerFunction(scope, Prohibition.class, BUSINESS_PARTNER_CONSTRAINT_KEY, prohibitionFunction); - - policyEngine.registerFunction(scope, Permission.class, TX_BUSINESS_PARTNER_CONSTRAINT_KEY, permissionFunction); - } - - private boolean logAgreementEvaluationSetting(ServiceExtensionContext context) { - return Boolean.parseBoolean(context.getSetting(BUSINESS_PARTNER_VALIDATION_LOG_AGREEMENT_VALIDATION, DEFAULT_LOG_AGREEMENT_EVALUATION)); - } -} diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerGroupFunction.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerGroupFunction.java index 5167d899e..5ad1154ab 100644 --- a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerGroupFunction.java +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerGroupFunction.java @@ -76,7 +76,6 @@ * @see BusinessPartnerStore */ public class BusinessPartnerGroupFunction implements AtomicConstraintFunction { - public static final String REFERRING_CONNECTOR_CLAIM = "referringConnector"; public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = TX_NAMESPACE + "BusinessPartnerGroup"; private static final List ALLOWED_OPERATORS = List.of(EQ, NEQ, IN, IS_ALL_OF, IS_ANY_OF, IS_NONE_OF); private static final Map> OPERATOR_EVALUATOR_MAP = new HashMap<>(); @@ -122,7 +121,7 @@ public boolean evaluate(Operator operator, Object rightValue, Permission rule, P } - var bpn = getBpnClaim(participantAgent); + var bpn = participantAgent.getIdentity(); var groups = store.resolveForBpn(bpn); // BPN not found in database @@ -160,22 +159,7 @@ private List parseRightOperand(Object rightValue, PolicyContext context) context.reportProblem(format("Right operand expected to be either String or a Collection, but was " + rightValue.getClass())); return null; } - - private String getBpnClaim(ParticipantAgent participantAgent) { - String bpnClaim = null; - var claims = participantAgent.getClaims(); - - var bpnClaimObject = claims.get(REFERRING_CONNECTOR_CLAIM); - - if (bpnClaimObject instanceof String) { - bpnClaim = (String) bpnClaimObject; - } - if (bpnClaim == null) { - bpnClaim = participantAgent.getIdentity(); - } - return bpnClaim; - } - + private Boolean evaluateIn(BpnGroupHolder bpnGroupHolder) { var assigned = bpnGroupHolder.assignedGroups; // checks whether both lists overlap diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerNumberPermissionFunction.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerNumberPermissionFunction.java new file mode 100644 index 000000000..a7339c16a --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerNumberPermissionFunction.java @@ -0,0 +1,76 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.validation.businesspartner.functions; + +import org.eclipse.edc.policy.engine.spi.AtomicConstraintFunction; +import org.eclipse.edc.policy.engine.spi.PolicyContext; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.policy.model.Permission; +import org.eclipse.edc.spi.agent.ParticipantAgent; + +import java.util.Optional; + +import static java.lang.String.format; + +/** + * AtomicConstraintFunction to validate business partner numbers for edc permissions. + */ +public class BusinessPartnerNumberPermissionFunction implements AtomicConstraintFunction { + + public BusinessPartnerNumberPermissionFunction() { + } + + @Override + public boolean evaluate(Operator operator, Object rightValue, Permission rule, PolicyContext context) { + + if (operator != Operator.EQ) { + var message = format("As operator only 'EQ' is supported. Unsupported operator: '%s'", operator); + context.reportProblem(message); + return false; + } + + var participantAgent = context.getContextData(ParticipantAgent.class); + if (participantAgent == null) { + context.reportProblem("Required PolicyContext data not found: " + ParticipantAgent.class.getName()); + return false; + } + + var identity = participantAgent.getIdentity(); + if (identity == null) { + context.reportProblem("Identity of the participant agent cannot be null"); + return false; + } + + if ((rightValue instanceof String businessPartnerNumberStr)) { + if (businessPartnerNumberStr.equals(identity)) { + return true; + } else { + context.reportProblem("Identity of the participant not matching the expected one: " + businessPartnerNumberStr); + return false; + } + } else { + var message = format("Invalid right operand value: expected 'String' but got '%s'", + Optional.of(rightValue).map(Object::getClass).map(Class::getName).orElse(null)); + context.reportProblem(message); + return false; + } + + } +} diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/AbstractBusinessPartnerValidation.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/AbstractBusinessPartnerValidation.java deleted file mode 100644 index 93525a0da..000000000 --- a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/AbstractBusinessPartnerValidation.java +++ /dev/null @@ -1,171 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.validation.businesspartner.functions.legacy; - -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.spi.agent.ParticipantAgent; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; -import org.jetbrains.annotations.Nullable; - -import java.util.Objects; - -import static java.lang.String.format; - -/** - * Abstract class for BusinessPartnerNumber validation. This class may be inherited from the EDC - * policy enforcing functions for duties, permissions and prohibitions. - * - * @deprecated Please use {@code BusinessPartnerGroupFunction} instead - */ -@Deprecated(forRemoval = true, since = "0.5.0") -public abstract class AbstractBusinessPartnerValidation { - - // Developer Note: - // Problems reported to the policy context are not logged. Therefore, everything - // that is reported to the policy context should be logged, too. - - private static final String FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING = - "Failing evaluation because of invalid BusinessPartnerNumber constraint. For operator 'EQ' right value must be of type 'String'. Unsupported type: '%s'"; - private static final String FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR = - "Failing evaluation because of invalid BusinessPartnerNumber constraint. As operator only 'EQ' is supported. Unsupported operator: '%s'"; - /** - * Name of the claim that contains the Business Partner Number. - * - *

Please note: At the time of writing (April 2022) the business partner - * number is part of the 'referringConnector' claim in the IDS DAT token. This will probably - * change for the next release. - */ - private static final String REFERRING_CONNECTOR_CLAIM = "referringConnector"; - private final Monitor monitor; - private final boolean logAgreementEvaluation; - - protected AbstractBusinessPartnerValidation(Monitor monitor, boolean logAgreementEvaluation) { - this.monitor = Objects.requireNonNull(monitor); - this.logAgreementEvaluation = logAgreementEvaluation; - } - - public boolean isLogAgreementEvaluation() { - return logAgreementEvaluation; - } - - /** - * Evaluation funtion to decide whether a claim belongs to a specific business partner. - * - * @param operator operator of the constraint - * @param rightValue right value fo the constraint, that contains the business partner number - * (e.g. BPNLCDQ90000X42KU) - * @param policyContext context of the policy with claims - * @return true if claims are from the constrained business partner - */ - public boolean evaluate(Operator operator, Object rightValue, PolicyContext policyContext) { - - monitor.warning("This policy evaluation function (class [%s]) was deprecated and is scheduled for removal in version 0.6.0 of Tractus-X EDC".formatted(getClass().getSimpleName())); - - if (policyContext.hasProblems() && !policyContext.getProblems().isEmpty()) { - var problems = String.join(", ", policyContext.getProblems()); - var message = - format( - "BusinessPartnerNumberValidation: Rejecting PolicyContext with problems. Problems: %s", - problems); - monitor.debug(message); - return false; - } - - var participantAgent = policyContext.getContextData(ParticipantAgent.class); - - if (participantAgent == null) { - return false; - } - var referringConnectorClaim = getReferringConnectorClaim(participantAgent); - - if (referringConnectorClaim == null || referringConnectorClaim.isEmpty()) { - return false; - } - - if (operator == Operator.EQ) { - return isBusinessPartnerNumber(referringConnectorClaim, rightValue, policyContext); - } else { - var message = format(FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR, operator); - monitor.warning(message); - policyContext.reportProblem(message); - return false; - } - } - - /** - * At the time of writing (11. April 2022) the business partner number is part of the - * 'referringConnector' claim, which contains a connector URL. As the CX projects are not further - * aligned about the URL formatting, the enforcement can only be done by checking whether the URL - * _contains_ the number. As this introduces some insecurities when validation business partner - * numbers, this should be addresses in the long term. - * - * @param referringConnectorClaim describing URL with business partner number - * @param businessPartnerNumber of the constraint - * @return true if claim contains the business partner number - */ - private static boolean isCorrectBusinessPartner(String referringConnectorClaim, String businessPartnerNumber) { - return referringConnectorClaim.contains(businessPartnerNumber); - } - - @Nullable - private String getReferringConnectorClaim(ParticipantAgent participantAgent) { - String referringConnectorClaim = null; - var claims = participantAgent.getClaims(); - - var referringConnectorClaimObject = claims.get(REFERRING_CONNECTOR_CLAIM); - - if (referringConnectorClaimObject instanceof String) { - referringConnectorClaim = (String) referringConnectorClaimObject; - } - if (referringConnectorClaim == null) { - referringConnectorClaim = participantAgent.getIdentity(); - } - - return referringConnectorClaim; - } - - private boolean isBusinessPartnerNumber(String referringConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { - if (businessPartnerNumber == null) { - var message = format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, "null"); - monitor.warning(message); - policyContext.reportProblem(message); - return false; - } - if (!(businessPartnerNumber instanceof String businessPartnerNumberStr)) { - var message = - format( - FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, - businessPartnerNumber.getClass().getName()); - monitor.warning(message); - policyContext.reportProblem(message); - return false; - } - - var agreement = policyContext.getContextData(ContractAgreement.class); - var isCorrectBusinessPartner = isCorrectBusinessPartner(referringConnectorClaim, businessPartnerNumberStr); - - if (agreement != null && logAgreementEvaluation) { - monitor.info(format("Evaluated policy access for referringConnectorClaim: %s and contract id: %s with result: %s", referringConnectorClaim, agreement.getId(), isCorrectBusinessPartner)); - } - return isCorrectBusinessPartner; - } -} diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerDutyFunction.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerDutyFunction.java deleted file mode 100644 index 1b8377809..000000000 --- a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerDutyFunction.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.validation.businesspartner.functions.legacy; - -import org.eclipse.edc.policy.engine.spi.AtomicConstraintFunction; -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Duty; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.spi.monitor.Monitor; - -/** - * AtomicConstraintFunction to validate business partner numbers for edc duties. - * - * @deprecated Please use {@code BusinessPartnerGroupFunction} instead - */ -@Deprecated(forRemoval = true, since = "0.5.0") -public class BusinessPartnerDutyFunction extends AbstractBusinessPartnerValidation - implements AtomicConstraintFunction { - - public BusinessPartnerDutyFunction(Monitor monitor, boolean shouldLogOnAgreementEvaluation) { - super(monitor, shouldLogOnAgreementEvaluation); - } - - @Override - public boolean evaluate(Operator operator, Object rightValue, Duty rule, PolicyContext context) { - return evaluate(operator, rightValue, context); - } -} diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerPermissionFunction.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerPermissionFunction.java deleted file mode 100644 index c357af43c..000000000 --- a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerPermissionFunction.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.validation.businesspartner.functions.legacy; - -import org.eclipse.edc.policy.engine.spi.AtomicConstraintFunction; -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.spi.monitor.Monitor; - -/** - * AtomicConstraintFunction to validate business partner numbers for edc permissions. - * - * @deprecated Please use {@code BusinessPartnerGroupFunction} instead - */ -@Deprecated(forRemoval = true, since = "0.5.0") -public class BusinessPartnerPermissionFunction extends AbstractBusinessPartnerValidation - implements AtomicConstraintFunction { - - public BusinessPartnerPermissionFunction(Monitor monitor, boolean shouldLogOnAgreementEvaluation) { - super(monitor, shouldLogOnAgreementEvaluation); - } - - @Override - public boolean evaluate(Operator operator, Object rightValue, Permission rule, PolicyContext context) { - return evaluate(operator, rightValue, context); - } -} diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerProhibitionFunction.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerProhibitionFunction.java deleted file mode 100644 index cc9b4ac8a..000000000 --- a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerProhibitionFunction.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.validation.businesspartner.functions.legacy; - -import org.eclipse.edc.policy.engine.spi.AtomicConstraintFunction; -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.policy.model.Prohibition; -import org.eclipse.edc.spi.monitor.Monitor; - -/** - * AtomicConstraintFunction to validate business partner numbers for edc prohibitions. - * - * @deprecated Please use {@code BusinessPartnerGroupFunction} instead - */ -@Deprecated(forRemoval = true, since = "0.5.0") -public class BusinessPartnerProhibitionFunction extends AbstractBusinessPartnerValidation - implements AtomicConstraintFunction { - - public BusinessPartnerProhibitionFunction(Monitor monitor, boolean shouldLogOnAgreementEvaluation) { - super(monitor, shouldLogOnAgreementEvaluation); - } - - @Override - public boolean evaluate(Operator operator, Object rightValue, Prohibition rule, PolicyContext context) { - return evaluate(operator, rightValue, context); - } -} diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/bpn-validation/bpn-validation-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index e741abd6a..f3e2b4cbc 100644 --- a/edc-extensions/bpn-validation/bpn-validation-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -20,4 +20,4 @@ org.eclipse.tractusx.edc.validation.businesspartner.BusinessPartnerValidationExtension org.eclipse.tractusx.edc.validation.businesspartner.defaults.DefaultStoreProviderExtension -org.eclipse.tractusx.edc.validation.businesspartner.LegacyBusinessPartnerValidationExtension +org.eclipse.tractusx.edc.validation.businesspartner.BusinessPartnerNumberValidationExtension diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerNumberValidationExtensionTest.java b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerNumberValidationExtensionTest.java new file mode 100644 index 000000000..f3d6fa3a8 --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerNumberValidationExtensionTest.java @@ -0,0 +1,83 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.validation.businesspartner; + +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.policy.engine.spi.PolicyEngine; +import org.eclipse.edc.policy.engine.spi.RuleBindingRegistry; +import org.eclipse.edc.policy.model.Permission; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.eclipse.tractusx.edc.validation.businesspartner.BusinessPartnerNumberValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY; +import static org.eclipse.tractusx.edc.validation.businesspartner.BusinessPartnerNumberValidationExtension.TX_BUSINESS_PARTNER_CONSTRAINT_KEY; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(DependencyInjectionExtension.class) +class BusinessPartnerNumberValidationExtensionTest { + + private final PolicyEngine policyEngine = mock(); + private final RuleBindingRegistry ruleBindingRegistry = mock(); + + @BeforeEach + void setup(ServiceExtensionContext context) { + context.registerService(PolicyEngine.class, policyEngine); + context.registerService(RuleBindingRegistry.class, ruleBindingRegistry); + } + + @Test + void testRegisterPermissionFunction(ServiceExtensionContext context, BusinessPartnerNumberValidationExtension extension) { + + // invoke + extension.initialize(context); + + // verify + verify(policyEngine, times(3)) + .registerFunction( + anyString(), + eq(Permission.class), + eq(BUSINESS_PARTNER_CONSTRAINT_KEY), + any()); + + // verify + verify(policyEngine, times(3)) + .registerFunction( + anyString(), + eq(Permission.class), + eq(TX_BUSINESS_PARTNER_CONSTRAINT_KEY), + any()); + + verify(ruleBindingRegistry, times(3)) + .bind(eq(BUSINESS_PARTNER_CONSTRAINT_KEY), + anyString()); + + verify(ruleBindingRegistry, times(3)) + .bind(eq(TX_BUSINESS_PARTNER_CONSTRAINT_KEY), + anyString()); + } + +} diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/LegacyBusinessPartnerValidationExtensionTest.java b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/LegacyBusinessPartnerValidationExtensionTest.java deleted file mode 100644 index 1ffdf61c4..000000000 --- a/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/LegacyBusinessPartnerValidationExtensionTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.validation.businesspartner; - -import org.eclipse.edc.policy.engine.spi.PolicyEngine; -import org.eclipse.edc.policy.engine.spi.RuleBindingRegistry; -import org.eclipse.edc.policy.model.Duty; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.policy.model.Prohibition; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.validation.businesspartner.functions.legacy.BusinessPartnerPermissionFunction; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class LegacyBusinessPartnerValidationExtensionTest { - - private LegacyBusinessPartnerValidationExtension extension; - - // mocks - private ServiceExtensionContext serviceExtensionContext; - private PolicyEngine policyEngine; - private RuleBindingRegistry ruleBindingRegistry; - - @BeforeEach - void setup() { - - policyEngine = mock(PolicyEngine.class); - ruleBindingRegistry = mock(RuleBindingRegistry.class); - - var monitor = mock(Monitor.class); - serviceExtensionContext = mock(ServiceExtensionContext.class); - - when(serviceExtensionContext.getMonitor()).thenReturn(monitor); - - extension = new LegacyBusinessPartnerValidationExtension(ruleBindingRegistry, policyEngine); - } - - @Test - void testRegisterDutyFunction() { - - // invoke - extension.initialize(serviceExtensionContext); - - // verify - verify(policyEngine, times(3)) - .registerFunction( - anyString(), - eq(Duty.class), - eq(LegacyBusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), - any()); - } - - @Test - void testRegisterPermissionFunction() { - - // invoke - extension.initialize(serviceExtensionContext); - - // verify - verify(policyEngine, times(3)) - .registerFunction( - anyString(), - eq(Permission.class), - eq(LegacyBusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), - any()); - } - - @Test - void testRegisterProhibitionFunction() { - - // invoke - extension.initialize(serviceExtensionContext); - - // verify - verify(policyEngine, times(3)) - .registerFunction( - anyString(), - eq(Prohibition.class), - eq(LegacyBusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), - any()); - } - - @Test - void testLogConfiguration() { - - when(serviceExtensionContext.getSetting(LegacyBusinessPartnerValidationExtension.BUSINESS_PARTNER_VALIDATION_LOG_AGREEMENT_VALIDATION, "true")).thenReturn("false"); - - var captor = ArgumentCaptor.forClass(BusinessPartnerPermissionFunction.class); - // invoke - extension.initialize(serviceExtensionContext); - - // verify - verify(policyEngine, times(3)) - .registerFunction( - anyString(), - eq(Permission.class), - eq(LegacyBusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), - captor.capture()); - - assertThat(captor.getValue().isLogAgreementEvaluation()).isFalse(); - } -} diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java deleted file mode 100644 index b330f1074..000000000 --- a/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java +++ /dev/null @@ -1,210 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.validation.businesspartner.functions; - -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.policy.model.Policy; -import org.eclipse.edc.spi.agent.ParticipantAgent; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; -import org.eclipse.tractusx.edc.validation.businesspartner.functions.legacy.AbstractBusinessPartnerValidation; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; - -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; - -class AbstractBusinessPartnerValidationTest { - - private AbstractBusinessPartnerValidation validation; - - // mocks - private Monitor monitor; - private PolicyContext policyContext; - private ParticipantAgent participantAgent; - - @BeforeEach - void beforeEach() { - this.monitor = Mockito.mock(Monitor.class); - this.policyContext = Mockito.mock(PolicyContext.class); - this.participantAgent = Mockito.mock(ParticipantAgent.class); - - Mockito.when(policyContext.getContextData(eq(ParticipantAgent.class))).thenReturn(participantAgent); - - validation = new AbstractBusinessPartnerValidation(monitor, true) { - }; - } - - @ParameterizedTest - @EnumSource(Operator.class) - void testFailsOnUnsupportedOperations(Operator operator) { - - if (operator == Operator.EQ) { // only allowed operator - return; - } - - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("yes"); - - // invoke & assert - Assertions.assertFalse(validation.evaluate(operator, "foo", policyContext)); - } - - @Test - void testFailsOnUnsupportedRightValue() { - - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("yes"); - - // invoke & assert - Assertions.assertFalse(validation.evaluate(Operator.EQ, 1, policyContext)); - } - - @Test - void testValidationFailsWhenClaimMissing() { - - // prepare - prepareContextProblems(null); - - // invoke - final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); - - // assert - Assertions.assertFalse(isValid); - } - - @Test - void testValidationSucceedsWhenClaimContainsValue() { - - // prepare - prepareContextProblems(null); - - // prepare equals - prepareBusinessPartnerClaim("foo"); - final boolean isEqualsTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - - // prepare contains - prepareBusinessPartnerClaim("foobar"); - final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - - // assert - Assertions.assertTrue(isEqualsTrue); - Assertions.assertTrue(isContainedTrue); - } - - @Test - void testValidationWhenParticipantHasProblems() { - - // prepare - prepareContextProblems(Collections.singletonList("big problem")); - prepareBusinessPartnerClaim("foo"); - - // invoke - final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); - - // Mockito.verify(monitor.debug(Mockito.anyString()); - Assertions.assertFalse(isValid); - } - - @Test - void testValidationWhenSingleParticipantIsValid() { - - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("foo"); - - // invoke - final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - - // Mockito.verify(monitor.debug(Mockito.anyString()); - Assertions.assertTrue(isContainedTrue); - } - - @Test - void testValidationWhenSingleParticipantIsValidWithAgreement() { - - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("foo"); - - var captor = ArgumentCaptor.forClass(String.class); - - var agreement = ContractAgreement.Builder.newInstance() - .id("agreementId") - .providerId("provider") - .consumerId("consumer") - .assetId("assetId") - .policy(Policy.Builder.newInstance().build()) - .build(); - - Mockito.when(policyContext.getContextData(eq(ContractAgreement.class))).thenReturn(agreement); - - // invoke - final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - - Assertions.assertTrue(isContainedTrue); - - Mockito.verify(monitor).info(captor.capture()); - - assertThat(captor.getValue()).contains(agreement.getId()).contains("foo"); - } - - // In the past it was possible to use the 'IN' constraint with multiple BPNs as - // a list. This is no longer supported. - // The EDC must now always decline this kind of BPN format. - @Test - void testValidationForMultipleParticipants() { - - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("foo"); - - // invoke & verify - Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("foo", "bar"), policyContext)); - Assertions.assertFalse(validation.evaluate(Operator.IN, List.of(1, "foo"), policyContext)); - Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("bar", "bar"), policyContext)); - } - - private void prepareContextProblems(List problems) { - Mockito.when(policyContext.getProblems()).thenReturn(problems); - - if (problems == null || problems.isEmpty()) { - Mockito.when(policyContext.hasProblems()).thenReturn(false); - } else { - Mockito.when(policyContext.hasProblems()).thenReturn(true); - } - } - - private void prepareBusinessPartnerClaim(String businessPartnerNumber) { - Mockito.when(participantAgent.getClaims()) - .thenReturn(Collections.singletonMap("referringConnector", businessPartnerNumber)); - } -} diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerGroupFunctionTest.java b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerGroupFunctionTest.java index c318ca7ee..0d982037c 100644 --- a/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerGroupFunctionTest.java +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerGroupFunctionTest.java @@ -53,8 +53,8 @@ import static org.eclipse.edc.policy.model.Operator.LEQ; import static org.eclipse.edc.policy.model.Operator.LT; import static org.eclipse.edc.policy.model.Operator.NEQ; +import static org.eclipse.edc.spi.agent.ParticipantAgent.PARTICIPANT_IDENTITY; import static org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerGroupFunction.BUSINESS_PARTNER_CONSTRAINT_KEY; -import static org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerGroupFunction.REFERRING_CONNECTOR_CLAIM; import static org.mockito.ArgumentMatchers.endsWith; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -97,8 +97,8 @@ void evaluate_invalidOperator(Operator invalidOperator) { @Test @DisplayName("Right-hand operand is not String or Collection") void evaluate_rightOperandNotStringOrCollection() { - when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(new ParticipantAgent(Map.of(REFERRING_CONNECTOR_CLAIM, TEST_BPN), Map.of())); when(store.resolveForBpn(TEST_BPN)).thenReturn(StoreResult.success(List.of("test-group"))); + when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(new ParticipantAgent(Map.of(), Map.of(PARTICIPANT_IDENTITY, TEST_BPN))); assertThat(function.evaluate(EQ, 42, createPermission(EQ, List.of("test-group")), context)).isFalse(); assertThat(function.evaluate(EQ, 42L, createPermission(EQ, List.of("test-group")), context)).isFalse(); @@ -117,7 +117,7 @@ void evaluate_rightOperandNotStringOrCollection() { void evaluate_validOperator(String ignored, Operator operator, List assignedBpn, boolean expectedOutcome) { var allowedGroups = List.of(TEST_GROUP_1, TEST_GROUP_2); - when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(new ParticipantAgent(Map.of(REFERRING_CONNECTOR_CLAIM, TEST_BPN), Map.of())); + when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(new ParticipantAgent(Map.of(), Map.of(PARTICIPANT_IDENTITY, TEST_BPN))); when(store.resolveForBpn(TEST_BPN)).thenReturn(StoreResult.success(assignedBpn)); assertThat(function.evaluate(operator, allowedGroups, createPermission(operator, allowedGroups), context)).isEqualTo(expectedOutcome); } @@ -126,7 +126,7 @@ void evaluate_validOperator(String ignored, Operator operator, List assi void evaluate_noEntryForBpn() { var operator = NEQ; var allowedGroups = List.of(TEST_GROUP_1, TEST_GROUP_2); - when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(new ParticipantAgent(Map.of(REFERRING_CONNECTOR_CLAIM, TEST_BPN), Map.of())); + when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(new ParticipantAgent(Map.of(), Map.of(PARTICIPANT_IDENTITY, TEST_BPN))); when(store.resolveForBpn(TEST_BPN)).thenReturn(StoreResult.notFound("foobar")); assertThat(function.evaluate(operator, allowedGroups, createPermission(operator, allowedGroups), context)).isFalse(); @@ -136,7 +136,7 @@ void evaluate_noEntryForBpn() { void evaluate_noGroupsAssignedToBpn() { var operator = NEQ; var allowedGroups = List.of(TEST_GROUP_1, TEST_GROUP_2); - when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(new ParticipantAgent(Map.of(REFERRING_CONNECTOR_CLAIM, TEST_BPN), Map.of())); + when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(new ParticipantAgent(Map.of(), Map.of(PARTICIPANT_IDENTITY, TEST_BPN))); when(store.resolveForBpn(TEST_BPN)).thenReturn(StoreResult.success(Collections.emptyList())); assertThat(function.evaluate(operator, allowedGroups, createPermission(operator, allowedGroups), context)).isFalse(); diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerNumberPermissionFunctionTest.java b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerNumberPermissionFunctionTest.java new file mode 100644 index 000000000..67cff19fa --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerNumberPermissionFunctionTest.java @@ -0,0 +1,111 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.validation.businesspartner.functions; + +import org.eclipse.edc.policy.engine.spi.PolicyContext; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.policy.model.Permission; +import org.eclipse.edc.spi.agent.ParticipantAgent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class BusinessPartnerNumberPermissionFunctionTest { + + private BusinessPartnerNumberPermissionFunction validation; + + private PolicyContext policyContext; + private ParticipantAgent participantAgent; + + private Permission permission = mock(); + + @BeforeEach + void beforeEach() { + this.policyContext = mock(PolicyContext.class); + this.participantAgent = mock(ParticipantAgent.class); + + when(policyContext.getContextData(eq(ParticipantAgent.class))).thenReturn(participantAgent); + + validation = new BusinessPartnerNumberPermissionFunction() { + }; + } + + @ParameterizedTest + @EnumSource(Operator.class) + void testFailsOnUnsupportedOperations(Operator operator) { + if (operator == Operator.EQ) { // only allowed operator + return; + } + assertFalse(validation.evaluate(operator, "foo", permission, policyContext)); + verify(policyContext).reportProblem(argThat(message -> message.contains("As operator only 'EQ' is supported"))); + } + + @Test + void testFailsOnUnsupportedRightValue() { + when(participantAgent.getIdentity()).thenReturn("foo"); + assertFalse(validation.evaluate(Operator.EQ, 1, permission, policyContext)); + verify(policyContext).reportProblem(argThat(message -> message.contains("Invalid right operand value: expected 'String' but got"))); + } + + @Test + void testValidationFailsIdentityIsMissing() { + assertThat(validation.evaluate(Operator.EQ, "foo", permission, policyContext)).isFalse(); + verify(policyContext).reportProblem(argThat(message -> message.contains("Identity of the participant agent cannot be null"))); + } + + @Test + void testValidationFailsParticipantAgentMissing() { + var context = mock(PolicyContext.class); + assertThat(validation.evaluate(Operator.EQ, "foo", permission, context)).isFalse(); + verify(context).reportProblem(argThat(message -> message.contains("Required PolicyContext data not found"))); + } + + @Test + void testValidationWhenSingleParticipantIsValid() { + when(participantAgent.getIdentity()).thenReturn("foo"); + assertThat(validation.evaluate(Operator.EQ, "foo", permission, policyContext)).isTrue(); + } + + @Test + void testValidationFailsInvalidIdentity() { + when(participantAgent.getIdentity()).thenReturn("bar"); + assertThat(validation.evaluate(Operator.EQ, "foo", permission, policyContext)).isFalse(); + verify(policyContext).reportProblem(argThat(message -> message.contains("Identity of the participant not matching the expected one: foo"))); + } + + @Test + void testValidationForMultipleParticipants() { + + assertFalse(validation.evaluate(Operator.IN, List.of("foo", "bar"), permission, policyContext)); + assertFalse(validation.evaluate(Operator.IN, List.of(1, "foo"), permission, policyContext)); + assertFalse(validation.evaluate(Operator.IN, List.of("bar", "bar"), permission, policyContext)); + } +} diff --git a/edc-extensions/bpn-validation/business-partner-store-sql/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/SqlBusinessPartnerStoreTest.java b/edc-extensions/bpn-validation/business-partner-store-sql/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/SqlBusinessPartnerStoreTest.java index c8af6d7fe..cd3e83c1d 100644 --- a/edc-extensions/bpn-validation/business-partner-store-sql/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/SqlBusinessPartnerStoreTest.java +++ b/edc-extensions/bpn-validation/business-partner-store-sql/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/SqlBusinessPartnerStoreTest.java @@ -19,6 +19,7 @@ package org.eclipse.tractusx.edc.validation.businesspartner.store.sql; +import org.eclipse.edc.junit.annotations.PostgresqlIntegrationTest; import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.sql.QueryExecutor; import org.eclipse.edc.sql.testfixtures.PostgresqlStoreSetupExtension; @@ -32,6 +33,7 @@ import java.nio.file.Files; import java.nio.file.Paths; +@PostgresqlIntegrationTest @ExtendWith(PostgresqlStoreSetupExtension.class) class SqlBusinessPartnerStoreTest extends BusinessPartnerStoreTestBase { private final TypeManager typeManager = new TypeManager(); diff --git a/edc-extensions/build.gradle.kts b/edc-extensions/build.gradle.kts deleted file mode 100644 index d6b1be67f..000000000 --- a/edc-extensions/build.gradle.kts +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************** - * Copyright (c) 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 - ********************************************************************************/ - -plugins { - `java-library` -} - -dependencies { - implementation(project(":edc-extensions:bpn-validation")) - implementation(project(":edc-extensions:data-encryption")) - implementation(project(":edc-extensions:dataplane-selector-configuration")) - implementation(project(":edc-extensions:postgresql-migration")) - implementation(project(":edc-extensions:provision-additional-headers")) - implementation(project(":edc-extensions:transferprocess-sftp-client")) - implementation(project(":edc-extensions:transferprocess-sftp-common")) - implementation(project(":edc-extensions:transferprocess-sftp-provisioner")) -} diff --git a/edc-extensions/cx-policy/build.gradle.kts b/edc-extensions/cx-policy/build.gradle.kts index 4e26c94bd..b8d56b1ec 100644 --- a/edc-extensions/cx-policy/build.gradle.kts +++ b/edc-extensions/cx-policy/build.gradle.kts @@ -23,10 +23,11 @@ plugins { dependencies { implementation(project(":spi:core-spi")) - implementation(project(":spi:ssi-spi")) + implementation(project(":core:core-utils")) implementation(libs.edc.spi.policyengine) implementation(libs.jakartaJson) + implementation(libs.edc.spi.identitytrust) + implementation(libs.edc.spi.vc) testImplementation(libs.jacksonJsonP) testImplementation(libs.titaniumJsonLd) - testImplementation(testFixtures(project(":spi:ssi-spi"))) } diff --git a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/CxPolicyExtension.java b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/CxPolicyExtension.java index d36664024..c8e93c4ed 100644 --- a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/CxPolicyExtension.java +++ b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/CxPolicyExtension.java @@ -25,8 +25,9 @@ import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; -import static org.eclipse.tractusx.edc.policy.cx.summary.SummaryConstraintFunctionsProvider.registerBindings; -import static org.eclipse.tractusx.edc.policy.cx.summary.SummaryConstraintFunctionsProvider.registerFunctions; +import static org.eclipse.tractusx.edc.policy.cx.CxPolicyRegistration.registerBindings; +import static org.eclipse.tractusx.edc.policy.cx.CxPolicyRegistration.registerFunctions; + /** * Provides implementations of standard CX usage policies. diff --git a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/CxPolicyRegistration.java b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/CxPolicyRegistration.java new file mode 100644 index 000000000..70c2a6960 --- /dev/null +++ b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/CxPolicyRegistration.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.policy.cx; + +import org.eclipse.edc.policy.engine.spi.PolicyEngine; +import org.eclipse.edc.policy.engine.spi.RuleBindingRegistry; +import org.eclipse.edc.policy.model.Permission; +import org.eclipse.tractusx.edc.policy.cx.dismantler.DismantlerCredentialConstraintFunction; +import org.eclipse.tractusx.edc.policy.cx.framework.FrameworkAgreementCredentialConstraintFunction; +import org.eclipse.tractusx.edc.policy.cx.membership.MembershipCredentialConstraintFunction; + +import java.util.Set; +import java.util.stream.Stream; + +import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_SCHEMA; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_POLICY_NS; +import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.CATALOG_REQUEST_SCOPE; +import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.CATALOG_SCOPE; +import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.NEGOTIATION_REQUEST_SCOPE; +import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.NEGOTIATION_SCOPE; +import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.TRANSFER_PROCESS_REQUEST_SCOPE; +import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.TRANSFER_PROCESS_SCOPE; +import static org.eclipse.tractusx.edc.policy.cx.dismantler.DismantlerCredentialConstraintFunction.DISMANTLER_LITERAL; +import static org.eclipse.tractusx.edc.policy.cx.framework.FrameworkAgreementCredentialConstraintFunction.FRAMEWORK_AGREEMENT_LITERAL; +import static org.eclipse.tractusx.edc.policy.cx.membership.MembershipCredentialConstraintFunction.MEMBERSHIP_LITERAL; + +public class CxPolicyRegistration { + private static final Set FUNCTION_SCOPES = Set.of(CATALOG_SCOPE, NEGOTIATION_SCOPE, TRANSFER_PROCESS_SCOPE); + private static final Set RULE_SCOPES = Set.of(CATALOG_REQUEST_SCOPE, NEGOTIATION_REQUEST_SCOPE, TRANSFER_PROCESS_REQUEST_SCOPE, CATALOG_SCOPE, NEGOTIATION_SCOPE, TRANSFER_PROCESS_SCOPE); + + public static void registerFunctions(PolicyEngine engine) { + FUNCTION_SCOPES.forEach(scope -> { + engine.registerFunction(scope, Permission.class, new DismantlerCredentialConstraintFunction()); + engine.registerFunction(scope, Permission.class, new MembershipCredentialConstraintFunction()); + engine.registerFunction(scope, Permission.class, new FrameworkAgreementCredentialConstraintFunction()); + }); + } + + public static void registerBindings(RuleBindingRegistry registry) { + registry.dynamicBind(s -> { + if (Stream.of(FRAMEWORK_AGREEMENT_LITERAL, DISMANTLER_LITERAL, MEMBERSHIP_LITERAL).anyMatch(postfix -> s.startsWith(CX_POLICY_NS + postfix))) { + return RULE_SCOPES; + } + return Set.of(); + }); + + registry.bind(ODRL_SCHEMA + "use", CATALOG_SCOPE); + registry.bind(ODRL_SCHEMA + "use", NEGOTIATION_SCOPE); + registry.bind(ODRL_SCHEMA + "use", TRANSFER_PROCESS_SCOPE); + } +} diff --git a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/common/AbstractDynamicCredentialConstraintFunction.java b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/common/AbstractDynamicCredentialConstraintFunction.java new file mode 100644 index 000000000..91d08d29c --- /dev/null +++ b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/common/AbstractDynamicCredentialConstraintFunction.java @@ -0,0 +1,80 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.policy.cx.common; + +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential; +import org.eclipse.edc.policy.engine.spi.DynamicAtomicConstraintFunction; +import org.eclipse.edc.policy.engine.spi.PolicyContext; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.policy.model.Permission; +import org.eclipse.edc.spi.agent.ParticipantAgent; +import org.eclipse.edc.spi.result.Result; + +import java.util.Collection; +import java.util.List; + +/** + * This is a base class for dynamically bound Tractus-X constraint evaluation functions that implements some basic common functionality and defines some + * common constants + */ +public abstract class AbstractDynamicCredentialConstraintFunction implements DynamicAtomicConstraintFunction { + public static final String VC_CLAIM = "vc"; + public static final String ACTIVE = "active"; + public static final String CREDENTIAL_LITERAL = "Credential"; + protected static final Collection EQUALITY_OPERATORS = List.of(Operator.EQ, Operator.NEQ); + + protected boolean checkOperator(Operator actual, PolicyContext context, Collection expectedOperators) { + if (!expectedOperators.contains(actual)) { + context.reportProblem("Invalid operator: this constraint only allows the following operators: %s, but received '%s'.".formatted(EQUALITY_OPERATORS, actual)); + return false; + } + return true; + } + + protected Result extractParticipantAgent(PolicyContext context) { + // make sure the ParticipantAgent is there + var participantAgent = context.getContextData(ParticipantAgent.class); + if (participantAgent == null) { + return Result.failure("Required PolicyContext data not found: " + ParticipantAgent.class.getName()); + } + return Result.success(participantAgent); + } + + /** + * Extracts a {@link List} of {@link VerifiableCredential} objects from the {@link ParticipantAgent}. Credentials must be + * stored in the agent's claims map using the "vc" key. + */ + protected Result> getCredentialList(ParticipantAgent agent) { + var vcListClaim = agent.getClaims().get(VC_CLAIM); + + if (vcListClaim == null) { + return Result.failure("ParticipantAgent did not contain a '%s' claim.".formatted(VC_CLAIM)); + } + if (!(vcListClaim instanceof List)) { + return Result.failure("ParticipantAgent contains a '%s' claim, but the type is incorrect. Expected %s, received %s.".formatted(VC_CLAIM, List.class.getName(), vcListClaim.getClass().getName())); + } + var vcList = (List) vcListClaim; + if (vcList.isEmpty()) { + return Result.failure("ParticipantAgent contains a '%s' claim but it did not contain any VerifiableCredentials.".formatted(VC_CLAIM)); + } + return Result.success(vcList); + } + +} diff --git a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/common/AbstractVpConstraintFunction.java b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/common/AbstractVpConstraintFunction.java deleted file mode 100644 index f0a955f8f..000000000 --- a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/common/AbstractVpConstraintFunction.java +++ /dev/null @@ -1,130 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.policy.cx.common; - -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; -import org.eclipse.edc.policy.engine.spi.AtomicConstraintFunction; -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdFieldExtractor; -import org.jetbrains.annotations.Nullable; - -import java.util.stream.Collectors; - -import static jakarta.json.JsonValue.ValueType.OBJECT; -import static java.lang.String.format; -import static java.util.Arrays.stream; -import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.joining; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CREDENTIAL_SUBJECT; - -/** - * Base processing for constraint functions that verify a permission against a Catena-X verifiable presentation. - */ -public abstract class AbstractVpConstraintFunction implements AtomicConstraintFunction { - - protected static final String VALUE = "@value"; - private static final String ERROR_PREFIX_TEMPLATE = "Invalid %s VC format: "; - protected final String errorPrefix; - protected final String credentialType; - private JsonLdFieldExtractor credentialSubjectExtractor = JsonLdFieldExtractor.Builder.newInstance() - .field(CREDENTIAL_SUBJECT) - .fieldAlias("credentialSubject") - .build(); - - /** - * Ctor. - * - * @param credentialType the credential type that will be verified against. - */ - public AbstractVpConstraintFunction(String credentialType) { - requireNonNull(credentialType); - this.credentialType = credentialType; - this.errorPrefix = format(ERROR_PREFIX_TEMPLATE, credentialType); - this.credentialSubjectExtractor = JsonLdFieldExtractor.Builder.newInstance() - .field(CREDENTIAL_SUBJECT) - .fieldAlias("credentialSubject") - .errorPrefix(errorPrefix) - .build(); - } - - /** - * Validates the operator is in the set of expected operators. - */ - protected boolean validateOperator(Operator operator, PolicyContext context, Operator... expectedOperators) { - var set = stream(expectedOperators).collect(Collectors.toSet()); - if (!set.contains(operator)) { - var valid = set.stream().map(Enum::toString).collect(joining(",")); - context.reportProblem(format("Unsupported operator for %s credential constraint, only %s allowed: %s", credentialType, valid, operator)); - return false; - } - return true; - } - - /** - * Validates the VP by checking that it is a {@link JsonObject}. - */ - protected boolean validatePresentation(@Nullable Object vp, PolicyContext context) { - if (vp == null) { - context.reportProblem(format("%s VP not found", credentialType)); - return false; - } - - if (!(vp instanceof JsonValue jsonValue)) { - context.reportProblem(format("%s VP is not a JSON type: %s", credentialType, vp.getClass().getName())); - return false; - } - - if (!(OBJECT == jsonValue.getValueType())) { - context.reportProblem(format("%s VP must be type %s but was: %s", credentialType, OBJECT, jsonValue.getValueType())); - return false; - } - - return true; - } - - /** - * Returns the credential subject portion of a VC or null if there was an error. Error information will be reported to the context. - */ - @Nullable - protected JsonObject extractCredentialSubject(JsonObject credential, PolicyContext context) { - return credentialSubjectExtractor.extract(credential).onFailure(failure -> context.reportProblem(failure.getFailureDetail())).getContent(); - } - - /** - * Returns true if the actual operand value is a string literal case-insensitive equal to the expected value. - */ - protected boolean validateRightOperand(String expectedValue, Object actualValue, PolicyContext context) { - if (!(actualValue instanceof String)) { - context.reportProblem(format("Invalid right operand format specified for %s credential", credentialType)); - return false; - } - - if (!expectedValue.equalsIgnoreCase(actualValue.toString().trim())) { - context.reportProblem(format("Invalid right operand specified for %s credential: %s", credentialType, actualValue)); - return false; - } - - return true; - } - -} diff --git a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/dismantler/DismantlerCredentialConstraintFunction.java b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/dismantler/DismantlerCredentialConstraintFunction.java new file mode 100644 index 000000000..12c319854 --- /dev/null +++ b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/dismantler/DismantlerCredentialConstraintFunction.java @@ -0,0 +1,184 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.policy.cx.dismantler; + +import jakarta.json.JsonObject; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential; +import org.eclipse.edc.policy.engine.spi.PolicyContext; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.policy.model.Permission; +import org.eclipse.tractusx.edc.core.utils.credentials.CredentialTypePredicate; +import org.eclipse.tractusx.edc.policy.cx.common.AbstractDynamicCredentialConstraintFunction; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.function.Predicate; + +import static org.eclipse.edc.policy.model.Operator.EQ; +import static org.eclipse.edc.policy.model.Operator.IN; +import static org.eclipse.edc.policy.model.Operator.IS_ANY_OF; +import static org.eclipse.edc.policy.model.Operator.IS_NONE_OF; +import static org.eclipse.edc.policy.model.Operator.NEQ; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_CREDENTIAL_NS; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_POLICY_NS; + +/** + * Enforces a Dismantler constraint. This function can check for these properties: + *

    + *
  • presence: whether a Dismantler credential is present or not
  • + *
  • activityType: whether an existing DismantlerCredential permits the activity types required by the constraint
  • + *
  • allowedBrands: whether an existing DismantlerCredential permits the vehicle brands required by the constraint
  • + *
+ */ +public class DismantlerCredentialConstraintFunction extends AbstractDynamicCredentialConstraintFunction { + + public static final String ALLOWED_VEHICLE_BRANDS = CX_CREDENTIAL_NS + "allowedVehicleBrands"; + public static final String DISMANTLER_LITERAL = "Dismantler"; + // allows to encode multiple values in a single string in the right-operand. Policies don't handle list-type right-operands well. + public static final String RIGHT_OPERAND_LIST_SEPARATOR = ","; + private static final String ALLOWED_ACTIVITIES = CX_CREDENTIAL_NS + "activityType"; + + @Override + public boolean evaluate(Object leftOperand, Operator operator, Object rightOperand, Permission permission, PolicyContext context) { + Predicate predicate; + + // make sure the ParticipantAgent is there + var participantAgent = extractParticipantAgent(context); + if (participantAgent.failed()) { + context.reportProblem(participantAgent.getFailureDetail()); + return false; + } + + // check if the participant agent contains the correct data + var vcListResult = getCredentialList(participantAgent.getContent()); + if (vcListResult.failed()) { // couldn't extract credential list from agent + context.reportProblem(vcListResult.getFailureDetail()); + return false; + } + + // always filter for DismantlerCredential type + predicate = new CredentialTypePredicate(CX_CREDENTIAL_NS, DISMANTLER_LITERAL + CREDENTIAL_LITERAL); + + + if (rightOperand.toString().contains(RIGHT_OPERAND_LIST_SEPARATOR)) { + rightOperand = getList(rightOperand); + } + + if (leftOperand.equals(CX_POLICY_NS + DISMANTLER_LITERAL)) { // only checks for presence + if (!checkOperator(operator, context, EQUALITY_OPERATORS)) { + return false; + } + if (!ACTIVE.equals(rightOperand)) { + context.reportProblem("Right-operand must be equal to '%s', but was '%s'".formatted(ACTIVE, rightOperand)); + return false; + } + if (operator == NEQ) { + predicate = predicate.negate(); + } + + } else if (leftOperand.equals(CX_POLICY_NS + DISMANTLER_LITERAL + ".activityType")) { + if (hasInvalidOperand(operator, rightOperand, context)) return false; + predicate = predicate.and(getCredentialPredicate(ALLOWED_ACTIVITIES, operator, rightOperand)); + } else if (leftOperand.equals(CX_POLICY_NS + DISMANTLER_LITERAL + ".allowedBrands")) { + if (hasInvalidOperand(operator, rightOperand, context)) return false; + predicate = predicate.and(getCredentialPredicate(ALLOWED_VEHICLE_BRANDS, operator, rightOperand)); + } else { + context.reportProblem("Invalid left-operand: must be 'Dismantler[.activityType | .allowedBrands ], but was '%s'".formatted(leftOperand)); + return false; + } + + return vcListResult.getContent().stream().anyMatch(predicate); + } + + @Override + public boolean canHandle(Object leftOperand) { + return leftOperand instanceof String && ((String) leftOperand).startsWith(CX_POLICY_NS + DISMANTLER_LITERAL); + } + + /** + * Creates a {@link Predicate} based on the {@code rightOperand} that tests whether whatever property is extracted from the {@link VerifiableCredential} + * is valid, according to the operator. For example {@link Operator#IS_ALL_OF} would check that the list from the constraint (= rightOperand) intersects with the list + * stored in the claim identified by {@code credentialSubjectProperty} in the {@link VerifiableCredential#getCredentialSubject()}. + * + * @param credentialSubjectProperty The name of the claim to be extracted from the {@link VerifiableCredential#getCredentialSubject()} + * @param operator the operator + * @param rightOperand The constraint value (i.e. policy expression right-operand) + * @return A predicate that tests a {@link VerifiableCredential} for the constraint + */ + @NotNull + private Predicate getCredentialPredicate(String credentialSubjectProperty, Operator operator, Object rightOperand) { + var allowedValues = getList(rightOperand); + // the filter predicate is determined by the operator + return credential -> credential.getCredentialSubject().stream().anyMatch(subject -> { + var claimsFromCredential = getList(subject.getClaims().getOrDefault(credentialSubjectProperty, List.of())); + return switch (operator) { + case EQ -> claimsFromCredential.equals(allowedValues); + case NEQ -> !claimsFromCredential.equals(allowedValues); + case IN -> + new HashSet<>(allowedValues).containsAll(claimsFromCredential); //IntelliJ says Hashset has better performance + case IS_ANY_OF -> !intersect(allowedValues, claimsFromCredential).isEmpty(); + case IS_NONE_OF -> intersect(allowedValues, claimsFromCredential).isEmpty(); + default -> false; + }; + }); + } + + /** + * Checks whether {@code operator} is valid in the context of {@code rightOperand}. In practice, this means that if {@code rightOperand} is a String, it checks for {@link AbstractDynamicCredentialConstraintFunction#EQUALITY_OPERATORS}, + * and if its list type, it checks for {@code List.of(EQ, NEQ, IN, IS_ANY_OF, IS_NONE_OF)} + */ + private boolean hasInvalidOperand(Operator operator, Object rightOperand, PolicyContext context) { + if (rightOperand instanceof String) { + return !checkOperator(operator, context, EQUALITY_OPERATORS); + } else if (rightOperand instanceof Iterable) { + return !checkOperator(operator, context, List.of(EQ, NEQ, IN, IS_ANY_OF, IS_NONE_OF)); + } else { + context.reportProblem("Invalid right-operand type: expected String or List, but received: %s".formatted(rightOperand.getClass().getName())); + return true; + } + } + + /** + * Checks whether two lists "intersect", i.e. that at least one element is found in both lists. + * Caution: {@code list1} may be modified! + */ + private List intersect(List list1, List list2) { + list1.retainAll(list2); + return list1; + } + + private List getList(Object object) { + if (object instanceof Iterable iterable) { + var list = new ArrayList<>(); + + iterable.iterator().forEachRemaining(element -> { + if (element instanceof JsonObject jo) { // workaround: lists in policy right-operands are not properly deserialized + list.add(jo.getString("@value")); + } else { + list.add(element); + } + }); + return list; + } + return List.of(object.toString().split(RIGHT_OPERAND_LIST_SEPARATOR)); // in case multiple values are encoded in a single string + } +} diff --git a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/framework/FrameworkAgreementConstraintFunction.java b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/framework/FrameworkAgreementConstraintFunction.java deleted file mode 100644 index 2914cc3d1..000000000 --- a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/framework/FrameworkAgreementConstraintFunction.java +++ /dev/null @@ -1,181 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.policy.cx.framework; - -import jakarta.json.JsonObject; -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.spi.agent.ParticipantAgent; -import org.eclipse.tractusx.edc.policy.cx.common.AbstractVpConstraintFunction; - -import java.util.Objects; - -import static java.lang.String.format; -import static java.util.Objects.requireNonNull; -import static org.eclipse.edc.policy.model.Operator.EQ; -import static org.eclipse.edc.policy.model.Operator.GEQ; -import static org.eclipse.edc.policy.model.Operator.GT; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_USE_CASE_NS; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTypeFunctions.extractObjectsOfType; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdValueFunctions.extractStringValue; - - -/** - * Enforces a Framework Agreement constraint. - *

- * A policy constraints requiring a usecase framework credential take a left operand in the form: - *

FrameworkAgreement.[type]
- *

- * The following example requires a client to present a sustainability credential: - *

- * "constraint": {
- *     "leftOperand": "FrameworkAgreement.sustainability",
- *     "operator": "eq",
- *     "rightOperand": "active"
- * }
- * 
- *

- * NB: This function will be enabled in the 3.2 release. - */ -public class FrameworkAgreementConstraintFunction extends AbstractVpConstraintFunction { - public static final String CONTRACT_VERSION_PROPERTY = CX_USE_CASE_NS + "/contractVersion"; - private static final String ACTIVE = "active"; - private String agreementType; - private String agreementVersion; - - private FrameworkAgreementConstraintFunction(String credentialType) { - super(credentialType); - } - - @Override - public boolean evaluate(Operator operator, Object rightValue, Permission permission, PolicyContext context) { - if (!validateOperator(operator, context, EQ, GT, GEQ)) { - return false; - } - - if (!validateRightOperand(ACTIVE, rightValue, context)) { - return false; - } - - var vp = (JsonObject) context.getContextData(ParticipantAgent.class).getClaims().get(VP_PROPERTY); - if (!validatePresentation(vp, context)) { - return false; - } - - return extractObjectsOfType(credentialType, vp) - .map(credential -> extractCredentialSubject(credential, context)) - .filter(Objects::nonNull) - .anyMatch(credentialSubject -> validateUseCase(credentialSubject, operator, context)); - } - - private boolean validateUseCase(JsonObject credentialSubject, Operator operator, PolicyContext context) { - var usecaseAgreement = extractObjectsOfType(agreementType, credentialSubject).findFirst().orElse(null); - if (usecaseAgreement == null) { - context.reportProblem(format("%s is missing the usecase type: %s", credentialType, agreementType)); - return false; - } - - return validateVersion(context, operator, usecaseAgreement); - } - - private boolean validateVersion(PolicyContext context, Operator operator, JsonObject usecaseAgreement) { - if (agreementVersion == null) { - return true; - } - var version = extractStringValue(usecaseAgreement.get(CONTRACT_VERSION_PROPERTY)); - if (version == null || version.trim().length() == 0) { - context.reportProblem(format("%s is missing a %s property", credentialType, CONTRACT_VERSION_PROPERTY)); - return false; - } - - switch (operator) { - case EQ -> { - if (!version.equals(agreementVersion)) { - context.reportProblem(format("%s version %s does not match required version: %s", credentialType, version, agreementVersion)); - return false; - } - return true; - } - case GT -> { - if (version.compareTo(agreementVersion) <= 0) { - context.reportProblem(format("%s version %s must be at greater than the required version: %s", credentialType, version, agreementVersion)); - return false; - } - return true; - } - case GEQ -> { - if (version.compareTo(agreementVersion) < 0) { - context.reportProblem(format("%s version %s must be at least the required version: %s", credentialType, version, agreementVersion)); - return false; - } - return true; - } - default -> { - return false; - } - } - } - - /** - * Configures a new constraint instance. - */ - public static class Builder { - private final FrameworkAgreementConstraintFunction constraint; - - private Builder(String credentialType) { - constraint = new FrameworkAgreementConstraintFunction(credentialType); - } - - /** - * Sets the framework agreement type. - */ - public Builder agreementType(String agreementType) { - constraint.agreementType = agreementType; - return this; - } - - /** - * Sets the optional required agreement version. Equals, greater than, and greater than or equals operations are supported. - */ - public Builder agreementVersion(String version) { - constraint.agreementVersion = version; - return this; - } - - public FrameworkAgreementConstraintFunction build() { - requireNonNull(constraint.agreementType, "agreementType"); - return constraint; - } - - /** - * Ctor. - * - * @param credentialType the framework credential type required by the constraint instance. - * @return the builder - */ - public static Builder newInstance(String credentialType) { - return new Builder(credentialType); - } - } - - -} diff --git a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/framework/FrameworkAgreementCredentialConstraintFunction.java b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/framework/FrameworkAgreementCredentialConstraintFunction.java new file mode 100644 index 000000000..253e01b2c --- /dev/null +++ b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/framework/FrameworkAgreementCredentialConstraintFunction.java @@ -0,0 +1,191 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.policy.cx.framework; + +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential; +import org.eclipse.edc.policy.engine.spi.PolicyContext; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.policy.model.Permission; +import org.eclipse.edc.spi.agent.ParticipantAgent; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.tractusx.edc.core.utils.credentials.CredentialTypePredicate; +import org.eclipse.tractusx.edc.policy.cx.common.AbstractDynamicCredentialConstraintFunction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_CREDENTIAL_NS; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_POLICY_NS; + + +/** + * Enforces a Framework Agreement constraint. + *

+ * This function can parse "FrameworkAgreement" constraints as defined in this documentation: + *

+ *     FrameworkAgreement EQ subtype[:version]
+ * 
+ * In addition, it will support the "legacy" notation, where the subtype is encoded in the left-operand: + *
+ *     FrameworkAgreement.subtype EQ active[:version]
+ * 
+ * Either notation is converted into a set of predicates which are applied to the list of credentials. If the resulting filtered list is empty, the + * policy is considered not fulfilled. Note that if the {@code version} is specified, it must be satisfied by the same + * credential that satisfies the {@code subtype} requirement. + */ +public class FrameworkAgreementCredentialConstraintFunction extends AbstractDynamicCredentialConstraintFunction { + public static final String CONTRACT_VERSION_PROPERTY = CX_CREDENTIAL_NS + "contractVersion"; + public static final String FRAMEWORK_AGREEMENT_LITERAL = "FrameworkAgreement"; + + /** + * Evaluates the constraint's left-operand and right-operand against a list of {@link VerifiableCredential} objects. + * + * @param leftValue the left-side expression for the constraint. Must be either {@code FrameworkAgreement} or {@code FrameworkAgreement.subtype}. + * @param operator the operation Must be {@link Operator#EQ} or {@link Operator#NEQ} + * @param rightValue the right-side expression for the constraint. Must be a string that is either {@code "active":[version]} or {@code subtype[:version]}. + * @param rule the rule associated with the constraint. Ignored by this function. + * @param context the policy context. Must contain the {@link ParticipantAgent}, which in turn must contain a list of {@link VerifiableCredential} stored + * in its claims using the {@code "vc"} key. + * @return true if at least one credential satisfied the requirement imposed by the constraint. + */ + @Override + public boolean evaluate(Object leftValue, Operator operator, Object rightValue, Permission rule, PolicyContext context) { + + if (!checkOperator(operator, context, EQUALITY_OPERATORS)) { + return false; + } + + // we do not support list-type right-operands + if (!(leftValue instanceof String) || !(rightValue instanceof String)) { + context.reportProblem("Both the right- and left-operand must be of type String but were '%s' and '%s', respectively.".formatted(leftValue.getClass(), rightValue.getClass())); + return false; + } + + var participantAgent = extractParticipantAgent(context); + if (participantAgent.failed()) { + context.reportProblem(participantAgent.getFailureDetail()); + return false; + } + + var leftOperand = leftValue.toString(); + var rightOperand = rightValue.toString(); + Result>> predicateResult; + + if (leftOperand.startsWith(CX_POLICY_NS + FRAMEWORK_AGREEMENT_LITERAL + ".")) { // legacy notation + predicateResult = getFilterPredicateLegacy(leftOperand, rightOperand); + + } else if (leftOperand.startsWith(CX_POLICY_NS + FRAMEWORK_AGREEMENT_LITERAL)) { // new notation + predicateResult = getFilterPredicate(rightOperand); + } else { //invalid notation + context.reportProblem("Constraint left-operand must start with '%s' but was '%s'.".formatted(FRAMEWORK_AGREEMENT_LITERAL, leftValue)); + return false; + } + + if (predicateResult.failed()) { // couldn't extract subtype/version predicate from constraint + context.reportProblem(predicateResult.getFailureDetail()); + return false; + } + + var vcListResult = getCredentialList(participantAgent.getContent()); + if (vcListResult.failed()) { // couldn't extract credential list from agent + context.reportProblem(vcListResult.getFailureDetail()); + return false; + } + var rootPredicate = reducePredicates(predicateResult.getContent(), operator); + var credentials = vcListResult.getContent().stream().filter(rootPredicate).toList(); + + if (credentials.isEmpty()) { + context.reportProblem("No credentials found that match the give Policy constraint: [%s %s %s]".formatted(leftValue.toString(), operator.toString(), rightValue.toString())); + return false; + } + return true; + + } + + /** + * Returns {@code true} if the left-operand starts with {@link FrameworkAgreementCredentialConstraintFunction#FRAMEWORK_AGREEMENT_LITERAL}, {@code false} otherwise. + */ + @Override + public boolean canHandle(Object leftValue) { + return leftValue instanceof String && leftValue.toString().startsWith(CX_POLICY_NS + FRAMEWORK_AGREEMENT_LITERAL); + } + + @NotNull + private Predicate reducePredicates(List> predicates, Operator operator) { + return Operator.EQ.equals(operator) ? + predicates.stream().reduce(Predicate::and).orElse(x -> true) : + predicates.stream().map(Predicate::negate).reduce(Predicate::and).orElse(x -> true); + } + + /** + * Converts the right-operand (new notation) into either 1 or 2 predicates, depending on whether the version was encoded or not. + */ + private Result>> getFilterPredicate(String rightOperand) { + var tokens = rightOperand.split(":"); + if (tokens.length > 2 || tokens.length == 0 || tokens[0] == null || tokens[0].isEmpty()) { + return Result.failure("Right-operand must contain the sub-type followed by an optional version string: [:version], but was '%s'.".formatted(rightOperand)); + } + var subtype = tokens[0]; + var version = tokens.length == 2 ? tokens[1] : null; + + return Result.success(createPredicates(subtype, version)); + } + + /** + * Converts the left- and right-operand (legacy notation) into either 1 or 2 predicates, depending on whether the version was encoded or not. + */ + private Result>> getFilterPredicateLegacy(String leftOperand, String rightOperand) { + var subType = leftOperand.replace(CX_POLICY_NS + FRAMEWORK_AGREEMENT_LITERAL + ".", ""); + if (subType.isEmpty()) { + return Result.failure("Left-operand must contain the sub-type 'FrameworkAgreement.'."); + } + if (!rightOperand.startsWith(ACTIVE)) { + return Result.failure("Right-operand must contain the keyword 'active' followed by an optional version string: 'active'[:version], but was '%s'.".formatted(rightOperand)); + } + var version = rightOperand.replace(ACTIVE, "").replace(":", ""); + if (version.isEmpty()) { + version = null; + } + + return Result.success(createPredicates(subType, version)); + } + + @NotNull + private List> createPredicates(String subtype, @Nullable String version) { + var list = new ArrayList>(); + list.add(new CredentialTypePredicate(CX_CREDENTIAL_NS, capitalize(subtype) + CREDENTIAL_LITERAL)); + + if (version != null) { + list.add(credential -> credential.getCredentialSubject().stream().anyMatch(cs -> version.equals(cs.getClaims().getOrDefault(CONTRACT_VERSION_PROPERTY, null)))); + } + return list; + } + + /** + * Capitalizes (makes uppercase) the first character of a non-null input string. + */ + private String capitalize(@NotNull String input) { + return input.substring(0, 1).toUpperCase() + input.substring(1); + } + +} diff --git a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/membership/MembershipCredentialConstraintFunction.java b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/membership/MembershipCredentialConstraintFunction.java new file mode 100644 index 000000000..b44593dfa --- /dev/null +++ b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/membership/MembershipCredentialConstraintFunction.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.policy.cx.membership; + +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential; +import org.eclipse.edc.policy.engine.spi.PolicyContext; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.policy.model.Permission; +import org.eclipse.edc.spi.agent.ParticipantAgent; +import org.eclipse.tractusx.edc.core.utils.credentials.CredentialTypePredicate; +import org.eclipse.tractusx.edc.policy.cx.common.AbstractDynamicCredentialConstraintFunction; + +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_CREDENTIAL_NS; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_POLICY_NS; + + +/** + * This constraint function checks that a MembershipCredential is present in a list of {@link VerifiableCredential} + * objects extracted from a {@link ParticipantAgent} which is expected to be present on the {@link PolicyContext}. + */ +public class MembershipCredentialConstraintFunction extends AbstractDynamicCredentialConstraintFunction { + public static final String MEMBERSHIP_LITERAL = "Membership"; + + @Override + public boolean evaluate(Object leftOperand, Operator operator, Object rightOperand, Permission permission, PolicyContext context) { + if (!ACTIVE.equals(rightOperand)) { + context.reportProblem("Right-operand must be equal to '%s', but was '%s'".formatted(ACTIVE, rightOperand)); + return false; + } + if (!(CX_POLICY_NS + MEMBERSHIP_LITERAL).equalsIgnoreCase(leftOperand.toString())) { + context.reportProblem("Invalid left-operand: must be 'Membership', but was '%s'".formatted(leftOperand)); + return false; + } + // make sure the ParticipantAgent is there + var participantAgent = context.getContextData(ParticipantAgent.class); + if (participantAgent == null) { + context.reportProblem("Required PolicyContext data not found: " + ParticipantAgent.class.getName()); + return false; + } + + var credentialResult = getCredentialList(participantAgent); + if (credentialResult.failed()) { + context.reportProblem(credentialResult.getFailureDetail()); + return false; + } + return credentialResult.getContent() + .stream() + .anyMatch(new CredentialTypePredicate(CX_CREDENTIAL_NS, MEMBERSHIP_LITERAL + CREDENTIAL_LITERAL)); + } + + @Override + public boolean canHandle(Object leftOperand) { + return leftOperand instanceof String && (CX_POLICY_NS + MEMBERSHIP_LITERAL).equalsIgnoreCase(leftOperand.toString()); + } +} diff --git a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunction.java b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunction.java deleted file mode 100644 index cde06be3f..000000000 --- a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunction.java +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.policy.cx.summary; - -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.spi.agent.ParticipantAgent; -import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces; -import org.eclipse.tractusx.edc.policy.cx.common.AbstractVpConstraintFunction; - -import java.util.Map; - -import static jakarta.json.JsonValue.ValueType.ARRAY; -import static jakarta.json.JsonValue.ValueType.OBJECT; -import static jakarta.json.JsonValue.ValueType.STRING; -import static java.lang.String.format; -import static java.util.Objects.requireNonNull; -import static org.eclipse.edc.policy.model.Operator.EQ; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.SUMMARY_CREDENTIAL_TYPE; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTypeFunctions.extractObjectsOfType; - - -/** - * Implements Catena-X policies by verifying policy constraints against the summary credential. - *

- * Verifies the presence of an entry in the {@link #SUMMARY_CREDENTIAL_ITEMS} of a summary credential token. - */ -public class SummaryConstraintFunction extends AbstractVpConstraintFunction { - private static final String SUMMARY_CREDENTIAL_ITEMS = CredentialsNamespaces.CX_SUMMARY_NS + "/items"; - private static final String CREDENTIAL_SUBJECT = "credentialSubject"; - - private static final String ACTIVE = "active"; - - private final String summaryType; - - public SummaryConstraintFunction(String summaryType) { - super("Summary"); - requireNonNull(summaryType); - this.summaryType = summaryType; - } - - @Override - public boolean evaluate(Operator operator, Object rightValue, Permission rule, PolicyContext context) { - if (!validateOperator(operator, context, EQ)) { - return false; - } - - if (!validateRightOperand(ACTIVE, rightValue, context)) { - return false; - } - - var vp = (JsonObject) context.getContextData(ParticipantAgent.class).getClaims().get(VP_PROPERTY); - if (!validatePresentation(vp, context)) { - return false; - } - - return extractObjectsOfType(SUMMARY_CREDENTIAL_TYPE, vp).anyMatch(credential -> hasSummaryType(credential, context)); - } - - /** - * Returns true if the summary credential has the item specified by {@link #summaryType}. - */ - private boolean hasSummaryType(JsonObject credential, PolicyContext context) { - var credentialSubject = extractCredentialSubject(credential, context); - if (credentialSubject == null) { - return false; - } - var items = credentialSubject.get(SUMMARY_CREDENTIAL_ITEMS); - - if (items == null || items.getValueType() != ARRAY) { - context.reportProblem(format("%s items not found in %s", errorPrefix, CREDENTIAL_SUBJECT)); - return false; - } - - if (items.asJsonArray().isEmpty()) { - context.reportProblem(format("%s empty %s items graph container", errorPrefix, CREDENTIAL_SUBJECT)); - return false; - } - - return items.asJsonArray().stream().filter(e -> e.getValueType() == OBJECT) - .flatMap(o -> o.asJsonObject().entrySet().stream()) - .anyMatch(this::matchSummaryType); - } - - /** - * Returns true if the entry is a string and matches the Json-Ld {@link #VALUE} type. - */ - private boolean matchSummaryType(Map.Entry e) { - return VALUE.equals(e.getKey()) && - e.getValue().getValueType() == STRING && - summaryType.equals(((JsonString) e.getValue()).getString()); - } - - -} diff --git a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunctionsProvider.java b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunctionsProvider.java deleted file mode 100644 index d9218ee35..000000000 --- a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunctionsProvider.java +++ /dev/null @@ -1,113 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.policy.cx.summary; - -import org.eclipse.edc.policy.engine.spi.PolicyEngine; -import org.eclipse.edc.policy.engine.spi.RuleBindingRegistry; -import org.eclipse.edc.policy.model.Permission; - -import java.util.HashMap; -import java.util.Map; - -import static java.util.Collections.unmodifiableMap; -import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_SCHEMA; -import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.CATALOG_REQUEST_SCOPE; -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.CATALOG_SCOPE; -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.NEGOTIATION_REQUEST_SCOPE; -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.NEGOTIATION_SCOPE; -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.TRANSFER_PROCESS_REQUEST_SCOPE; -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.TRANSFER_PROCESS_SCOPE; - -/** - * Registers {@link SummaryConstraintFunction} and {@link SummaryTokenPolicyFunction} instances with the runtime policy engine. - */ -public class SummaryConstraintFunctionsProvider { - - /** - * Mappings from policy constraint left operand values to the corresponding item value in the summary VP. - */ - static final Map CREDENTIAL_MAPPINGS; - - - static { - var initialMappings = Map.of( - "Membership", "MembershipCredential", - "Dismantler", "DismantlerCredential", - "FrameworkAgreement.pcf", "PcfCredential", - "FrameworkAgreement.sustainability", "SustainabilityCredential", - "FrameworkAgreement.quality", "QualityCredential", - "FrameworkAgreement.traceability", "TraceabilityCredential", - "FrameworkAgreement.behavioraltwin", "BehaviorTwinCredential", - "BPN", "BpnCredential" - ); - var mappings = new HashMap<>(initialMappings); - initialMappings.forEach((credentialName, summaryType) -> { - mappings.put(TX_NAMESPACE + credentialName, summaryType); - }); - CREDENTIAL_MAPPINGS = unmodifiableMap(mappings); - } - - /** - * Configures and registers required summary functions with the policy engine. - */ - public static void registerFunctions(PolicyEngine engine) { - var tokenPolicyFunction = new SummaryTokenPolicyFunction(); - engine.registerPreValidator(CATALOG_REQUEST_SCOPE, tokenPolicyFunction); - engine.registerPreValidator(NEGOTIATION_REQUEST_SCOPE, tokenPolicyFunction); - engine.registerPreValidator(TRANSFER_PROCESS_REQUEST_SCOPE, tokenPolicyFunction); - - CREDENTIAL_MAPPINGS.forEach((constraintName, summaryType) -> { - - engine.registerFunction(CATALOG_SCOPE, - Permission.class, - constraintName, - new SummaryConstraintFunction(summaryType)); - - engine.registerFunction(NEGOTIATION_SCOPE, - Permission.class, - constraintName, - new SummaryConstraintFunction(summaryType)); - - engine.registerFunction(TRANSFER_PROCESS_SCOPE, - Permission.class, - constraintName, - new SummaryConstraintFunction(summaryType)); - }); - - } - - public static void registerBindings(RuleBindingRegistry registry) { - CREDENTIAL_MAPPINGS.forEach((constraintName, summaryType) -> { - registry.bind(constraintName, CATALOG_REQUEST_SCOPE); - registry.bind(constraintName, NEGOTIATION_REQUEST_SCOPE); - registry.bind(constraintName, TRANSFER_PROCESS_REQUEST_SCOPE); - registry.bind(constraintName, CATALOG_SCOPE); - registry.bind(constraintName, NEGOTIATION_SCOPE); - registry.bind(constraintName, TRANSFER_PROCESS_SCOPE); - }); - - registry.bind(ODRL_SCHEMA + "use", CATALOG_SCOPE); - registry.bind(ODRL_SCHEMA + "use", NEGOTIATION_SCOPE); - registry.bind(ODRL_SCHEMA + "use", TRANSFER_PROCESS_SCOPE); - - } - -} diff --git a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryTokenPolicyFunction.java b/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryTokenPolicyFunction.java deleted file mode 100644 index 7125a1ae8..000000000 --- a/edc-extensions/cx-policy/src/main/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryTokenPolicyFunction.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.policy.cx.summary; - -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Policy; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.iam.RequestScope; - -import java.util.function.BiFunction; - -import static java.lang.String.format; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_SUMMARY_CREDENTIAL; - -/** - * Includes a summary credential in the token parameters. - */ -public class SummaryTokenPolicyFunction implements BiFunction { - - @Override - public Boolean apply(Policy policy, PolicyContext context) { - var scopes = context.getContextData(RequestScope.Builder.class); - if (scopes == null) { - throw new EdcException(format("%s not set in policy context", RequestScope.Builder.class.getName())); - } - - scopes.scope(CX_SUMMARY_CREDENTIAL); - return true; - } -} diff --git a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/CredentialFunctions.java b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/CredentialFunctions.java new file mode 100644 index 000000000..1aedea42d --- /dev/null +++ b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/CredentialFunctions.java @@ -0,0 +1,91 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.policy.cx; + + +import org.eclipse.edc.iam.verifiablecredentials.spi.model.CredentialSubject; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.Issuer; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_CREDENTIAL_NS; + +public class CredentialFunctions { + + public static VerifiableCredential.Builder createCredential(String type, String version) { + return VerifiableCredential.Builder.newInstance() + .types(List.of(CX_CREDENTIAL_NS + "VerifiableCredential", CX_CREDENTIAL_NS + type)) + .id(UUID.randomUUID().toString()) + .issuer(new Issuer(UUID.randomUUID().toString(), Map.of("prop1", "val1"))) + .expirationDate(Instant.now().plus(365, ChronoUnit.DAYS)) + .issuanceDate(Instant.now()) + .credentialSubject(CredentialSubject.Builder.newInstance() + .id("subject-id") + .claim(CX_CREDENTIAL_NS + "holderIdentifier", "did:web:holder") + .claim(CX_CREDENTIAL_NS + "contractVersion", version) + .claim(CX_CREDENTIAL_NS + "contractTemplate", "https://public.catena-x.org/contracts/pcf.v1.pdf") + .build()); + } + + public static VerifiableCredential.Builder createPcfCredential() { + return createCredential("PcfCredential", "1.0.0"); + } + + public static VerifiableCredential.Builder createDismantlerCredential(String... brands) { + return createDismantlerCredential(Arrays.asList(brands), "vehicleDismantle"); + } + + public static VerifiableCredential.Builder createDismantlerCredential(Collection brands, String... activityType) { + var at = activityType.length == 1 ? activityType[0] : List.of(activityType); + return VerifiableCredential.Builder.newInstance() + .types(List.of("VerifiableCredential", CX_CREDENTIAL_NS + "DismantlerCredential")) + .id(UUID.randomUUID().toString()) + .issuer(new Issuer(UUID.randomUUID().toString(), Map.of("prop1", "val1"))) + .expirationDate(Instant.now().plus(365, ChronoUnit.DAYS)) + .issuanceDate(Instant.now()) + .credentialSubject(CredentialSubject.Builder.newInstance() + .id("subject-id") + .claim(CX_CREDENTIAL_NS + "holderIdentifier", "did:web:holder") + .claim(CX_CREDENTIAL_NS + "allowedVehicleBrands", brands) + .claim(CX_CREDENTIAL_NS + "activityType", at) + .build()); + } + + public static VerifiableCredential.Builder createMembershipCredential() { + return VerifiableCredential.Builder.newInstance() + .types(List.of(CX_CREDENTIAL_NS + "VerifiableCredential", CX_CREDENTIAL_NS + "MembershipCredential")) + .id(UUID.randomUUID().toString()) + .issuer(new Issuer(UUID.randomUUID().toString(), Map.of("prop1", "val1"))) + .expirationDate(Instant.now().plus(365, ChronoUnit.DAYS)) + .issuanceDate(Instant.now()) + .credentialSubject(CredentialSubject.Builder.newInstance() + .id("subject-id") + .claim(CX_CREDENTIAL_NS + "holderIdentifier", "did:web:holder") + .build()); + } + +} diff --git a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/common/AbstractVpConstraintFunctionTest.java b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/common/AbstractVpConstraintFunctionTest.java deleted file mode 100644 index 8a978121e..000000000 --- a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/common/AbstractVpConstraintFunctionTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.policy.cx.common; - -import com.fasterxml.jackson.core.JsonProcessingException; -import jakarta.json.Json; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.policy.model.Permission; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.createObjectMapper; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.expand; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -class AbstractVpConstraintFunctionTest { - private static final String FOO_CREDENTIAL = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", - "type": [ - "VerifiableCredential", - "FooCredential" - ], - "issuer": "did:web:test", - "credentialSubject": { - "id": "did:web:test" - } - } - """; - private static final String PRESENTATION = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": "VerifiablePresentation" - } - """; - private AbstractVpConstraintFunction function; - private PolicyContext context; - - @Test - void verify_operators() { - assertThat(function.validateOperator(Operator.EQ, context, Operator.EQ)).isEqualTo(true); - } - - @Test - void verify_invalid_operators() { - assertThat(function.validateOperator(Operator.NEQ, context, Operator.EQ)).isEqualTo(false); - verify(context).reportProblem(anyString()); - } - - @Test - void verify_presentation() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(PRESENTATION, JsonObject.class), Map.of()); - - assertThat(function.validatePresentation(vp, context)).isTrue(); - - assertThat(function.validatePresentation(null, context)).isFalse(); - - assertThat(function.validatePresentation("invalid", context)).isFalse(); - - var array = Json.createArrayBuilder().build(); - assertThat(function.validatePresentation(array, context)).isFalse(); - } - - @Test - void verify_extract_credential_subject() throws JsonProcessingException { - var credential = expand(createObjectMapper().readValue(FOO_CREDENTIAL, JsonObject.class), Map.of()); - - var subject = function.extractCredentialSubject(credential, context); - - assertThat(subject).isNotNull(); - assertThat(((JsonString) subject.get("@id")).getString()).isEqualTo("did:web:test"); - } - - @BeforeEach - void setUp() { - context = mock(PolicyContext.class); - function = new AbstractVpConstraintFunction("FooCredential") { - @Override - public boolean evaluate(Operator operator, Object rightValue, Permission rule, PolicyContext context) { - throw new UnsupportedOperationException(); - } - }; - } -} diff --git a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/dismantler/DismantlerConstraintFunctionTest.java b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/dismantler/DismantlerConstraintFunctionTest.java new file mode 100644 index 000000000..2dfce69fa --- /dev/null +++ b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/dismantler/DismantlerConstraintFunctionTest.java @@ -0,0 +1,441 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.policy.cx.dismantler; + +import org.eclipse.edc.policy.engine.spi.PolicyContext; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.spi.agent.ParticipantAgent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.policy.model.Operator.IN; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_POLICY_NS; +import static org.eclipse.tractusx.edc.policy.cx.CredentialFunctions.createDismantlerCredential; +import static org.eclipse.tractusx.edc.policy.cx.CredentialFunctions.createPcfCredential; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class DismantlerConstraintFunctionTest { + + private final DismantlerCredentialConstraintFunction function = new DismantlerCredentialConstraintFunction(); + private final PolicyContext context = mock(); + private ParticipantAgent participantAgent; + + @BeforeEach + void setup() { + participantAgent = mock(ParticipantAgent.class); + when(context.getContextData(eq(ParticipantAgent.class))) + .thenReturn(participantAgent); + } + + @Test + void evaluate_leftOperandInvalid() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Tatra", "Moskvich").build()))); + assertThat(function.evaluate("foobar", Operator.EQ, "active", null, context)).isFalse(); + verify(context).reportProblem(eq("Invalid left-operand: must be 'Dismantler[.activityType | .allowedBrands ], but was 'foobar'")); + } + + @Test + void evaluate_noParticipantAgentOnContext() { + when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(null); + assertThat(function.evaluate("Dismantler", Operator.EQ, "active", null, context)).isFalse(); + verify(context).reportProblem("Required PolicyContext data not found: org.eclipse.edc.spi.agent.ParticipantAgent"); + } + + @Test + void evaluate_noVcClaimOnParticipantAgent() { + assertThat(function.evaluate("Dismantler", Operator.EQ, "active", null, context)).isFalse(); + verify(context).reportProblem(eq("ParticipantAgent did not contain a 'vc' claim.")); + } + + @Test + void evaluate_vcClaimEmpty() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of())); + assertThat(function.evaluate("Dismantler", Operator.EQ, "active", null, context)).isFalse(); + verify(context).reportProblem(eq("ParticipantAgent contains a 'vc' claim but it did not contain any VerifiableCredentials.")); + } + + @Test + void evaluate_vcClaimNotList() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", new Object())); + assertThat(function.evaluate("Dismantler", Operator.EQ, "active", null, context)).isFalse(); + verify(context).reportProblem(eq("ParticipantAgent contains a 'vc' claim, but the type is incorrect. Expected java.util.List, received java.lang.Object.")); + } + + @Nested + class Active { + @Test + void evaluate_eq_satisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Tatra", "Moskvich").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler", Operator.EQ, "active", null, context)).isTrue(); + } + + @Test + void evaluate_eq_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createPcfCredential().build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler", Operator.EQ, "active", null, context)).isFalse(); + } + + @Test + void evaluate_neq_satisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createPcfCredential().build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler", Operator.NEQ, "active", null, context)).isTrue(); + } + + @Test + void evaluate_neq_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Yugo", "Tatra").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler", Operator.NEQ, "active", null, context)).isFalse(); + } + + @Test + void evaluate_invalidOperators() { + var invalidOperators = new ArrayList<>(Arrays.asList(Operator.values())); + invalidOperators.remove(Operator.EQ); + invalidOperators.remove(Operator.NEQ); + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createPcfCredential().build()))); + + assertThat(invalidOperators).allSatisfy(invalidOperator -> assertThat(function.evaluate(CX_POLICY_NS + "Dismantler", invalidOperator, "active", null, context)).isFalse()); + + } + + @Test + void evaluate_rightOperandInvalid() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createPcfCredential().build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler", Operator.EQ, "invalid", null, context)).isFalse(); + verify(context).reportProblem("Right-operand must be equal to 'active', but was 'invalid'"); + } + } + + @Nested + class AllowedBrands { + + @DisplayName("Constraint (list) must match credential EXACTLY") + @Test + void evaluate_eq_list() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Tatra", "Moskvich").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.EQ, List.of("Tatra", "Moskvich"), null, context)).isTrue(); + } + + @DisplayName("Constraint (list) must credential EXACTLY - failure") + @Test + void evaluate_eq_list_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Tatra", "Moskvich", "Lada").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.EQ, List.of("Tatra", "Moskvich"), null, context)).isFalse(); + verify(context, never()).reportProblem(anyString()); + } + + @DisplayName("Constraint (scalar) must match credential EXACTLY") + @Test + void evaluate_eq_scalar() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Yugo").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.EQ, "Yugo", null, context)).isTrue(); + } + + @DisplayName("Constraint (scalar) must credential EXACTLY - failure") + @Test + void evaluate_eq_scalar_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Tatra").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.EQ, "Yugo", null, context)).isFalse(); + verify(context, never()).reportProblem(anyString()); + } + + @DisplayName("Constraint and credential must be DISJOINT") + @Test + void evaluate_neq_satisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Tatra", "Moskvich").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.NEQ, List.of("Lada", "Yugo"), null, context)).isTrue(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Tatra", "Moskvich", "Yugo", "Lada").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.NEQ, List.of("Lada", "Yugo"), null, context)).isTrue(); + } + + @DisplayName("Constraint and credential must be DISJOINT - failure") + @Test + void evaluate_neq_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Lada", "Yugo").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.NEQ, List.of("Lada", "Yugo"), null, context)).isFalse(); + } + + @DisplayName("Constraint and credential must INTERSECT") + @Test + void evaluate_isAnyOf_satisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Lada").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_ANY_OF, List.of("Tatra", "Moskvich", "Lada"), null, context)).isTrue(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Moskvich", "Tatra").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_ANY_OF, List.of("Lada", "Moskvich"), null, context)).isTrue(); + } + + @DisplayName("Constraint and credential must INTERSECT - failure") + @Test + void evaluate_isAnyOf_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Gaz").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_ANY_OF, List.of("Tatra", "Moskvich", "Lada"), null, context)).isFalse(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Gaz", "Yugo").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_ANY_OF, List.of("Tatra", "Moskvich", "Lada"), null, context)).isFalse(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential().build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_ANY_OF, List.of("Tatra", "Moskvich", "Lada"), null, context)).isFalse(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Moskvich", "Tatra").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_ANY_OF, List.of(), null, context)).isFalse(); + } + + @DisplayName("Constraint and credential must NOT INTERSECT") + @Test + void evaluate_isNoneOf_satisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Gaz", "Yugo").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_NONE_OF, List.of("Tatra", "Moskvich", "Lada"), null, context)).isTrue(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Gaz", "Yugo").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_NONE_OF, List.of(), null, context)).isTrue(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential().build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_NONE_OF, List.of("Tatra", "Moskvich", "Lada"), null, context)).isTrue(); + } + + @DisplayName("Constraint and credential must NOT INTERSECT - failure") + @Test + void evaluate_isNoneOf_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Gaz", "Yugo").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_NONE_OF, List.of("Tatra", "Moskvich", "Yugo"), null, context)).isFalse(); + + } + + @DisplayName("Brand list from credential must be FULLY CONTAINED within constraint") + @Test + void evaluate_in_satisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Gaz", "Yugo").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", IN, List.of("Gaz", "Moskvich", "Yugo", "Lada"), null, context)).isTrue(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Gaz").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", IN, List.of("Gaz"), null, context)).isTrue(); + } + + @DisplayName("Brand list from credential must be FULLY CONTAINED within constraint - failure") + @Test + void evaluate_in_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Gaz", "Yugo").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", IN, List.of("Gaz", "Moskvich", "Yugo", "Lada"), null, context)).isTrue(); + } + + @DisplayName("Illegal operator when constraint contains a list value") + @Test + void evaluate_illegalOperator_constraintIsList() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Gaz", "Yugo").build()))); + var illegalOp = List.of(Operator.HAS_PART, Operator.GEQ, Operator.LEQ, Operator.GT, Operator.LT, Operator.IS_ALL_OF); + + assertThat(illegalOp).allSatisfy(op -> { + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", op, List.of("Gaz", "Moskvich"), null, context)).isFalse(); + verify(context).reportProblem("Invalid operator: this constraint only allows the following operators: [EQ, NEQ], but received '%s'.".formatted(op)); + }); + } + + @DisplayName("Illegal operator when constraint contains a scalar value") + @Test + void evaluate_illegalOperator_constraintIsScalar() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Gaz", "Yugo").build()))); + + var invalidOperators = new ArrayList<>(Arrays.asList(Operator.values())); + invalidOperators.remove(Operator.EQ); + invalidOperators.remove(Operator.NEQ); + + assertThat(invalidOperators).allSatisfy(op -> { + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", op, "Moskvich", null, context)).isFalse(); + verify(context).reportProblem("Invalid operator: this constraint only allows the following operators: [EQ, NEQ], but received '%s'.".formatted(op)); + }); + } + + @DisplayName("Constraint right-operand has an invalid type") + @Test + void evaluate_righOpInvalidType() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential("Gaz", "Yugo").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.allowedBrands", IN, Map.of("foo", "bar"), null, context)).isFalse(); + verify(context).reportProblem(startsWith("Invalid right-operand type: expected String or List, but received:")); + } + } + + @Nested + class ActivityType { + private final Collection brands = List.of("Tatra", "Yugo"); + + @DisplayName("Constraint (list) must match credential EXACTLY") + @Test + void evaluate_eq_list() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle", "vehicleScrap").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.EQ, List.of("vehicleDismantle", "vehicleScrap"), null, context)).isTrue(); + } + + @DisplayName("Constraint (list) must credential EXACTLY - failure") + @Test + void evaluate_eq_list_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle", "vehicleScrap").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.EQ, List.of("vehicleRefurbish"), null, context)).isFalse(); + verify(context, never()).reportProblem(anyString()); + } + + @DisplayName("Constraint (scalar) must match credential EXACTLY") + @Test + void evaluate_eq_scalar() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.EQ, "vehicleDismantle", null, context)).isTrue(); + } + + @DisplayName("Constraint (scalar) must credential EXACTLY - failure") + @Test + void evaluate_eq_scalar_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.EQ, "vehicleScrap", null, context)).isFalse(); + verify(context, never()).reportProblem(anyString()); + } + + @DisplayName("Constraint and credential must be DISJOINT") + @Test + void evaluate_neq_satisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.NEQ, "vehicleScrap", null, context)).isTrue(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.NEQ, List.of("vehicleScrap", "vehicleRefurbish"), null, context)).isTrue(); + } + + @DisplayName("Constraint and credential must be DISJOINT - failure") + @Test + void evaluate_neq_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.NEQ, "vehicleDismantle", null, context)).isFalse(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.NEQ, List.of("vehicleDismantle", "vehicleRefurbish"), null, context)).isTrue(); + } + + @DisplayName("Constraint and credential must INTERSECT") + @Test + void evaluate_isAnyOf_satisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle", "vehicleScrap").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IS_ANY_OF, List.of("vehicleDismantle", "vehicleRefurbish"), null, context)).isTrue(); + } + + @DisplayName("Constraint and credential must INTERSECT - failure") + @Test + void evaluate_isAnyOf_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle", "vehicleScrap").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IS_ANY_OF, List.of("vehicleCrush", "vehicleRefurbish"), null, context)).isFalse(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle", "vehicleScrap").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IS_ANY_OF, List.of(), null, context)).isFalse(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands).build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IS_ANY_OF, List.of("vehicleCrush", "vehicleRefurbish"), null, context)).isFalse(); + } + + @DisplayName("Constraint and credential must NOT INTERSECT") + @Test + void evaluate_isNoneOf_satisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle", "vehicleScrap").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IS_NONE_OF, List.of("vehicleRefurbish"), null, context)).isTrue(); + + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle", "vehicleScrap").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IS_NONE_OF, List.of(), null, context)).isTrue(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands).build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IS_NONE_OF, List.of("vehicleRefurbish"), null, context)).isTrue(); + } + + @DisplayName("Constraint and credential must NOT INTERSECT - failure") + @Test + void evaluate_isNoneOf_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle", "vehicleRefurbish").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IS_NONE_OF, List.of("vehicleRefurbish"), null, context)).isFalse(); + } + + @DisplayName("Activity list from credential must be FULLY CONTAINED within constraint") + @Test + void evaluate_in_satisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleRefurbish").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IN, List.of("vehicleRefurbish", "vehicleDismantle"), null, context)).isTrue(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IN, List.of("vehicleDismantle"), null, context)).isTrue(); + } + + @DisplayName("Activity list from credential must be FULLY CONTAINED within constraint - failure") + @Test + void evaluate_in_notSatisfied() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleRefurbish", "vehicleScrap").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IN, List.of("vehicleRefurbish", "vehicleDismantle"), null, context)).isFalse(); + + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleScrap").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", Operator.IN, List.of("vehicleRefurbish", "vehicleDismantle"), null, context)).isFalse(); + + } + + @DisplayName("Illegal operator when constraint contains a list value") + @Test + void evaluate_illegalOperator_constraintIsList() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle").build()))); + var illegalOp = List.of(Operator.HAS_PART, Operator.GEQ, Operator.LEQ, Operator.GT, Operator.LT, Operator.IS_ALL_OF); + + assertThat(illegalOp).allSatisfy(op -> { + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", op, List.of("vehicleDismantle"), null, context)).isFalse(); + }); + } + + @DisplayName("Illegal operator when constraint contains a scalar value") + @Test + void evaluate_illegalOperator_constraintIsScalar() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle").build()))); + + var invalidOperators = new ArrayList<>(Arrays.asList(Operator.values())); + invalidOperators.remove(Operator.EQ); + invalidOperators.remove(Operator.NEQ); + + assertThat(invalidOperators).allSatisfy(op -> { + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", op, "vehicleDismantle", null, context)).isFalse(); + verify(context).reportProblem("Invalid operator: this constraint only allows the following operators: [EQ, NEQ], but received '%s'.".formatted(op)); + }); + } + + @DisplayName("Constraint right-operand has an invalid type") + @Test + void evaluate_righOpInvalidType() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createDismantlerCredential(brands, "vehicleDismantle").build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Dismantler.activityType", IN, Map.of("foo", "bar"), null, context)).isFalse(); + verify(context).reportProblem(startsWith("Invalid right-operand type: expected String or List, but received:")); + } + } +} \ No newline at end of file diff --git a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/framework/FrameworkAgreementConstraintFunctionTest.java b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/framework/FrameworkAgreementConstraintFunctionTest.java index 3c7c49ef9..56ef833f1 100644 --- a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/framework/FrameworkAgreementConstraintFunctionTest.java +++ b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/framework/FrameworkAgreementConstraintFunctionTest.java @@ -19,129 +19,277 @@ package org.eclipse.tractusx.edc.policy.cx.framework; -import com.fasterxml.jackson.core.JsonProcessingException; -import jakarta.json.JsonObject; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential; import org.eclipse.edc.policy.engine.spi.PolicyContext; +import org.eclipse.edc.policy.model.Operator; import org.eclipse.edc.policy.model.Permission; import org.eclipse.edc.spi.agent.ParticipantAgent; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.policy.model.Operator.EQ; -import static org.eclipse.edc.policy.model.Operator.GEQ; -import static org.eclipse.edc.policy.model.Operator.GT; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_USE_CASE_NS_V1; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.createObjectMapper; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.expand; -import static org.eclipse.tractusx.edc.policy.cx.framework.PcfCredential.PCF_VP; -import static org.eclipse.tractusx.edc.policy.cx.framework.UseCaseContext.USE_CASE_CONTEXT; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_POLICY_NS; +import static org.eclipse.tractusx.edc.policy.cx.CredentialFunctions.createCredential; +import static org.eclipse.tractusx.edc.policy.cx.CredentialFunctions.createPcfCredential; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.startsWith; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; class FrameworkAgreementConstraintFunctionTest { - private static final Map CONTEXT_CACHE = Map.of(CX_USE_CASE_NS_V1, USE_CASE_CONTEXT); + private final FrameworkAgreementCredentialConstraintFunction function = new FrameworkAgreementCredentialConstraintFunction(); + private final PolicyContext context = mock(); private Permission permission; - private PolicyContext context; + private ParticipantAgent participantAgent; - @Test - void verify_constraint() throws JsonProcessingException { - var function = FrameworkAgreementConstraintFunction.Builder - .newInstance("PcfCredential") - .agreementType("PcfAgreement") - .build(); - - setVpInContextVp(); - - var result = function.evaluate(EQ, "active", permission, context); - - assertThat(result).isTrue(); + @BeforeEach + void setup() { + participantAgent = mock(ParticipantAgent.class); + when(context.getContextData(eq(ParticipantAgent.class))) + .thenReturn(participantAgent); } @Test - void verify_contract_version() throws JsonProcessingException { - var function = FrameworkAgreementConstraintFunction.Builder - .newInstance("PcfCredential") - .agreementType("PcfAgreement") - .agreementVersion("1.0.0") - .build(); - - setVpInContextVp(); - - var result = function.evaluate(EQ, "active", permission, context); - assertThat(result).isTrue(); - - result = function.evaluate(GEQ, "active", permission, context); - assertThat(result).isTrue(); - - result = function.evaluate(GT, "active", permission, context); - assertThat(result).isFalse(); // should fail because version is equal + void evaluate_leftOperandInvalid() { + assertThat(function.evaluate("ThisIsInvalid", Operator.EQ, "irrelevant", permission, context)).isFalse(); + verify(context).reportProblem(startsWith("Constraint left-operand must start with 'FrameworkAgreement'")); } @Test - void verify_contract_version_gt_fail() throws JsonProcessingException { - var function = FrameworkAgreementConstraintFunction.Builder - .newInstance("PcfCredential") - .agreementType("PcfAgreement") - .agreementVersion("2.0.0") - .build(); - - setVpInContextVp(); - - var result = function.evaluate(GT, "active", permission, context); - assertThat(result).isFalse(); // should fail because version is equal + void evaluate_invalidOperator() { + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement.foobar", Operator.HAS_PART, "irrelevant", permission, context)).isFalse(); + verify(context).reportProblem(eq("Invalid operator: this constraint only allows the following operators: [EQ, NEQ], but received 'HAS_PART'.")); + verifyNoMoreInteractions(context); + } - verify(context, times(1)).reportProblem(Mockito.contains("version")); + @Test + void evaluate_noParticipantOnContext() { + when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(null); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement.foobar", Operator.EQ, "irrelevant", permission, context)).isFalse(); + verify(context).reportProblem(eq("Required PolicyContext data not found: " + ParticipantAgent.class.getName())); + verify(context).getContextData(eq(ParticipantAgent.class)); + verifyNoMoreInteractions(context); } @Test - void verify_invalid_agreement_fail() throws JsonProcessingException { - var function = FrameworkAgreementConstraintFunction.Builder - .newInstance("PcfCredential") - .agreementType("UnknownAgreement") - .build(); + void evaluate_vcClaimNotPresent() { + when(participantAgent.getClaims()).thenReturn(Map.of()); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement.foobar", Operator.EQ, "active", permission, context)).isFalse(); + verify(context).reportProblem(eq("ParticipantAgent did not contain a 'vc' claim.")); + } - setVpInContextVp(); + @Test + void evaluate_vcClaimNotListOfCredentials() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", new Object() + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement.foobar", Operator.EQ, "active:0.0.1", permission, context)).isFalse(); + verify(context).reportProblem(eq("ParticipantAgent contains a 'vc' claim, but the type is incorrect. Expected java.util.List, received java.lang.Object.")); + } - var result = function.evaluate(EQ, "active", permission, context); + @Test + void evaluate_vcClaimCredentialsEmpty() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", new ArrayList() + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement.foobar", Operator.EQ, "active", permission, context)).isFalse(); + verify(context).reportProblem(eq("ParticipantAgent contains a 'vc' claim but it did not contain any VerifiableCredentials.")); + } - assertThat(result).isFalse(); + @Test + void evaluate_rightOperandInvalidFormat() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of(createPcfCredential().build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement.pcf", Operator.EQ, "/violate$", permission, context)).isFalse(); + verify(context).reportProblem(eq("Right-operand must contain the keyword 'active' followed by an optional version string: 'active'[:version], but was '/violate$'.")); + } - verify(context, times(1)).reportProblem(Mockito.contains("missing the usecase type")); + @Test + void evaluate_requiredCredentialNotFound() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of( + createCredential(CX_POLICY_NS + "PcfCredential", "1.3.0").build(), + createCredential(CX_POLICY_NS + "PcfCredential", "1.0.0").build()) + )); + assertThat(function.evaluate("FrameworkAgreement", Operator.EQ, "someOther:1.3.0", permission, context)).isFalse(); } @Test - void verify_no_credential_fail() { - var function = FrameworkAgreementConstraintFunction.Builder - .newInstance("PcfCredential") - .agreementType("PcfAgreement") - .build(); + void evaluate_requiredCredential_wrongVersion() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of( + createCredential("SomeOtherCredential", "2.0.0").build(), + createCredential("PcfCredential", "1.8.0").build(), + createCredential("PcfCredential", "1.0.0").build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement", Operator.EQ, "pcf:1.3.0", permission, context)).isFalse(); + } - when(context.getContextData(ParticipantAgent.class)).thenReturn(new ParticipantAgent(Map.of(), Map.of())); + @Test + void evaluate_requiredCredentialFound() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of( + createCredential("PcfCredential", "6.0.0").build(), + createCredential("SomeOtherType", "3.4.1").build() + ) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement", Operator.EQ, "pcf", permission, context)).isTrue(); + } - var result = function.evaluate(EQ, "active", permission, context); + @Test + void evaluate_requiredCredentialFound_withCorrectVersion() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of( + createCredential("PcfCredential", "2.0.0").build(), + createCredential("PcfCredential", "1.3.0").build(), + createCredential("PcfCredential", "1.0.0").build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement", Operator.EQ, "pcf:1.3.0", permission, context)).isTrue(); + } - assertThat(result).isFalse(); + @Test + void evaluate_neq_requiredCredentialFound() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of( + createCredential("PcfCredential", "6.0.0").build(), + createCredential("SomeOtherType", "3.4.1").build() + ) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement", Operator.NEQ, "sustainability", permission, context)).isTrue(); + } - verify(context, times(1)).reportProblem(Mockito.contains("VP not found")); + @Test + void evaluate_neq_oneOfManyViolates() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of( + createCredential("PcfCredential", "6.0.0").build(), + createCredential("SustainabilityCredential", "6.0.0").build(), + createCredential("SomeOtherType", "3.4.1").build() + ) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement", Operator.NEQ, "sustainability", permission, context)).isTrue(); } - @BeforeEach - void setUp() { - permission = Permission.Builder.newInstance().build(); - context = mock(PolicyContext.class); + @Test + void evaluate_neq_oneViolates() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of( + createCredential("SustainabilityCredential", "6.0.0").build() + ) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement", Operator.NEQ, "sustainability", permission, context)).isFalse(); } - private void setVpInContextVp() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(PCF_VP, JsonObject.class), CONTEXT_CACHE); - when(context.getContextData(ParticipantAgent.class)).thenReturn(new ParticipantAgent(Map.of(VP_PROPERTY, vp), Map.of())); + @Test + void evaluate_neq_requiredCredentialFound_withCorrectVersion() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of( + createCredential("PcfCredential", "2.0.0").build(), + createCredential("FooBarCredential", "1.3.0").build(), + createCredential("BarBazCredential", "1.0.0").build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement", Operator.NEQ, "sustainability:1.3.0", permission, context)).isTrue(); } + @Nested + class LegacyLeftOperand { + @Test + void evaluate_leftOperand_notContainSubtype() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of(createPcfCredential().build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement.", Operator.EQ, "active:0.4.2", permission, context)).isFalse(); + verify(context).reportProblem(eq("Left-operand must contain the sub-type 'FrameworkAgreement.'.")); + } + + @Test + void evaluate_leftOperand_notContainFrameworkLiteral() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of(createPcfCredential().build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "foobar.pcf", Operator.EQ, "active:0.4.2", permission, context)).isFalse(); + verify(context).reportProblem(startsWith("Constraint left-operand must start with 'FrameworkAgreement' but was")); + } + + @Test + void evaluate_leftOperand_notStartsWithFrameworkLiteral() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of(createPcfCredential().build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "foobarFrameworkAgreement.pcf", Operator.EQ, "active:0.4.2", permission, context)).isFalse(); + verify(context).reportProblem(startsWith("Constraint left-operand must start with 'FrameworkAgreement' but was")); + } + + @Test + void evaluate_rightOperand_notStartWithActiveLiteral() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of(createPcfCredential().build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement.pcf", Operator.EQ, "violates", permission, context)).isFalse(); + verify(context).reportProblem(eq("Right-operand must contain the keyword 'active' followed by an optional version string: 'active'[:version], but was 'violates'.")); + } + + @Test + void evaluate_rightOperandWithVersion() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of(createPcfCredential().build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement.pcf", Operator.EQ, "active:1.0.0", permission, context)).isTrue(); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement.pcf", Operator.EQ, "active:5.3.1", permission, context)).isFalse(); + } + + @Test + void evaluate_rightOperand() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of(createPcfCredential().build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement.pcf", Operator.EQ, "active", permission, context)).isTrue(); + } + } + + @Nested + class NewLeftOperand { + @Test + void evaluate_leftOperandNotFrameworkLiteral() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of(createPcfCredential().build()) + )); + assertThat(function.evaluate("Foobar", Operator.EQ, "active:0.4.2", permission, context)).isFalse(); + verify(context).reportProblem(eq("Constraint left-operand must start with 'FrameworkAgreement' but was 'Foobar'.")); + } + + @Test + void evaluate_rightOperand_onlySubtype() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of(createPcfCredential().build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement", Operator.EQ, "pcf", permission, context)).isTrue(); + } + + @Test + void evaluate_rightOperand_withVersion() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of(createPcfCredential().build()) + )); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement", Operator.EQ, "pcf:1.0.0", permission, context)).isTrue(); + assertThat(function.evaluate(CX_POLICY_NS + "FrameworkAgreement", Operator.EQ, "pcf:4.2.0", permission, context)).isFalse(); + } + + @Test + void evaluate_rightOperandMissesSubtype() { + when(participantAgent.getClaims()).thenReturn(Map.of( + "vc", List.of(createPcfCredential().build()) + )); + assertThat(function.evaluate("FrameworkAgreement", Operator.EQ, ":1.0.0", permission, context)).isFalse(); + } + } } diff --git a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/membership/MembershipConstraintFunctionTest.java b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/membership/MembershipConstraintFunctionTest.java new file mode 100644 index 000000000..69839f3e6 --- /dev/null +++ b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/membership/MembershipConstraintFunctionTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.policy.cx.membership; + +import org.eclipse.edc.policy.engine.spi.PolicyContext; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.spi.agent.ParticipantAgent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_POLICY_NS; +import static org.eclipse.tractusx.edc.policy.cx.CredentialFunctions.createMembershipCredential; +import static org.eclipse.tractusx.edc.policy.cx.CredentialFunctions.createPcfCredential; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class MembershipConstraintFunctionTest { + + private final MembershipCredentialConstraintFunction function = new MembershipCredentialConstraintFunction(); + private final PolicyContext context = mock(); + private ParticipantAgent participantAgent; + + @BeforeEach + void setup() { + participantAgent = mock(ParticipantAgent.class); + when(context.getContextData(eq(ParticipantAgent.class))) + .thenReturn(participantAgent); + } + + @Test + void evaluate_leftOperandInvalid() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createMembershipCredential().build()))); + assertThat(function.evaluate(CX_POLICY_NS + "foobar", Operator.EQ, "active", null, context)).isFalse(); + verify(context).reportProblem(startsWith("Invalid left-operand: must be 'Membership', but was")); + } + + @Test + void evaluate_noParticipantAgentOnContext() { + when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(null); + assertThat(function.evaluate(CX_POLICY_NS + "Membership", Operator.EQ, "active", null, context)).isFalse(); + verify(context).reportProblem("Required PolicyContext data not found: org.eclipse.edc.spi.agent.ParticipantAgent"); + } + + @Test + void evaluate_noVcClaimOnParticipantAgent() { + assertThat(function.evaluate(CX_POLICY_NS + "Membership", Operator.EQ, "active", null, context)).isFalse(); + verify(context).reportProblem(eq("ParticipantAgent did not contain a 'vc' claim.")); + } + + @Test + void evaluate_vcClaimEmpty() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of())); + assertThat(function.evaluate(CX_POLICY_NS + "Membership", Operator.EQ, "active", null, context)).isFalse(); + verify(context).reportProblem(eq("ParticipantAgent contains a 'vc' claim but it did not contain any VerifiableCredentials.")); + } + + @Test + void evaluate_vcClaimNotList() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", new Object())); + assertThat(function.evaluate(CX_POLICY_NS + "Membership", Operator.EQ, "active", null, context)).isFalse(); + verify(context).reportProblem(eq("ParticipantAgent contains a 'vc' claim, but the type is incorrect. Expected java.util.List, received java.lang.Object.")); + } + + @Test + void evaluate_rightOperandNotActive() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createMembershipCredential().build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Membership", Operator.EQ, "invalid", null, context)).isFalse(); + verify(context).reportProblem(eq("Right-operand must be equal to 'active', but was 'invalid'")); + } + + @Test + void evaluate_whenSingleCredentialFound() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createMembershipCredential().build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Membership", Operator.EQ, "active", null, context)).isTrue(); + } + + @Test + void evaluate_whenMultipleCredentialsFound() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createMembershipCredential().build(), + createMembershipCredential().build(), + createPcfCredential().build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Membership", Operator.EQ, "active", null, context)).isTrue(); + } + + @Test + void evaluate_whenCredentialNotFound() { + when(participantAgent.getClaims()).thenReturn(Map.of("vc", List.of(createPcfCredential().build()))); + assertThat(function.evaluate(CX_POLICY_NS + "Membership", Operator.EQ, "active", null, context)).isFalse(); + } +} \ No newline at end of file diff --git a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunctionTest.java b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunctionTest.java deleted file mode 100644 index 5fe674e0b..000000000 --- a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunctionTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.policy.cx.summary; - -import com.fasterxml.jackson.core.JsonProcessingException; -import jakarta.json.JsonObject; -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.spi.agent.ParticipantAgent; -import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.policy.model.Operator.EQ; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_SUMMARY_NS_V1; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.createObjectMapper; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.expand; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class SummaryConstraintFunctionTest { - public static final String CX_QUALITY = "QualityCredential"; - private static final Map CONTEXT_CACHE = Map.of(CX_SUMMARY_NS_V1, SummaryContext.SUMMARY_CONTEXT); - private Permission permission; - private PolicyContext context; - - @Test - void verify_constraint_success() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); - - var function = new SummaryConstraintFunction(CX_QUALITY); - - when(context.getContextData(ParticipantAgent.class)).thenReturn(new ParticipantAgent(Map.of(VP_PROPERTY, vp), Map.of())); - - var result = function.evaluate(EQ, "active", permission, context); - - assertThat(result).isTrue(); - - verify(context, atLeastOnce()).getContextData(ParticipantAgent.class); - } - - @BeforeEach - void setUp() { - permission = Permission.Builder.newInstance().build(); - context = mock(PolicyContext.class); - } -} diff --git a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunctionsProviderTest.java b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunctionsProviderTest.java deleted file mode 100644 index 4603dec70..000000000 --- a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryConstraintFunctionsProviderTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.policy.cx.summary; - -import org.eclipse.edc.policy.engine.spi.PolicyEngine; -import org.eclipse.edc.policy.engine.spi.RuleBindingRegistry; -import org.eclipse.edc.policy.model.Permission; -import org.junit.jupiter.api.Test; - -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.CATALOG_REQUEST_SCOPE; -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.CATALOG_SCOPE; -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.NEGOTIATION_REQUEST_SCOPE; -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.NEGOTIATION_SCOPE; -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.TRANSFER_PROCESS_REQUEST_SCOPE; -import static org.eclipse.tractusx.edc.policy.cx.common.PolicyScopes.TRANSFER_PROCESS_SCOPE; -import static org.eclipse.tractusx.edc.policy.cx.summary.SummaryConstraintFunctionsProvider.registerBindings; -import static org.eclipse.tractusx.edc.policy.cx.summary.SummaryConstraintFunctionsProvider.registerFunctions; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -class SummaryConstraintFunctionsProviderTest { - - @Test - void verify_function_registrations() { - var policyEngine = mock(PolicyEngine.class); - - registerFunctions(policyEngine); - - assertTokenFunctionsRegistered(CATALOG_REQUEST_SCOPE, policyEngine); - assertTokenFunctionsRegistered(NEGOTIATION_REQUEST_SCOPE, policyEngine); - assertTokenFunctionsRegistered(TRANSFER_PROCESS_REQUEST_SCOPE, policyEngine); - - SummaryConstraintFunctionsProvider.CREDENTIAL_MAPPINGS.forEach((credentialName, summaryType) -> { - assertSummaryFunctionsRegistered(CATALOG_SCOPE, policyEngine, credentialName); - assertSummaryFunctionsRegistered(NEGOTIATION_SCOPE, policyEngine, credentialName); - assertSummaryFunctionsRegistered(TRANSFER_PROCESS_SCOPE, policyEngine, credentialName); - }); - } - - @Test - void verify_binding_registrations() { - var bindingRegistry = mock(RuleBindingRegistry.class); - - registerBindings(bindingRegistry); - - assertRuleTypeRegistered("Membership", bindingRegistry); - assertRuleTypeRegistered("Dismantler", bindingRegistry); - assertRuleTypeRegistered("FrameworkAgreement.pcf", bindingRegistry); - assertRuleTypeRegistered("FrameworkAgreement.sustainability", bindingRegistry); - assertRuleTypeRegistered("FrameworkAgreement.quality", bindingRegistry); - assertRuleTypeRegistered("FrameworkAgreement.traceability", bindingRegistry); - assertRuleTypeRegistered("FrameworkAgreement.behavioraltwin", bindingRegistry); - } - - private void assertTokenFunctionsRegistered(String scope, PolicyEngine policyEngine) { - verify(policyEngine, times(1)).registerPreValidator(eq(scope), any()); - } - - private void assertSummaryFunctionsRegistered(String scope, PolicyEngine policyEngine, String credentialName) { - verify(policyEngine, times(1)).registerFunction( - eq(scope), - eq(Permission.class), - eq(credentialName), - any(SummaryConstraintFunction.class)); - } - - private void assertRuleTypeRegistered(String ruleType, RuleBindingRegistry bindingRegistry) { - verify(bindingRegistry, times(1)).bind(ruleType, CATALOG_REQUEST_SCOPE); - verify(bindingRegistry, times(1)).bind(ruleType, CATALOG_SCOPE); - verify(bindingRegistry, times(1)).bind(ruleType, NEGOTIATION_REQUEST_SCOPE); - verify(bindingRegistry, times(1)).bind(ruleType, NEGOTIATION_SCOPE); - verify(bindingRegistry, times(1)).bind(ruleType, TRANSFER_PROCESS_REQUEST_SCOPE); - verify(bindingRegistry, times(1)).bind(ruleType, TRANSFER_PROCESS_SCOPE); - } - -} diff --git a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryTokenPolicyFunctionTest.java b/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryTokenPolicyFunctionTest.java deleted file mode 100644 index 82e4f175e..000000000 --- a/edc-extensions/cx-policy/src/test/java/org/eclipse/tractusx/edc/policy/cx/summary/SummaryTokenPolicyFunctionTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.policy.cx.summary; - -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Policy; -import org.eclipse.edc.spi.iam.RequestScope; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_SUMMARY_CREDENTIAL; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -class SummaryTokenPolicyFunctionTest { - - @Test - void verify_add_credential() { - var function = new SummaryTokenPolicyFunction(); - - var context = mock(PolicyContext.class); - var builder = RequestScope.Builder.newInstance(); - when(context.getContextData(eq(RequestScope.Builder.class))).thenReturn(builder); - - var policy = Policy.Builder.newInstance().build(); - - function.apply(policy, context); - - assertThat(builder.build().getScopes().contains(CX_SUMMARY_CREDENTIAL)).isTrue(); - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/ArrayUtil.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/ArrayUtil.java deleted file mode 100644 index 65c6475c2..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/ArrayUtil.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * Copyright (c) 2021,2022 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.edc.data.encryption; - -public class ArrayUtil { - - private ArrayUtil() { - } - - public static byte[] concat(byte[] a, byte[] b) { - byte[] c = new byte[a.length + b.length]; - System.arraycopy(a, 0, c, 0, a.length); - System.arraycopy(b, 0, c, a.length, b.length); - return c; - } - - public static byte[] subArray(byte[] a, int startIndex, int length) { - if (startIndex + length > a.length) { - throw new IllegalArgumentException("Start index + length is greater than array length"); - } - - byte[] b = new byte[length]; - System.arraycopy(a, startIndex, b, 0, length); - return b; - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/TxEncryptorExtension.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/TxEncryptorExtension.java deleted file mode 100644 index d6a246824..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/TxEncryptorExtension.java +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.data.encryption; - -import org.eclipse.edc.connector.transfer.dataplane.security.NoopDataEncrypter; -import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provider; -import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.data.encryption.aes.AesEncryptor; - -import java.util.Objects; - -import static java.lang.String.format; - -@Extension(value = "Registers a DataEncryptor") -public class TxEncryptorExtension implements ServiceExtension { - public static final String NAME = "Data Encryption Extension"; - public static final String AES_ALGORITHM = "AES"; - - @Setting(value = "Vault alias, under which the encryption key is stored in the vault", required = true) - public static final String ENCRYPTION_KEY_ALIAS = "edc.data.encryption.keys.alias"; - - @Setting(value = "Algorithm to be used", defaultValue = AES_ALGORITHM) - public static final String ENCRYPTION_ALGORITHM = "edc.data.encryption.algorithm"; - @Setting(value = "DEPRECATED - caching keys is unsafe and is not used anymore. Will be ignored.") - @Deprecated - public static final String CACHING_ENABLED = "edc.data.encryption.caching.enabled"; - @Setting(value = "DEPRECATED - caching keys is unsafe and is not used anymore. Will be ignored.") - @Deprecated - public static final String CACHING_SECONDS = "edc.data.encryption.caching.seconds"; - @Inject - private Vault vault; - - @Override - public String name() { - return NAME; - } - - @Provider - public DataEncrypter createEncryptor(ServiceExtensionContext context) { - var keyAlias = context.getSetting(ENCRYPTION_KEY_ALIAS, null); - Objects.requireNonNull(keyAlias, ENCRYPTION_KEY_ALIAS + " property not found"); - var algorithm = context.getSetting(ENCRYPTION_ALGORITHM, AES_ALGORITHM); - - if (context.getSetting(CACHING_ENABLED, null) != null || context.getSetting(CACHING_SECONDS, null) != null) { - context.getMonitor().warning(format("Caching the secret keys was deprecated because it is unsafe. " + - "This version will ignore the properties '%s' and '%s' and will NOT cache the keys", CACHING_ENABLED, CACHING_SECONDS)); - } - - if (algorithm.equalsIgnoreCase(AES_ALGORITHM)) { - return new AesEncryptor(vault, keyAlias); - } - context.getMonitor().warning(format("Algorithm %s is not known, will use a NOOP encryptor!", algorithm)); - return new NoopDataEncrypter(); - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/aes/AesEncryptor.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/aes/AesEncryptor.java deleted file mode 100644 index 6aa47938d..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/aes/AesEncryptor.java +++ /dev/null @@ -1,147 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.data.encryption.aes; - -import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.data.encryption.ArrayUtil; - -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Base64; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import static org.eclipse.tractusx.edc.data.encryption.ArrayUtil.concat; - -/** - * This class implements the {@link DataEncrypter} interface by encrypting and decrypting an input string using the GCM mode. - * Furthermore, it uses the {@code AES/CBC/PKCS5Padding} alongside the {@link java.security.SecureRandom} class to generate initialisation vectors (IV). - */ -public class AesEncryptor implements DataEncrypter { - - public static final int GCM_AUTH_TAG_LENGTH = 128; - public static final String AES = "AES"; - private static final String AES_GCM = "AES/GCM/NoPadding"; - private static final int IV_SIZE_BYTES = 16; // 16 bytes - private static final int[] ALLOWED_SIZES = new int[] {16, 24, 32}; // AES allows for 128, 192 or 256 bits - private final Vault vault; - private final String secretAlias; - private final Base64.Decoder decoder = Base64.getDecoder(); - private final Base64.Encoder encoder = Base64.getEncoder(); - private final Cipher cipher; - - /** - * Initializes the encryptor with private key (stored in the vault). The key, that was stored in the vault, is expected to be in Base64 format, and - * this encryptor will produce encrypted text in Base64. - * - * @param vault A Vault instance that contains the encryption key. Keys should never be held in memory. - * @param secretAlias The alias under which the key was stored in the vault. The key, which is stored in the vault under the given alias, must be in Base64 format - */ - public AesEncryptor(Vault vault, String secretAlias) { - this.vault = vault; - this.secretAlias = secretAlias; - try { - cipher = Cipher.getInstance(AES_GCM); - } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { - throw new EdcException("Error while instantiating Cipher", e); - } - } - - - @Override - public String encrypt(String raw) { - var key = getKey(secretAlias); - var iv = generateIv(IV_SIZE_BYTES); - try { - var gcmSpec = new GCMParameterSpec(GCM_AUTH_TAG_LENGTH, iv); - cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec); - - byte[] cipherText = cipher.doFinal(raw.getBytes()); - var ivAndCipher = concat(iv, cipherText); - - return encoder.encodeToString(ivAndCipher); - } catch (InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) { - throw new EdcException("Error while encrypting", e); - } - } - - /** - * The encrypted text is expected in Base64 format: it is simply the ciphertext encoded as Base64 - */ - @Override - public String decrypt(String encrypted) { - var key = getKey(secretAlias); - // will throw IllegalArgumentException if not base64 - var decodedCipher = decoder.decode(encrypted); - - if (decodedCipher.length < IV_SIZE_BYTES) { - throw new IllegalArgumentException("Decoded ciphertext was shorter than the IV size (" + IV_SIZE_BYTES + ")"); - } - var iv = ArrayUtil.subArray(decodedCipher, 0, IV_SIZE_BYTES); - var cipherText = ArrayUtil.subArray(decodedCipher, IV_SIZE_BYTES, decodedCipher.length - IV_SIZE_BYTES); - - var gcmSpec = new GCMParameterSpec(GCM_AUTH_TAG_LENGTH, iv); - - try { - cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec); - byte[] plainText = cipher.doFinal(cipherText); - return new String(plainText); - } catch (InvalidAlgorithmParameterException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) { - throw new EdcException("Error while decrypting", e); - } - } - - private byte[] generateIv(int length) { - byte[] iv = new byte[length]; - new SecureRandom().nextBytes(iv); - return iv; - } - - private SecretKey getKey(String alias) { - var secretBase64 = vault.resolveSecret(alias); - if (secretBase64 == null) { - throw new EdcException("Cannot perform AES encryption: secret key not found in vault"); - } - var decoded = decoder.decode(secretBase64); - if (isAllowedSize(decoded)) { - return new SecretKeySpec(decoded, AES); - } - throw new EdcException("Expected a key size of 16, 24 or 32 bytes byt found " + decoded.length); - } - - /** - * Check is the decoded byte array is 16, 24 or 32 bytes in size - */ - private boolean isAllowedSize(byte[] decoded) { - return Arrays.stream(ALLOWED_SIZES).anyMatch(i -> i == decoded.length); - } - - -} diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/TxEncryptorExtensionTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/TxEncryptorExtensionTest.java deleted file mode 100644 index e5fffc02a..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/TxEncryptorExtensionTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.data.encryption; - -import org.eclipse.edc.connector.transfer.dataplane.security.NoopDataEncrypter; -import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.data.encryption.aes.AesEncryptor; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.data.encryption.TxEncryptorExtension.CACHING_ENABLED; -import static org.eclipse.tractusx.edc.data.encryption.TxEncryptorExtension.CACHING_SECONDS; -import static org.eclipse.tractusx.edc.data.encryption.TxEncryptorExtension.ENCRYPTION_ALGORITHM; -import static org.eclipse.tractusx.edc.data.encryption.TxEncryptorExtension.ENCRYPTION_KEY_ALIAS; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.startsWith; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(DependencyInjectionExtension.class) -class TxEncryptorExtensionTest { - - private final Monitor monitor = mock(); - - @BeforeEach - void setup(ServiceExtensionContext context) { - context.registerService(Monitor.class, monitor); - when(context.getSetting(ENCRYPTION_KEY_ALIAS, null)).thenReturn("test-key"); - } - - @Test - void createEncryptor_noConfig_createsDefault(ServiceExtensionContext context, TxEncryptorExtension extension) { - var encryptor = extension.createEncryptor(context); - assertThat(encryptor).isInstanceOf(AesEncryptor.class); - } - - @Test - void createEncryptor_otherAlgorithm_createsNoop(ServiceExtensionContext context, TxEncryptorExtension extension) { - when(context.getSetting(eq(ENCRYPTION_ALGORITHM), any())).thenReturn("some-algorithm"); - var encryptor = extension.createEncryptor(context); - assertThat(encryptor).isInstanceOf(NoopDataEncrypter.class); - verify(monitor).warning(eq("Algorithm some-algorithm is not known, will use a NOOP encryptor!")); - } - - @Test - void createEncryptor_withPropertyEqualsAes(ServiceExtensionContext context, TxEncryptorExtension extension) { - when(context.getSetting(eq(ENCRYPTION_ALGORITHM), any())).thenReturn("AES"); - var encryptor = extension.createEncryptor(context); - assertThat(encryptor).isInstanceOf(AesEncryptor.class); - } - - @ParameterizedTest - @ValueSource(strings = { CACHING_ENABLED, CACHING_SECONDS }) - void verifyDeprecationWarnings(String deprecatedSetting, ServiceExtensionContext context, TxEncryptorExtension extension) { - when(context.getSetting(eq(deprecatedSetting), any())).thenReturn("doesn't matter"); - extension.createEncryptor(context); - verify(monitor).warning(startsWith("Caching the secret keys was deprecated because it is unsafe")); - } -} diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/aes/AesEncryptorTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/aes/AesEncryptorTest.java deleted file mode 100644 index 1cd99abd3..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/aes/AesEncryptorTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.data.encryption.aes; - -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.security.Vault; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import java.security.SecureRandom; -import java.util.Base64; -import javax.crypto.BadPaddingException; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -class AesEncryptorTest { - - private static final String TESTALIAS = "test-alias"; - private final Vault vaultMock = mock(); - private AesEncryptor encryptor; - - @BeforeEach - void setup() { - encryptor = new AesEncryptor(vaultMock, TESTALIAS); - } - - @ParameterizedTest - @ValueSource(ints = {16, 24, 32}) - void encrypt(int validSize) { - when(vaultMock.resolveSecret(eq(TESTALIAS))).thenReturn(generateBase64(validSize)); - - var encrypted = encryptor.encrypt("foobar barbaz"); - assertThat(encrypted).isNotNull(); - } - - @ParameterizedTest - @ValueSource(ints = {0, 1, 17, 33, 1024, 8192}) - void encrypt_invalidKeySize(int invalidKeySize) { - when(vaultMock.resolveSecret(eq(TESTALIAS))).thenReturn(generateBase64(invalidKeySize)); - assertThatThrownBy(() -> encryptor.encrypt("hello world!")) - .isInstanceOf(EdcException.class) - .hasMessage("Expected a key size of 16, 24 or 32 bytes byt found " + invalidKeySize); - } - - @Test - void encrypt_secretNotBase64() { - when(vaultMock.resolveSecret(eq(TESTALIAS))).thenReturn("not-base64"); - - assertThatThrownBy(() -> encryptor.encrypt("hello world!")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageStartingWith("Illegal base64 character"); - } - - @Test - void encrypt_secretNotExist() { - assertThatThrownBy(() -> encryptor.encrypt("hello world!")) - .isInstanceOf(EdcException.class) - .hasMessage("Cannot perform AES encryption: secret key not found in vault"); - } - - @Test - void decrypt() { - when(vaultMock.resolveSecret(eq(TESTALIAS))).thenReturn(generateBase64(32)); - var rawText = "hello world!"; - var encrypted = encryptor.encrypt(rawText); - assertThat(encryptor.decrypt(encrypted)).isEqualTo(rawText); - } - - @Test - void decrypt_wrongSecretKey() { - when(vaultMock.resolveSecret(eq(TESTALIAS))).thenReturn(generateBase64(32)); - var rawText = "hello world!"; - var encrypted = encryptor.encrypt(rawText); - when(vaultMock.resolveSecret(eq(TESTALIAS))).thenReturn(generateBase64(32)); - assertThatThrownBy(() -> encryptor.decrypt(encrypted)).isInstanceOf(EdcException.class) - .hasMessage("Error while decrypting") - .hasRootCauseInstanceOf(BadPaddingException.class); - } - - @Test - void decrypt_keyNotBase64() { - when(vaultMock.resolveSecret(eq(TESTALIAS))).thenReturn("not-base64"); - assertThatThrownBy(() -> encryptor.decrypt("encrypted-text")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageStartingWith("Illegal base64 character"); - } - - @Test - void decrypt_ciphertextTooShort() { - when(vaultMock.resolveSecret(eq(TESTALIAS))).thenReturn(generateBase64(32)); - var cipherText = Base64.getEncoder().encodeToString("asdf".getBytes()); - assertThatThrownBy(() -> encryptor.decrypt(cipherText)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageStartingWith("Decoded ciphertext was shorter than the IV size (16)"); - } - - private String generateBase64(int validSize) { - var bytes = new byte[validSize]; - - new SecureRandom().nextBytes(bytes); - return Base64.getEncoder().encodeToString(bytes); - - } -} diff --git a/edc-extensions/data-encryption/build.gradle.kts b/edc-extensions/data-flow-properties-provider/build.gradle.kts similarity index 80% rename from edc-extensions/data-encryption/build.gradle.kts rename to edc-extensions/data-flow-properties-provider/build.gradle.kts index 0e2e546dc..1bb930aa9 100644 --- a/edc-extensions/data-encryption/build.gradle.kts +++ b/edc-extensions/data-flow-properties-provider/build.gradle.kts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -18,12 +18,14 @@ ********************************************************************************/ plugins { + `maven-publish` `java-library` } dependencies { - api(libs.edc.spi.core) - implementation(libs.edc.spi.dataplane.transfer) - implementation(libs.edc.dpf.transfer) + implementation(libs.edc.spi.transfer) + implementation(project(":spi:core-spi")) + implementation(project(":spi:bdrs-client-spi")) + testImplementation(libs.edc.junit) } diff --git a/edc-extensions/data-flow-properties-provider/src/main/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProvider.java b/edc-extensions/data-flow-properties-provider/src/main/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProvider.java new file mode 100644 index 000000000..d524d3c55 --- /dev/null +++ b/edc-extensions/data-flow-properties-provider/src/main/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProvider.java @@ -0,0 +1,56 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.flow; + +import org.eclipse.edc.connector.controlplane.transfer.spi.flow.DataFlowPropertiesProvider; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess; +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.spi.response.ResponseStatus; +import org.eclipse.edc.spi.response.StatusResult; +import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; +import org.eclipse.tractusx.edc.spi.identity.mapper.BdrsClient; + +import java.util.Map; + +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.AUDIENCE_PROPERTY; + +/** + * Extension of {@link DataFlowPropertiesProvider} which provides additional properties in the {@link DataFlowStartMessage} + * like the DID of the counter-party BPN. The resolution is made with the {@link BdrsClient} + */ +public class TxDataFlowPropertiesProvider implements DataFlowPropertiesProvider { + + + private final BdrsClient bdrsClient; + + public TxDataFlowPropertiesProvider(BdrsClient bdrsClient) { + this.bdrsClient = bdrsClient; + } + + @Override + public StatusResult> propertiesFor(TransferProcess transferProcess, Policy policy) { + var did = bdrsClient.resolve(policy.getAssignee()); + if (did != null) { + return StatusResult.success(Map.of(AUDIENCE_PROPERTY, did)); + } else { + return StatusResult.failure(ResponseStatus.FATAL_ERROR, "Failed to fetch did for BPN %s".formatted(policy.getAssignee())); + } + } +} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwCredentialClientExtension.java b/edc-extensions/data-flow-properties-provider/src/main/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProviderExtension.java similarity index 54% rename from edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwCredentialClientExtension.java rename to edc-extensions/data-flow-properties-provider/src/main/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProviderExtension.java index c20317621..8493b1a12 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwCredentialClientExtension.java +++ b/edc-extensions/data-flow-properties-provider/src/main/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProviderExtension.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,42 +17,27 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.miw; +package org.eclipse.tractusx.edc.flow; -import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.connector.controlplane.transfer.spi.flow.DataFlowPropertiesProvider; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provider; -import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClient; -import org.eclipse.tractusx.edc.iam.ssi.miw.credentials.SsiMiwCredentialClient; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiCredentialClient; +import org.eclipse.tractusx.edc.spi.identity.mapper.BdrsClient; -@Extension(SsiMiwCredentialClientExtension.EXTENSION_NAME) -public class SsiMiwCredentialClientExtension implements ServiceExtension { +import static org.eclipse.tractusx.edc.flow.TxDataFlowPropertiesProviderExtension.NAME; - public static final String EXTENSION_NAME = "SSI MIW Credential Client"; +@Extension(NAME) +public class TxDataFlowPropertiesProviderExtension implements ServiceExtension { - @Inject - private MiwApiClient apiClient; - - @Inject - private JsonLd jsonLdService; + protected static final String NAME = "Tractus-X Data flow properties provider extension"; @Inject - private Monitor monitor; - - @Override - public String name() { - return EXTENSION_NAME; - } - + private BdrsClient bdrsClient; @Provider - public SsiCredentialClient credentialVerifier() { - return new SsiMiwCredentialClient(apiClient, jsonLdService, monitor); + public DataFlowPropertiesProvider dataFlowPropertiesProvider() { + return new TxDataFlowPropertiesProvider(bdrsClient); } - - } diff --git a/core/edr-cache-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/data-flow-properties-provider/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 86% rename from core/edr-cache-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/data-flow-properties-provider/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 82993677d..3a7b065a9 100644 --- a/core/edr-cache-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/data-flow-properties-provider/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -1,5 +1,5 @@ ################################################################################# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. @@ -17,4 +17,4 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# -org.eclipse.tractusx.edc.edr.core.EdrCacheCoreExtension +org.eclipse.tractusx.edc.flow.TxDataFlowPropertiesProviderExtension \ No newline at end of file diff --git a/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/EdrCoreExtensionTest.java b/edc-extensions/data-flow-properties-provider/src/test/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProviderExtensionTest.java similarity index 57% rename from core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/EdrCoreExtensionTest.java rename to edc-extensions/data-flow-properties-provider/src/test/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProviderExtensionTest.java index 1b0381ba0..11e095ba6 100644 --- a/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/EdrCoreExtensionTest.java +++ b/edc-extensions/data-flow-properties-provider/src/test/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProviderExtensionTest.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,14 +17,11 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.edr.core; +package org.eclipse.tractusx.edc.flow; -import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.edr.core.manager.EdrManagerImpl; -import org.eclipse.tractusx.edc.edr.spi.EdrManager; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -33,20 +30,17 @@ import static org.mockito.Mockito.mock; @ExtendWith(DependencyInjectionExtension.class) -public class EdrCoreExtensionTest { +class TxDataFlowPropertiesProviderExtensionTest { + + private final Monitor monitor = mock(); @BeforeEach - void setUp(ServiceExtensionContext context) { - context.registerService(ContractNegotiationService.class, mock(ContractNegotiationService.class)); - context.registerService(EndpointDataReferenceCache.class, mock(EndpointDataReferenceCache.class)); + void setup(ServiceExtensionContext context) { + context.registerService(Monitor.class, monitor); } @Test - void shouldInitializeTheExtension(ServiceExtensionContext context, EdrCoreExtension extension) { - extension.initialize(context); - - var service = context.getService(EdrManager.class); - assertThat(service).isInstanceOf(EdrManagerImpl.class); - + void createMapper(TxDataFlowPropertiesProviderExtension extension) { + assertThat(extension.dataFlowPropertiesProvider()).isInstanceOf(TxDataFlowPropertiesProvider.class); } -} +} \ No newline at end of file diff --git a/edc-extensions/data-flow-properties-provider/src/test/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProviderTest.java b/edc-extensions/data-flow-properties-provider/src/test/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProviderTest.java new file mode 100644 index 000000000..0c7efcf05 --- /dev/null +++ b/edc-extensions/data-flow-properties-provider/src/test/java/org/eclipse/tractusx/edc/flow/TxDataFlowPropertiesProviderTest.java @@ -0,0 +1,63 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.flow; + +import org.assertj.core.api.Assertions; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess; +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.tractusx.edc.spi.identity.mapper.BdrsClient; +import org.junit.jupiter.api.Test; + +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.AUDIENCE_PROPERTY; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TxDataFlowPropertiesProviderTest { + + private final BdrsClient client = mock(); + private final TxDataFlowPropertiesProvider provider = new TxDataFlowPropertiesProvider(client); + + + @Test + void propertiesFor() { + + var bpn = "bpn"; + var did = "did"; + + when(client.resolve(bpn)).thenReturn(did); + var result = provider.propertiesFor(TransferProcess.Builder.newInstance().build(), Policy.Builder.newInstance().assignee(bpn).build()); + + assertThat(result).isSucceeded().satisfies(properties -> { + Assertions.assertThat(properties).containsEntry(AUDIENCE_PROPERTY, did); + }); + } + + @Test + void propertiesFor_fails_whenResolutionIsNull() { + + var bpn = "bpn"; + + when(client.resolve(bpn)).thenReturn(null); + var result = provider.propertiesFor(TransferProcess.Builder.newInstance().build(), Policy.Builder.newInstance().assignee(bpn).build()); + + assertThat(result).isFailed().withFailMessage("Failed to fetch did for BPN %s".formatted(bpn)); + } +} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/README.md b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/README.md deleted file mode 100644 index a931a6d46..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# DataPlane Proxy Provider API - -This extension provide additional dataplane extension for proxying requests to backends. -The configuration of the proxy can be found [here](../edc-dataplane-proxy-provider-core/README.md) - -The provider proxy is mounted into the EDC default context, and it's available in the path `/gateway` - -The proxy will look for subPath in the request and match the subpath with the configured ones and forward -the rest of the path and query parameters. - -For example: - -with this URL `http://localhost:8181/api/gateway/aas/test` it will look for the `aas` alias in the configuration, -and it will compose the final url to call based on that configuration appending to it the remaining part of the path and query -parameters. - -When the proxy receive a request, it must contain the EDR, which will be decoded with the `token` validation endpoint. - -## Configuration - -| Key | Required | Default | Description | -|--------------------------------------------|----------------------------------------------------------------------------------------| -| tx.dpf.provider.proxy.thread.pool | | 10 | Thread pool size for the provider data plane proxy gateway | -| edc.dataplane.token.validation.endpoint | x | | URL of the token validation endpoint | -| web.http.gateway.context.path | | | Path to register the ProviderGatewayController to | -| web.http.gateway.context.port | | | Port to register the ProviderGatewayController to | diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/build.gradle.kts b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/build.gradle.kts deleted file mode 100644 index 4244a6162..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/build.gradle.kts +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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 { - `java-library` - id("io.swagger.core.v3.swagger-gradle-plugin") -} - -dependencies { - - implementation(libs.edc.spi.http) - implementation(libs.edc.util) - implementation(libs.edc.dpf.util) - implementation(libs.edc.ext.http) - implementation(libs.edc.spi.jwt) - implementation(libs.edc.token.core) - implementation(libs.edc.spi.dataplane.http) - - implementation(libs.jakarta.rsApi) - implementation(libs.nimbus.jwt) - - implementation(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-spi")) - - testImplementation(libs.edc.junit) - testImplementation(libs.okhttp.mockwebserver) -} - diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/DataPlaneProxyProviderApiExtension.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/DataPlaneProxyProviderApiExtension.java deleted file mode 100644 index 644855e2c..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/DataPlaneProxyProviderApiExtension.java +++ /dev/null @@ -1,122 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.api; - -import org.eclipse.edc.connector.dataplane.spi.pipeline.PipelineService; -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.http.EdcHttpClient; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.edc.web.spi.WebService; -import org.eclipse.tractusx.edc.dataplane.proxy.provider.api.gateway.ProviderGatewayController; -import org.eclipse.tractusx.edc.dataplane.proxy.provider.api.validation.ProxyProviderDataAddressResolver; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.authorization.AuthorizationHandlerRegistry; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration.GatewayConfigurationRegistry; - -import java.util.concurrent.ExecutorService; - -import static java.util.concurrent.Executors.newFixedThreadPool; - -/** - * Adds the consumer proxy data plane API. - */ -@Extension(value = DataPlaneProxyProviderApiExtension.NAME) -public class DataPlaneProxyProviderApiExtension implements ServiceExtension { - public static final int DEFAULT_THREAD_POOL = 10; - static final String NAME = "Data Plane Proxy Provider API"; - @Setting(value = "Thread pool size for the provider data plane proxy gateway", type = "int") - private static final String THREAD_POOL_SIZE = "tx.dpf.provider.proxy.thread.pool"; - - @Setting(value = "Path to register the ProviderGatewayController to", type = "String") - private static final String WEB_HTTP_GATEWAY_PATH_SETTING = "web.http.gateway.path"; - - @Setting(value = "Port to register the ProviderGatewayController to", type = "int") - private static final String WEB_HTTP_GATEWAY_PORT_SETTING = "web.http.gateway.port"; - - private static final String GATEWAY_CONTEXT = "gateway"; - - @Setting - private static final String CONTROL_PLANE_VALIDATION_ENDPOINT = "edc.dataplane.token.validation.endpoint"; - - @Inject - private WebService webService; - - @Inject - private PipelineService pipelineService; - - @Inject - private Monitor monitor; - - @Inject - private GatewayConfigurationRegistry configurationRegistry; - - @Inject - private AuthorizationHandlerRegistry authorizationRegistry; - - @Inject - private TypeManager typeManager; - - @Inject - private EdcHttpClient httpClient; - - private ExecutorService executorService; - - @Override - public String name() { - return NAME; - } - - @Override - public void initialize(ServiceExtensionContext context) { - executorService = newFixedThreadPool(context.getConfig().getInteger(THREAD_POOL_SIZE, DEFAULT_THREAD_POOL)); - - var validationEndpoint = context.getConfig().getString(CONTROL_PLANE_VALIDATION_ENDPOINT); - - var dataAddressResolver = new ProxyProviderDataAddressResolver(httpClient, validationEndpoint, typeManager.getMapper()); - - var controller = new ProviderGatewayController(pipelineService, - dataAddressResolver, - configurationRegistry, - authorizationRegistry, - executorService, - monitor); - - // If a setting for the port mapping for a separate gateway context exists, we assume the context also exists and register into that - // Otherwise we use the default context - if (context.getConfig().hasKey(WEB_HTTP_GATEWAY_PATH_SETTING) && context.getConfig().hasKey(WEB_HTTP_GATEWAY_PORT_SETTING)) { - webService.registerResource(GATEWAY_CONTEXT, controller); - } else { - webService.registerResource(controller); - } - } - - - @Override - public void shutdown() { - if (executorService != null) { - executorService.shutdown(); - } - } - -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/gateway/ProviderGatewayApi.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/gateway/ProviderGatewayApi.java deleted file mode 100644 index b4d169193..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/gateway/ProviderGatewayApi.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.api.gateway; - -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.ws.rs.container.AsyncResponse; -import jakarta.ws.rs.container.ContainerRequestContext; -import jakarta.ws.rs.container.Suspended; -import jakarta.ws.rs.core.Context; - -/** - * Open API definition. - */ -@OpenAPIDefinition -@Tag(name = "Data Plane Proxy API") -public interface ProviderGatewayApi { - - @Operation(responses = { - @ApiResponse(content = @Content(mediaType = "application/json"), description = "Gets asset data") - }) - void requestAsset(@Context ContainerRequestContext context, @Suspended AsyncResponse response); -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/gateway/ProviderGatewayController.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/gateway/ProviderGatewayController.java deleted file mode 100644 index 19d35432e..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/gateway/ProviderGatewayController.java +++ /dev/null @@ -1,254 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.api.gateway; - -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.container.AsyncResponse; -import jakarta.ws.rs.container.ContainerRequestContext; -import jakarta.ws.rs.container.Suspended; -import jakarta.ws.rs.core.Context; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.PathSegment; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.StreamingOutput; -import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; -import org.eclipse.edc.connector.dataplane.spi.pipeline.StreamResult; -import org.eclipse.edc.connector.dataplane.spi.pipeline.TransferService; -import org.eclipse.edc.connector.dataplane.spi.resolver.DataAddressResolver; -import org.eclipse.edc.connector.dataplane.util.sink.AsyncStreamingDataSink; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.authorization.AuthorizationHandlerRegistry; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration.GatewayConfiguration; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration.GatewayConfigurationRegistry; - -import java.util.Map; -import java.util.concurrent.ExecutorService; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; -import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; -import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; -import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; -import static jakarta.ws.rs.core.Response.Status.UNAUTHORIZED; -import static jakarta.ws.rs.core.Response.status; -import static java.lang.String.format; -import static java.lang.String.join; -import static java.util.UUID.randomUUID; -import static java.util.stream.Collectors.joining; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -import static org.eclipse.tractusx.edc.dataplane.proxy.provider.api.response.ResponseHelper.createMessageResponse; - -/** - * Implements the HTTP data proxy API. - */ -@Path("/" + ProviderGatewayController.GATEWAY_PATH) -public class ProviderGatewayController implements ProviderGatewayApi { - protected static final String GATEWAY_PATH = "gateway"; - private static final String BASE_URL = EDC_NAMESPACE + "baseUrl"; - private static final String ASYNC = "async"; - - private static final int ALIAS_SEGMENT = 1; - private static final String BEARER_PREFIX = "Bearer "; - - private final TransferService transferService; - private final GatewayConfigurationRegistry configurationRegistry; - private final AuthorizationHandlerRegistry authorizationRegistry; - - private final DataAddressResolver dataAddressResolver; - - private final Monitor monitor; - - private final ExecutorService executorService; - - public ProviderGatewayController(TransferService transferService, - DataAddressResolver dataAddressResolver, - GatewayConfigurationRegistry configurationRegistry, - AuthorizationHandlerRegistry authorizationRegistry, - ExecutorService executorService, - Monitor monitor) { - this.transferService = transferService; - this.dataAddressResolver = dataAddressResolver; - this.configurationRegistry = configurationRegistry; - this.authorizationRegistry = authorizationRegistry; - this.executorService = executorService; - this.monitor = monitor; - } - - @GET - @Path("/{paths: .+}") - @Override - public void requestAsset(@Context ContainerRequestContext context, @Suspended AsyncResponse response) { - var tokens = context.getHeaders().get(HttpHeaders.AUTHORIZATION); - if (tokens == null || tokens.isEmpty()) { - response.resume(createMessageResponse(UNAUTHORIZED, "No bearer token", context.getMediaType())); - return; - } - var token = tokens.get(0); - if (!token.startsWith(BEARER_PREFIX)) { - response.resume(createMessageResponse(UNAUTHORIZED, "Invalid bearer token", context.getMediaType())); - return; - } else { - token = token.substring(BEARER_PREFIX.length()); - } - - - var uriInfo = context.getUriInfo(); - var segments = uriInfo.getPathSegments(); - if (segments.size() < 3 || !GATEWAY_PATH.equals(segments.get(0).getPath())) { - response.resume(createMessageResponse(BAD_REQUEST, "Invalid path", context.getMediaType())); - return; - } - - var alias = segments.get(ALIAS_SEGMENT).getPath(); - var configuration = configurationRegistry.getConfiguration(alias); - if (configuration == null) { - response.resume(createMessageResponse(NOT_FOUND, "Unknown path", context.getMediaType())); - return; - } - - var httpDataAddressResult = extractSourceDataAddress(token, configuration); - HttpDataAddress httpDataAddress; - - if (httpDataAddressResult.succeeded()) { - httpDataAddress = httpDataAddressResult.getContent(); - } else { - monitor.debug("Request to token validation endpoint failed with errors: " + join(", ", httpDataAddressResult.getFailureMessages())); - response.resume(createMessageResponse(UNAUTHORIZED, "Failed to decode data address", context.getMediaType())); - return; - } - - if (!configuration.getProxiedPath().startsWith(httpDataAddress.getBaseUrl())) { - response.resume(createMessageResponse(NOT_FOUND, "Data address path not matched", context.getMediaType())); - return; - } - - // calculate the sub-path, which all segments after the GATEWAY segment, including the alias segment - var subPath = segments.stream().skip(1).map(PathSegment::getPath).collect(joining("/")); - if (!authenticate(token, configuration.getAuthorizationType(), subPath, context, response)) { - return; - } - - // calculate the request path, which all segments after the alias segment - var requestPath = segments.stream().skip(2).map(PathSegment::getPath).collect(joining("/")); - var flowRequest = createRequest(requestPath, configuration, httpDataAddress); - - // transfer the data asynchronously - - AsyncStreamingDataSink.AsyncResponseContext asyncResponseContext = callback -> { - StreamingOutput output = t -> callback.outputStreamConsumer().accept(t); - var resp = Response.ok(output).type(callback.mediaType()).build(); - return response.resume(resp); - }; - var sink = new AsyncStreamingDataSink(asyncResponseContext, executorService); - - try { - transferService.transfer(flowRequest, sink).whenComplete((result, throwable) -> handleCompletion(response, result, throwable)); - } catch (Exception e) { - reportError(response, e); - } - } - - private DataFlowRequest createRequest(String subPath, GatewayConfiguration configuration, HttpDataAddress httpDataAddress) { - var path = configuration.getProxiedPath() + "/" + subPath; - - var sourceAddressBuilder = HttpDataAddress.Builder.newInstance() - .property(BASE_URL, path); - - httpDataAddress.getAdditionalHeaders().forEach(sourceAddressBuilder::addAdditionalHeader); - - var destinationAddress = DataAddress.Builder.newInstance() - .type(ASYNC) - .build(); - - return DataFlowRequest.Builder.newInstance() - .processId(randomUUID().toString()) - .trackable(false) - .sourceDataAddress(sourceAddressBuilder.build()) - .destinationDataAddress(destinationAddress) - .traceContext(Map.of()) - .build(); - } - - private boolean authenticate(String token, String authType, String subPath, ContainerRequestContext context, AsyncResponse response) { - var handler = authorizationRegistry.getHandler(authType); - if (handler == null) { - var correlationId = randomUUID().toString(); - monitor.severe(format("Authentication handler not configured for type: %s [id: %s]", authType, correlationId)); - response.resume(createMessageResponse(INTERNAL_SERVER_ERROR, format("Internal server error: %s", correlationId), context.getMediaType())); - return false; - } - - var authResponse = handler.authorize(token, subPath); - if (authResponse.failed()) { - response.resume(status(UNAUTHORIZED).build()); - return false; - } - return true; - } - - /** - * Handles a request completion, checking for errors. If no errors are present, nothing needs to be done as the response will have already been written to the client. - */ - private void handleCompletion(AsyncResponse response, StreamResult result, Throwable throwable) { - if (result != null && result.failed()) { - switch (result.reason()) { - case NOT_FOUND: - response.resume(status(NOT_FOUND).type(APPLICATION_JSON).build()); - break; - case NOT_AUTHORIZED: - response.resume(status(UNAUTHORIZED).type(APPLICATION_JSON).build()); - break; - case GENERAL_ERROR: - response.resume(status(INTERNAL_SERVER_ERROR).type(APPLICATION_JSON).build()); - break; - default: - throw new IllegalStateException("Unexpected value: " + result.reason()); - } - } else if (throwable != null) { - reportError(response, throwable); - } - } - - /** - * Reports an error to the client. On the provider side, the error is reported as a {@code INTERNAL_SERVER_ERROR} since the provider data plane is considered an origin server - * even though it may delegate requests to other internal sources. - */ - private void reportError(AsyncResponse response, Throwable throwable) { - monitor.severe("Error processing gateway request", throwable); - var entity = status(INTERNAL_SERVER_ERROR).entity(format("'%s'", throwable.getMessage())).type(APPLICATION_JSON).build(); - response.resume(entity); - } - - - private Result extractSourceDataAddress(String token, GatewayConfiguration configuration) { - return dataAddressResolver.resolve(token).map(dataAddress -> mapToHttpDataAddress(dataAddress, configuration, token)); - } - - private HttpDataAddress mapToHttpDataAddress(DataAddress dataAddress, GatewayConfiguration configuration, String token) { - var builder = HttpDataAddress.Builder.newInstance().copyFrom(dataAddress); - if (configuration.isForwardEdrToken()) { - builder.addAdditionalHeader(configuration.getForwardEdrTokenHeaderKey(), token); - } - return builder.build(); - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/ResponseHelper.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/ResponseHelper.java deleted file mode 100644 index eb449d52e..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/ResponseHelper.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.api.response; - -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import org.jetbrains.annotations.Nullable; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; -import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; -import static jakarta.ws.rs.core.Response.status; -import static java.lang.String.format; - -/** - * Utility functions for creating responses. - */ -public class ResponseHelper { - - /** - * Creates a response with a message encoded for the given media type. Currently, {@code APPLICATION_JSON} and {@code TEXT_PLAIN} are supported. - */ - public static Response createMessageResponse(Response.Status status, String message, @Nullable MediaType mediaType) { - if (mediaType != null && APPLICATION_JSON.equals(mediaType.toString())) { - return status(status).entity(format("'%s'", message)).type(APPLICATION_JSON).build(); - } else { - return status(status).entity(format("%s", message)).type(TEXT_PLAIN).build(); - } - } - - private ResponseHelper() { - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/validation/ProxyProviderDataAddressResolver.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/validation/ProxyProviderDataAddressResolver.java deleted file mode 100644 index c9cc6901f..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/validation/ProxyProviderDataAddressResolver.java +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 Amadeus - * - * 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.edc.dataplane.proxy.provider.api.validation; - -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.ws.rs.core.HttpHeaders; -import okhttp3.Request; -import org.eclipse.edc.connector.dataplane.spi.resolver.DataAddressResolver; -import org.eclipse.edc.spi.http.EdcHttpClient; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.types.domain.DataAddress; - -import java.io.IOException; - -import static java.lang.String.format; - -public class ProxyProviderDataAddressResolver implements DataAddressResolver { - - private final EdcHttpClient httpClient; - private final String endpoint; - private final ObjectMapper mapper; - - public ProxyProviderDataAddressResolver(EdcHttpClient httpClient, String endpoint, ObjectMapper mapper) { - this.httpClient = httpClient; - this.endpoint = endpoint; - this.mapper = mapper; - } - - /** - * Resolves access token received in input of Data Plane public API (consumer pull) into the {@link DataAddress} - * of the requested data. - * - * @param token Access token received in input of the Data Plane public API - * @return Data address - */ - @Override - public Result resolve(String token) { - var request = new Request.Builder().url(endpoint).header(HttpHeaders.AUTHORIZATION, token).get().build(); - try (var response = httpClient.execute(request)) { - var body = response.body(); - var stringBody = body != null ? body.string() : null; - if (stringBody == null) { - return Result.failure("Token validation server returned null body"); - } - - if (response.isSuccessful()) { - return Result.success(mapper.readValue(stringBody, DataAddress.class)); - } else { - return Result.failure(format("Call to token validation sever failed: %s - %s. %s", response.code(), response.message(), stringBody)); - } - } catch (IOException e) { - return Result.failure("Unhandled exception occurred during call to token validation server: " + e.getMessage()); - } - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/DataPlaneProxyProviderApiExtensionTest.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/DataPlaneProxyProviderApiExtensionTest.java deleted file mode 100644 index d68e4e076..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/DataPlaneProxyProviderApiExtensionTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Mercedes Benz Tech Innovation GmbH - * - * 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.edc.dataplane.proxy.provider.api.response; - -import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.configuration.ConfigFactory; -import org.eclipse.edc.spi.system.injection.ObjectFactory; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.edc.web.spi.WebService; -import org.eclipse.tractusx.edc.dataplane.proxy.provider.api.DataPlaneProxyProviderApiExtension; -import org.eclipse.tractusx.edc.dataplane.proxy.provider.api.gateway.ProviderGatewayController; -import org.eclipse.tractusx.edc.dataplane.proxy.provider.api.validation.ProxyProviderDataAddressResolver; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; - -import java.util.HashMap; -import java.util.Map; - -@ExtendWith(DependencyInjectionExtension.class) -class DataPlaneProxyProviderApiExtensionTest { - - private DataPlaneProxyProviderApiExtension extension; - - private static final String CONFIG_THREAD_POOL_SIZE_KEY = "tx.dpf.provider.proxy.thread.pool"; - private static final String CONFIG_THREAD_POOL_SIZE_VALUE = "10"; - - private static final String CONFIG_WEB_HTTP_GATEWAY_PATH_KEY = "web.http.gateway.path"; - private static final String CONFIG_WEB_HTTP_GATEWAY_PATH_VALUE = "/api/v1/gateway"; - - private static final String CONFIG_WEB_HTTP_GATEWAY_PORT_KEY = "web.http.gateway.port"; - private static final String CONFIG_WEB_HTTP_GATEWAY_PORT_VALUE = "11111"; - - private static final String CONFIG_CONTROL_PLANE_VALIDATION_ENDPOINT_KEY = "edc.dataplane.token.validation.endpoint"; - private static final String CONFIG_CONTROL_PLANE_VALIDATION_ENDPOINT_VALUE = "http://example.com"; - - // mocks - private ServiceExtensionContext serviceExtensionContext; - private ProviderGatewayController providerGatewayController; - private Monitor monitor; - private WebService webService; - private ProxyProviderDataAddressResolver proxyProviderDataAddressResolver; - private TypeManager typeManager; - - - @BeforeEach - void setup(ObjectFactory factory, ServiceExtensionContext context) { - serviceExtensionContext = Mockito.mock(ServiceExtensionContext.class); - providerGatewayController = Mockito.mock(ProviderGatewayController.class); - monitor = Mockito.mock(Monitor.class); - webService = Mockito.mock(WebService.class); - proxyProviderDataAddressResolver = Mockito.mock(ProxyProviderDataAddressResolver.class); - typeManager = Mockito.mock(TypeManager.class); - - Mockito.when(serviceExtensionContext.getService(ProviderGatewayController.class)) - .thenReturn(providerGatewayController); - Mockito.when(serviceExtensionContext.getMonitor()).thenReturn(monitor); - context.registerService(TypeManager.class, typeManager); - context.registerService(WebService.class, webService); - - extension = factory.constructInstance(DataPlaneProxyProviderApiExtension.class); - } - - private Map getConfig() { - return new HashMap<>() { - { - put(CONFIG_THREAD_POOL_SIZE_KEY, CONFIG_THREAD_POOL_SIZE_VALUE); - put(CONFIG_CONTROL_PLANE_VALIDATION_ENDPOINT_KEY, CONFIG_CONTROL_PLANE_VALIDATION_ENDPOINT_VALUE); - } - }; - } - - private Map getConfigWithContext() { - var config = getConfig(); - config.put(CONFIG_WEB_HTTP_GATEWAY_PATH_KEY, CONFIG_WEB_HTTP_GATEWAY_PATH_VALUE); - config.put(CONFIG_WEB_HTTP_GATEWAY_PORT_KEY, CONFIG_WEB_HTTP_GATEWAY_PORT_VALUE); - return config; - } - - @Test - void testInitialize() { - var config = ConfigFactory.fromMap(getConfig()); - Mockito.when(serviceExtensionContext.getConfig()).thenReturn(config); - - extension.initialize(serviceExtensionContext); - - Mockito.verify(webService, Mockito.times(1)).registerResource(Mockito.any(ProviderGatewayController.class)); - } - - @Test - void testInitializeWithContext() { - var config = ConfigFactory.fromMap(getConfigWithContext()); - Mockito.when(serviceExtensionContext.getConfig()).thenReturn(config); - - extension.initialize(serviceExtensionContext); - - Mockito.verify(webService, Mockito.times(1)).registerResource(Mockito.any(String.class), Mockito.any(ProviderGatewayController.class)); - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/ProxyProviderDataAddressResolverTest.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/ProxyProviderDataAddressResolverTest.java deleted file mode 100644 index 5ffd665a7..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/ProxyProviderDataAddressResolverTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.api.response; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.tractusx.edc.dataplane.proxy.provider.api.validation.ProxyProviderDataAddressResolver; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; -import static org.eclipse.edc.junit.testfixtures.TestUtils.testHttpClient; - -public class ProxyProviderDataAddressResolverTest { - - private static final ObjectMapper MAPPER = new TypeManager().getMapper(); - private static final int PORT = getFreePort(); - private static final String TOKEN_VALIDATION_SERVER_URL = "http://localhost:" + PORT; - private MockWebServer mockServer; - - private ProxyProviderDataAddressResolver resolver; - - @BeforeEach - public void startServer() throws IOException { - mockServer = new MockWebServer(); - mockServer.start(PORT); - resolver = new ProxyProviderDataAddressResolver(testHttpClient(), TOKEN_VALIDATION_SERVER_URL, MAPPER); - } - - @AfterEach - public void stopServer() throws IOException { - mockServer.shutdown(); - } - - @Test - void verifySuccessTokenValidation() throws JsonProcessingException { - var token = UUID.randomUUID().toString(); - var address = DataAddress.Builder.newInstance() - .type("test-type") - .build(); - - mockServer.enqueue(new MockResponse().setBody(MAPPER.writeValueAsString(address)).setResponseCode(200)); - - - var result = resolver.resolve(token); - - assertThat(result.succeeded()).isTrue(); - assertThat(result.getContent().getType()).isEqualTo(address.getType()); - } - - @Test - void verifyFailedResultReturnedIfServerResponseIsUnsuccessful() throws JsonProcessingException { - var token = UUID.randomUUID().toString(); - - mockServer.enqueue(new MockResponse().setResponseCode(400)); - - var result = resolver.resolve(token); - - assertThat(result.failed()).isTrue(); - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/ResponseHelperTest.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/ResponseHelperTest.java deleted file mode 100644 index 8f9a8cf21..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/response/ResponseHelperTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.api.response; - -import org.junit.jupiter.api.Test; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; -import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN_TYPE; -import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.dataplane.proxy.provider.api.response.ResponseHelper.createMessageResponse; - -class ResponseHelperTest { - - @Test - void verify_responses() { - assertThat(createMessageResponse(INTERNAL_SERVER_ERROR, "Some error", APPLICATION_JSON_TYPE).getEntity()).isEqualTo("'Some error'"); - assertThat(createMessageResponse(INTERNAL_SERVER_ERROR, "Some error", TEXT_PLAIN_TYPE).getEntity()).isEqualTo("Some error"); - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/README.md b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/README.md deleted file mode 100644 index 706a31e78..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# DataPlane Proxy Provider Core - -This extension provide the base service and configuration for the DataPlane Provider Proxy. - -## Configuration - -| Key | Required | Default | Description | -|----------------------------------------------------|----------------------------------------------------------------------------------| -| tx.dpf.proxy.gateway.alias.proxied.path |X | 10 | The backend URL to proxy | -| tx.dpf.proxy.gateway.alias.proxied.edr.forward | | false | If the original EDR must be forwarded to the backend | -| tx.dpf.proxy.gateway.alias.proxied.edr.headerKey | | Edc-Edr | The header name to use when forwarding the EDR | - -Where `alias` is the first part of the subpath after `gateway` mentioned [here](../edc-dataplane-proxy-provider-api/README.md) diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/build.gradle.kts b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/build.gradle.kts deleted file mode 100644 index c9b7004e2..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/build.gradle.kts +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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 { - `java-library` -} - -dependencies { - - implementation(libs.edc.util) - implementation(libs.edc.dpf.util) - implementation(libs.edc.token.core) - implementation(libs.edc.ext.http) - implementation(libs.edc.spi.http) - - implementation(libs.edc.spi.jwt) - - implementation(libs.jakarta.rsApi) - implementation(libs.nimbus.jwt) - - implementation(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-spi")) -} - diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/ProxyProviderCoreExtension.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/ProxyProviderCoreExtension.java deleted file mode 100644 index 1395550f8..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/ProxyProviderCoreExtension.java +++ /dev/null @@ -1,91 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.core; - -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provides; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.dataplane.proxy.provider.core.gateway.auth.AuthorizationHandlerRegistryImpl; -import org.eclipse.tractusx.edc.dataplane.proxy.provider.core.gateway.configuration.GatewayConfigurationRegistryImpl; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.authorization.AuthorizationExtension; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.authorization.AuthorizationHandlerRegistry; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration.GatewayConfigurationRegistry; -import org.jetbrains.annotations.NotNull; - -import static java.lang.String.format; -import static org.eclipse.edc.spi.result.Result.success; -import static org.eclipse.tractusx.edc.dataplane.proxy.provider.core.gateway.configuration.GatewayConfigurationLoader.loadConfiguration; -import static org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration.GatewayConfiguration.NO_AUTHORIZATION; - -/** - * Registers default services for the data plane provider proxy implementation. - */ -@Extension(value = ProxyProviderCoreExtension.NAME) -@Provides({ GatewayConfigurationRegistry.class, AuthorizationHandlerRegistry.class }) -public class ProxyProviderCoreExtension implements ServiceExtension { - static final String NAME = "Data Plane Provider Proxy Core"; - - @Inject(required = false) - private AuthorizationExtension authorizationExtension; - - @Inject - private Vault vault; - - @Inject - private Monitor monitor; - - @Override - public String name() { - return NAME; - } - - @Override - public void initialize(ServiceExtensionContext context) { - var configurationRegistry = new GatewayConfigurationRegistryImpl(); - context.registerService(GatewayConfigurationRegistry.class, configurationRegistry); - - if (authorizationExtension == null) { - context.getMonitor().info("Proxy JWT authorization is configured to only validate tokens and not provide path access control"); - authorizationExtension = (c, p) -> success(); - } - - var authorizationRegistry = createAuthorizationRegistry(); - context.registerService(AuthorizationHandlerRegistry.class, authorizationRegistry); - - loadConfiguration(context).forEach(configuration -> { - monitor.info(format("Registering gateway configuration alias `%s` to %s", configuration.getAlias(), configuration.getProxiedPath())); - configurationRegistry.register(configuration); - }); - } - - @NotNull - private AuthorizationHandlerRegistryImpl createAuthorizationRegistry() { - var authorizationRegistry = new AuthorizationHandlerRegistryImpl(); - - authorizationRegistry.register(NO_AUTHORIZATION, (t, p) -> success()); - - return authorizationRegistry; - } - -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/AuthorizationHandlerRegistryImpl.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/AuthorizationHandlerRegistryImpl.java deleted file mode 100644 index 79c344695..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/AuthorizationHandlerRegistryImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.core.gateway.auth; - -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.authorization.AuthorizationHandler; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.authorization.AuthorizationHandlerRegistry; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; - -/** - * Default implementation of the registry. - */ -public class AuthorizationHandlerRegistryImpl implements AuthorizationHandlerRegistry { - private final Map handlers = new HashMap<>(); - - @Override - public @Nullable AuthorizationHandler getHandler(String alias) { - return handlers.get(alias); - } - - @Override - public void register(String alias, AuthorizationHandler handler) { - handlers.put(alias, handler); - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationLoader.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationLoader.java deleted file mode 100644 index 3be3bc6f6..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationLoader.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.core.gateway.configuration; - -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.configuration.Config; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration.GatewayConfiguration; - -import java.util.List; - -import static java.util.stream.Collectors.toList; -import static org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration.GatewayConfiguration.NO_AUTHORIZATION; - -/** - * Loads gateway configuration from the {@link #TX_GATEWAY_PREFIX} prefix. - */ -public class GatewayConfigurationLoader { - public static final String DEFAULT_FORWARD_EDR_HEADER_KEY = "Edc-Edr"; - static final String TX_GATEWAY_PREFIX = "tx.dpf.proxy.gateway"; - static final String AUTHORIZATION_TYPE = "authorization.type"; - static final String PROXIED_PATH = "proxied.path"; - static final String FORWARD_EDR = "proxied.edr.forward"; - static final String FORWARD_EDR_HEADER_KEY = "proxied.edr.headerKey"; - - public static List loadConfiguration(ServiceExtensionContext context) { - var root = context.getConfig(TX_GATEWAY_PREFIX); - return root.partition().map(GatewayConfigurationLoader::createGatewayConfiguration).collect(toList()); - } - - private static GatewayConfiguration createGatewayConfiguration(Config config) { - return GatewayConfiguration.Builder.newInstance() - .alias(config.currentNode()) - .authorizationType(config.getString(AUTHORIZATION_TYPE, NO_AUTHORIZATION)) - .forwardEdrToken(config.getBoolean(FORWARD_EDR, false)) - .forwardEdrTokenHeaderKey(config.getString(FORWARD_EDR_HEADER_KEY, DEFAULT_FORWARD_EDR_HEADER_KEY)) - - .proxiedPath(config.getString(PROXIED_PATH)) - .build(); - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationRegistryImpl.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationRegistryImpl.java deleted file mode 100644 index c7584107a..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationRegistryImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.core.gateway.configuration; - -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration.GatewayConfiguration; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration.GatewayConfigurationRegistry; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; - -/** - * Default implementation. - */ -public class GatewayConfigurationRegistryImpl implements GatewayConfigurationRegistry { - private final Map configurations = new HashMap<>(); - - @Override - public @Nullable GatewayConfiguration getConfiguration(String alias) { - return configurations.get(alias); - } - - @Override - public void register(GatewayConfiguration configuration) { - configurations.put(configuration.getAlias(), configuration); - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationLoaderTest.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationLoaderTest.java deleted file mode 100644 index b087cb078..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationLoaderTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.core.gateway.configuration; - -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.configuration.ConfigFactory; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.dataplane.proxy.provider.core.gateway.configuration.GatewayConfigurationLoader.AUTHORIZATION_TYPE; -import static org.eclipse.tractusx.edc.dataplane.proxy.provider.core.gateway.configuration.GatewayConfigurationLoader.PROXIED_PATH; -import static org.eclipse.tractusx.edc.dataplane.proxy.provider.core.gateway.configuration.GatewayConfigurationLoader.TX_GATEWAY_PREFIX; -import static org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration.GatewayConfiguration.NO_AUTHORIZATION; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -class GatewayConfigurationLoaderTest { - - @Test - void verify_loadConfiguration() { - var context = mock(ServiceExtensionContext.class); - - var config = ConfigFactory.fromMap( - Map.of(format("alias.%s", AUTHORIZATION_TYPE), NO_AUTHORIZATION, - format("alias.%s", PROXIED_PATH), "https://test.com")); - when(context.getConfig(TX_GATEWAY_PREFIX)).thenReturn(config); - - var configurations = GatewayConfigurationLoader.loadConfiguration(context); - - assertThat(configurations).isNotEmpty(); - var configuration = configurations.get(0); - - assertThat(configuration.getAlias()).isEqualTo("alias"); - assertThat(configuration.getAuthorizationType()).isEqualTo(NO_AUTHORIZATION); - assertThat(configuration.getProxiedPath()).isEqualTo("https://test.com"); - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationRegistryImplTest.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationRegistryImplTest.java deleted file mode 100644 index 3bd00bbc6..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/configuration/GatewayConfigurationRegistryImplTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.provider.core.gateway.configuration; - -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration.GatewayConfiguration; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class GatewayConfigurationRegistryImplTest { - - @Test - void verify_Configuration() { - var registry = new GatewayConfigurationRegistryImpl(); - registry.register(GatewayConfiguration.Builder.newInstance().proxiedPath("https://test.com").alias("alias").build()); - - assertThat(registry.getConfiguration("alias")).isNotNull(); - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/authorization/AuthorizationExtension.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/authorization/AuthorizationExtension.java deleted file mode 100644 index 1f520bea2..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/authorization/AuthorizationExtension.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.spi.provider.gateway.authorization; - -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.result.Result; - -/** - * Performs an authorization check for the given path against a set of claims. - */ -public interface AuthorizationExtension { - - /** - * Performs an authorization check for the given path against the presented claims. The path is the request alias path, not - * the proxied path. - * - * @param token the validated claim token - * @param path the request alias path, not the dereferenced proxied path - */ - Result authorize(ClaimToken token, String path); - -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/authorization/AuthorizationHandler.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/authorization/AuthorizationHandler.java deleted file mode 100644 index b5a1752b3..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/authorization/AuthorizationHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.spi.provider.gateway.authorization; - -import org.eclipse.edc.spi.result.Result; - -/** - * Performs an authorization using the request token for a given path. Implementation support different token formats such as JWT. - */ -@FunctionalInterface -public interface AuthorizationHandler { - - /** - * Performs the authorization check. - * - * @param token the unvalidated token - * @param path the request alias path, not the dereferenced proxied path - */ - Result authorize(String token, String path); - -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/configuration/GatewayConfiguration.java b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/configuration/GatewayConfiguration.java deleted file mode 100644 index 6d797a8b0..000000000 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/configuration/GatewayConfiguration.java +++ /dev/null @@ -1,107 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.spi.provider.gateway.configuration; - -import static java.util.Objects.requireNonNull; - -/** - * A configuration that exposes a proxied endpoint via an alias. Each configuration is associated with an extensible {@code authorizationType} such as - * {@link #NO_AUTHORIZATION} (the default) and {@link #NO_AUTHORIZATION}. The {@code proxiedPath} will be prepended to a request sub-path to create an absolute endpoint - * URL where data is fetched from. - */ -public class GatewayConfiguration { - public static final String NO_AUTHORIZATION = "none"; - - private String alias; - private String proxiedPath; - private String authorizationType = NO_AUTHORIZATION; - - private boolean forwardEdrToken; - private String forwardEdrTokenHeaderKey; - - - private GatewayConfiguration() { - } - - public String getAlias() { - return alias; - } - - public String getProxiedPath() { - return proxiedPath; - } - - public String getAuthorizationType() { - return authorizationType; - } - - public boolean isForwardEdrToken() { - return forwardEdrToken; - } - - public String getForwardEdrTokenHeaderKey() { - return forwardEdrTokenHeaderKey; - } - - public static class Builder { - - private final GatewayConfiguration configuration; - - private Builder() { - configuration = new GatewayConfiguration(); - } - - public static Builder newInstance() { - return new Builder(); - } - - public Builder alias(String alias) { - this.configuration.alias = alias; - return this; - } - - public Builder proxiedPath(String proxiedPath) { - this.configuration.proxiedPath = proxiedPath; - return this; - } - - public Builder authorizationType(String authorizationType) { - this.configuration.authorizationType = authorizationType; - return this; - } - - public Builder forwardEdrToken(boolean forwardEdrToken) { - this.configuration.forwardEdrToken = forwardEdrToken; - return this; - } - - public Builder forwardEdrTokenHeaderKey(String forwardEdrTokenHeaderKey) { - this.configuration.forwardEdrTokenHeaderKey = forwardEdrTokenHeaderKey; - return this; - } - - public GatewayConfiguration build() { - requireNonNull(configuration.alias, "alias"); - requireNonNull(configuration.proxiedPath, "proxiedPath"); - requireNonNull(configuration.authorizationType, "authorizationType"); - return configuration; - } - } -} diff --git a/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java b/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java deleted file mode 100644 index eaa6d6434..000000000 --- a/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * Copyright (c) 2021,2022 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.edc.dataplane.selector.configuration; - -import org.eclipse.edc.connector.dataplane.selector.spi.DataPlaneSelectorService; -import org.eclipse.edc.junit.annotations.ComponentTest; -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.eclipse.edc.runtime.metamodel.annotation.Provides; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; - -import java.util.HashMap; -import java.util.Map; - -@ComponentTest -@ExtendWith(EdcExtension.class) -class DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest { - - private static final String S3_BUCKET = "s3-bucket"; - private static final String BLOB_STORAGE = "blob-storage"; - private static final String LOCAL_FILE_SYSTEM = "local-file-system"; - - private static final String DATA_PLANE_INSTANCE_ID = "test-plane"; - private static final String DATA_PLANE_INSTANCE_URL = "http://127.0.0.1:8080/test"; - private static final String DATA_PLANE_INSTANCE_SOURCE_TYPES = - String.format("%s, %s", S3_BUCKET, BLOB_STORAGE); - private static final String DATA_PLANE_INSTANCE_DESTINATION_TYPES = LOCAL_FILE_SYSTEM; - - // mocks - private DataPlaneSelectorService dataPlaneSelectorService; - - @BeforeEach - final void beforeEach(EdcExtension extension) { - dataPlaneSelectorService = Mockito.mock(DataPlaneSelectorService.class); - - extension.registerSystemExtension(ServiceExtension.class, new TestExtension()); - extension.setConfiguration(getConfig()); - } - - private Map getConfig() { - final String urlKey = - String.format( - "%s.%s.%s", - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.URL_SUFFIX); - final String sourceTypesKey = - String.format( - "%s.%s.%s", - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.SOURCE_TYPES_SUFFIX); - final String destinationTypesKey = - String.format( - "%s.%s.%s", - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.DESTINATION_TYPES_SUFFIX); - final String propertiesKey = - String.format( - "%s.%s.%s", - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.PROPERTIES_SUFFIX); - - return new HashMap<>() { - { - put(urlKey, DATA_PLANE_INSTANCE_URL); - put(sourceTypesKey, DATA_PLANE_INSTANCE_SOURCE_TYPES); - put(destinationTypesKey, DATA_PLANE_INSTANCE_DESTINATION_TYPES); - put( - propertiesKey, - String.format( - "{ \"%s\": \"%s\" }", - DataPlaneSelectorConfigurationServiceExtension.PUBLIC_API_URL_PROPERTY, - DATA_PLANE_INSTANCE_URL)); - } - }; - } - - @Test - void registersDataPlaneInstance() { - Mockito.verify(dataPlaneSelectorService, Mockito.times(1)) - .addInstance( - Mockito.argThat( - dataPlaneInstance -> { - final DataAddress s3Source = - DataAddress.Builder.newInstance().type(S3_BUCKET).build(); - final DataAddress blobSource = - DataAddress.Builder.newInstance().type(BLOB_STORAGE).build(); - final DataAddress fsSink = - DataAddress.Builder.newInstance().type(LOCAL_FILE_SYSTEM).build(); - - final boolean matchingId = - dataPlaneInstance.getId().equals(DATA_PLANE_INSTANCE_ID); - final boolean matchingUrl = - dataPlaneInstance.getUrl().toString().equals(DATA_PLANE_INSTANCE_URL); - final boolean matchingCanHandleS3ToFileSystem = - dataPlaneInstance.canHandle(s3Source, fsSink); - final boolean matchingCanHandleBlobToFileSystem = - dataPlaneInstance.canHandle(blobSource, fsSink); - - if (!matchingId) { - System.err.printf( - "Expected ID %s, but got %s%n", - DATA_PLANE_INSTANCE_ID, dataPlaneInstance.getId()); - } - if (!matchingUrl) { - System.err.printf( - "Expected URL %s, but got %s%n", - DATA_PLANE_INSTANCE_URL, dataPlaneInstance.getUrl()); - } - if (!matchingCanHandleS3ToFileSystem) { - System.err.printf( - "Expected Instance to be handle source %s and sink %s%n", - S3_BUCKET, LOCAL_FILE_SYSTEM); - } - if (!matchingCanHandleBlobToFileSystem) { - System.err.printf( - "Expected Instance to be handle source %s and sink %s%n", - BLOB_STORAGE, LOCAL_FILE_SYSTEM); - } - - return matchingId && - matchingUrl && - matchingCanHandleS3ToFileSystem && - matchingCanHandleBlobToFileSystem; - })); - } - - @Provides({DataPlaneSelectorService.class}) - private class TestExtension implements ServiceExtension { - - public void initialize(ServiceExtensionContext context) { - context.registerService(DataPlaneSelectorService.class, dataPlaneSelectorService); - } - } -} diff --git a/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java b/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java deleted file mode 100644 index 55b685242..000000000 --- a/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java +++ /dev/null @@ -1,229 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * Copyright (c) 2021,2022 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.edc.dataplane.selector.configuration; - -import org.eclipse.edc.connector.dataplane.selector.spi.DataPlaneSelectorService; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.configuration.Config; -import org.eclipse.edc.spi.system.configuration.ConfigFactory; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.ArgumentsProvider; -import org.junit.jupiter.params.provider.ArgumentsSource; -import org.mockito.Mockito; - -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Stream; - -class DataPlaneSelectorConfigurationServiceExtensionTest { - private static final String S3_BUCKET = "s3-bucket"; - private static final String BLOB_STORAGE = "blob-storage"; - private static final String LOCAL_FILE_SYSTEM = "local-file-system"; - - private static final String DATA_PLANE_INSTANCE_ID = "test-plane"; - private static final String DATA_PLANE_INSTANCE_URL = "http://127.0.0.1:8080/test"; - private static final String DATA_PLANE_INSTANCE_SOURCE_TYPES = - String.format("%s, %s", S3_BUCKET, BLOB_STORAGE); - private static final String DATA_PLANE_INSTANCE_DESTINATION_TYPES = LOCAL_FILE_SYSTEM; - - private static final String URL_KEY = - String.format( - "%s.%s", - DATA_PLANE_INSTANCE_ID, DataPlaneSelectorConfigurationServiceExtension.URL_SUFFIX); - private static final String SOURCE_TYPES_KEY = - String.format( - "%s.%s", - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.SOURCE_TYPES_SUFFIX); - private static final String DESTINATION_TYPES_KEY = - String.format( - "%s.%s", - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.DESTINATION_TYPES_SUFFIX); - private static final String PROPERTIES_KEY = - String.format( - "%s.%s", - DATA_PLANE_INSTANCE_ID, DataPlaneSelectorConfigurationServiceExtension.PROPERTIES_SUFFIX); - - private DataPlaneSelectorConfigurationServiceExtension extension; - - // mocks - private ServiceExtensionContext serviceExtensionContext; - private DataPlaneSelectorService dataPlaneSelectorService; - private Monitor monitor; - - @BeforeEach - void setup() { - extension = new DataPlaneSelectorConfigurationServiceExtension(); - - serviceExtensionContext = Mockito.mock(ServiceExtensionContext.class); - dataPlaneSelectorService = Mockito.mock(DataPlaneSelectorService.class); - monitor = Mockito.mock(Monitor.class); - - Mockito.when(serviceExtensionContext.getService(DataPlaneSelectorService.class)) - .thenReturn(dataPlaneSelectorService); - Mockito.when(serviceExtensionContext.getMonitor()).thenReturn(monitor); - } - - private Map getConfig() { - return new HashMap<>() { - { - put(URL_KEY, DATA_PLANE_INSTANCE_URL); - put(SOURCE_TYPES_KEY, DATA_PLANE_INSTANCE_SOURCE_TYPES); - put(DESTINATION_TYPES_KEY, DATA_PLANE_INSTANCE_DESTINATION_TYPES); - put( - PROPERTIES_KEY, - String.format( - "{ \"%s\": \"%s\" }", - DataPlaneSelectorConfigurationServiceExtension.PUBLIC_API_URL_PROPERTY, - DATA_PLANE_INSTANCE_URL)); - } - }; - } - - @Test - void testName() { - final DataPlaneSelectorConfigurationServiceExtension extension = - new DataPlaneSelectorConfigurationServiceExtension(); - - Assertions.assertNotNull(extension.name()); - Assertions.assertEquals("Data Plane Selector Configuration Extension", extension.name()); - } - - @Test - void testInitialize() { - - final Config config = ConfigFactory.fromMap(getConfig()); - - Mockito.when(serviceExtensionContext.getConfig("edc.dataplane.selector")).thenReturn(config); - - extension.initialize(serviceExtensionContext); - - Mockito.verify(serviceExtensionContext, Mockito.times(1)) - .getService(DataPlaneSelectorService.class); - Mockito.verify(serviceExtensionContext, Mockito.times(1)).getMonitor(); - Mockito.when( - serviceExtensionContext.getConfig( - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) - .thenReturn(config); - - Mockito.verify(dataPlaneSelectorService, Mockito.times(1)) - .addInstance( - Mockito.argThat( - dataPlaneInstance -> { - final DataAddress s3Source = - DataAddress.Builder.newInstance().type(S3_BUCKET).build(); - final DataAddress blobSource = - DataAddress.Builder.newInstance().type(BLOB_STORAGE).build(); - final DataAddress fsSink = - DataAddress.Builder.newInstance().type(LOCAL_FILE_SYSTEM).build(); - - final boolean matchingId = - dataPlaneInstance.getId().equals(DATA_PLANE_INSTANCE_ID); - final boolean matchingUrl = - dataPlaneInstance.getUrl().toString().equals(DATA_PLANE_INSTANCE_URL); - final boolean matchingCanHandleS3ToFileSystem = - dataPlaneInstance.canHandle(s3Source, fsSink); - final boolean matchingCanHandleBlobToFileSystem = - dataPlaneInstance.canHandle(blobSource, fsSink); - - if (!matchingId) { - System.err.printf( - "Expected ID %s, but got %s%n", - DATA_PLANE_INSTANCE_ID, dataPlaneInstance.getId()); - } - if (!matchingUrl) { - System.err.printf( - "Expected URL %s, but got %s%n", - DATA_PLANE_INSTANCE_URL, dataPlaneInstance.getUrl()); - } - if (!matchingCanHandleS3ToFileSystem) { - System.err.printf( - "Expected Instance to be handle source %s and sink %s%n", - S3_BUCKET, LOCAL_FILE_SYSTEM); - } - if (!matchingCanHandleBlobToFileSystem) { - System.err.printf( - "Expected Instance to be handle source %s and sink %s%n", - BLOB_STORAGE, LOCAL_FILE_SYSTEM); - } - - return matchingId && - matchingUrl && - matchingCanHandleS3ToFileSystem && - matchingCanHandleBlobToFileSystem; - })); - } - - @ParameterizedTest - @ArgumentsSource(MissingConfigArgumentsProvider.class) - void testWarningOnPropertyMissing(String configKey, String configValue) { - Map configMap = getConfig(); - configMap.put(configKey, configValue); - - final Config config = ConfigFactory.fromMap(configMap); - - Mockito.when( - serviceExtensionContext.getConfig( - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) - .thenReturn(config); - - extension.initialize(serviceExtensionContext); - - // one warning config missing, one warning data plane instance skipped - Mockito.verify(monitor, Mockito.times(2)).warning(Mockito.anyString()); - } - - @Test - void throwsExceptionOnPropertiesNoJson() { - Map configMap = getConfig(); - configMap.put(PROPERTIES_KEY, "no json"); - - final Config config = ConfigFactory.fromMap(configMap); - - Mockito.when( - serviceExtensionContext.getConfig( - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) - .thenReturn(config); - - Assertions.assertThrows( - EdcException.class, () -> extension.initialize(serviceExtensionContext)); - } - - private static class MissingConfigArgumentsProvider implements ArgumentsProvider { - @Override - public Stream provideArguments(ExtensionContext context) { - return Stream.of( - Arguments.of(URL_KEY, ""), - Arguments.of(SOURCE_TYPES_KEY, ""), - Arguments.of(DESTINATION_TYPES_KEY, ""), - Arguments.of(PROPERTIES_KEY, "{}")); - } - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/README.md b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/README.md similarity index 100% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/README.md rename to edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/README.md diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/build.gradle.kts b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/build.gradle.kts similarity index 94% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/build.gradle.kts rename to edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/build.gradle.kts index ebc229f52..53845bed9 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/build.gradle.kts +++ b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/build.gradle.kts @@ -28,10 +28,11 @@ dependencies { implementation(libs.edc.spi.http) implementation(libs.edc.spi.dataplane.http) - implementation(libs.edc.util) + implementation(libs.edc.lib.util) implementation(libs.edc.dpf.util) implementation(libs.edc.ext.http) implementation(libs.edc.spi.auth) + implementation(libs.edc.spi.edrstore) implementation(project(":spi:edr-spi")) diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java similarity index 95% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java rename to edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java index abbecdd02..39ee2fe18 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java +++ b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java @@ -34,7 +34,7 @@ import org.eclipse.edc.web.spi.configuration.WebServiceSettings; import org.eclipse.tractusx.edc.dataplane.proxy.consumer.api.asset.ClientErrorExceptionMapper; import org.eclipse.tractusx.edc.dataplane.proxy.consumer.api.asset.ConsumerAssetRequestController; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.edr.spi.service.EdrService; import java.util.concurrent.ExecutorService; @@ -65,7 +65,7 @@ public class DataPlaneProxyConsumerApiExtension implements ServiceExtension { private PipelineService pipelineService; @Inject - private EndpointDataReferenceCache edrCache; + private EdrService edrService; @Inject private WebServiceConfigurer configurer; @@ -92,7 +92,7 @@ public void initialize(ServiceExtensionContext context) { webService.registerResource(CONSUMER_API_ALIAS, new AuthenticationRequestFilter(authenticationService)); webService.registerResource(CONSUMER_API_ALIAS, new ClientErrorExceptionMapper()); - webService.registerResource(CONSUMER_API_ALIAS, new ConsumerAssetRequestController(edrCache, pipelineService, executorService, monitor)); + webService.registerResource(CONSUMER_API_ALIAS, new ConsumerAssetRequestController(edrService, pipelineService, executorService, monitor)); } @Override diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ClientErrorExceptionMapper.java b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ClientErrorExceptionMapper.java similarity index 100% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ClientErrorExceptionMapper.java rename to edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ClientErrorExceptionMapper.java diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestApi.java b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestApi.java similarity index 100% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestApi.java rename to edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestApi.java diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java similarity index 68% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java rename to edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java index 5d104c214..b2aa6c08e 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java +++ b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java @@ -33,16 +33,18 @@ import org.eclipse.edc.connector.dataplane.spi.pipeline.TransferService; import org.eclipse.edc.connector.dataplane.util.sink.AsyncStreamingDataSink; import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.query.Criterion; +import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; +import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; import org.eclipse.tractusx.edc.dataplane.proxy.consumer.api.asset.model.AssetRequest; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.edr.spi.service.EdrService; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutorService; +import java.util.function.Function; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import static jakarta.ws.rs.core.Response.Status.BAD_GATEWAY; @@ -54,7 +56,8 @@ import static java.util.UUID.randomUUID; import static org.eclipse.edc.connector.dataplane.spi.schema.DataFlowRequestSchema.PATH; import static org.eclipse.edc.connector.dataplane.spi.schema.DataFlowRequestSchema.QUERY_PARAMS; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.types.RefreshMode.AUTO_REFRESH; /** * Implements the HTTP proxy API. @@ -62,23 +65,28 @@ @Path("/aas") @Produces(MediaType.APPLICATION_JSON) public class ConsumerAssetRequestController implements ConsumerAssetRequestApi { - public static final String BASE_URL = EDC_NAMESPACE + "baseUrl"; - private static final String HTTP_DATA = "HttpData"; + public static final String BASE_URL = EDC_NAMESPACE + "endpoint"; + public static final String EDR_ERROR_MESSAGE = "No EDR for transfer process: %s : %s"; private static final String ASYNC_TYPE = "async"; private static final String HEADER_AUTHORIZATION = "header:authorization"; private static final String BEARER_PREFIX = "Bearer "; - - private final EndpointDataReferenceCache edrCache; + private final EdrService edrService; private final TransferService transferService; private final Monitor monitor; private final ExecutorService executorService; - public ConsumerAssetRequestController(EndpointDataReferenceCache edrCache, + + private final Map> mappers = Map.of( + "provider", AssetRequest::getProviderId, + "transferProcessId", AssetRequest::getTransferProcessId, + "assetId", AssetRequest::getAssetId); + + public ConsumerAssetRequestController(EdrService edrService, TransferService transferService, ExecutorService executorService, Monitor monitor) { - this.edrCache = edrCache; + this.edrService = edrService; this.transferService = transferService; this.executorService = executorService; this.monitor = monitor; @@ -91,23 +99,16 @@ public void requestAsset(AssetRequest request, @Suspended AsyncResponse response // resolve the EDR and add it to the request var edr = resolveEdr(request); - var sourceAddress = Optional.ofNullable(request.getEndpointUrl()) - .map(url -> gatewayAddress(url, edr)) - .orElseGet(() -> dataPlaneAddress(edr)); - + var sourceAddress = dataPlaneAddress(edr); + var properties = dataPlaneProperties(request); var destinationAddress = DataAddress.Builder.newInstance() .type(ASYNC_TYPE) .build(); - var properties = Optional.ofNullable(request.getEndpointUrl()) - .map((url) -> Map.of()) - .orElseGet(() -> dataPlaneProperties(request)); - - var flowRequest = DataFlowRequest.Builder.newInstance() + var flowRequest = DataFlowStartMessage.Builder.newInstance() .processId(randomUUID().toString()) - .trackable(false) .sourceDataAddress(sourceAddress) .destinationDataAddress(destinationAddress) .traceContext(Map.of()) @@ -138,38 +139,65 @@ private Map dataPlaneProperties(AssetRequest request) { return props; } - private DataAddress gatewayAddress(String url, EndpointDataReference edr) { - return HttpDataAddress.Builder.newInstance() - .baseUrl(url) - .property(HEADER_AUTHORIZATION, BEARER_PREFIX + edr.getAuthCode()) - .build(); - } - private DataAddress dataPlaneAddress(EndpointDataReference edr) { + private DataAddress dataPlaneAddress(DataAddress edr) { + // TODO the header scheme should be dynamic based on the `authType` + // https://github.com/eclipse-edc/Connector/blob/main/docs/developer/data-plane-signaling/data-plane-signaling-token-handling.md + var endpoint = edr.getStringProperty("endpoint"); + var token = edr.getStringProperty("authorization"); return HttpDataAddress.Builder.newInstance() - .baseUrl(edr.getEndpoint()) + .baseUrl(endpoint) .proxyQueryParams("true") .proxyPath("true") - .property(HEADER_AUTHORIZATION, edr.getAuthCode()) + .property(HEADER_AUTHORIZATION, token) .build(); } - private EndpointDataReference resolveEdr(AssetRequest request) { + private DataAddress resolveEdr(AssetRequest request) { if (request.getTransferProcessId() != null) { - var edr = edrCache.resolveReference(request.getTransferProcessId()); - if (edr == null) { - throw new BadRequestException("No EDR for transfer process: " + request.getTransferProcessId()); + var edr = edrService.resolveByTransferProcess(request.getTransferProcessId(), AUTO_REFRESH); + if (edr.failed()) { + throw new BadRequestException(EDR_ERROR_MESSAGE.formatted(request.getTransferProcessId(), edr.getFailureDetail())); } - return edr; + return edr.getContent(); } else { - var resolvedEdrs = edrCache.referencesForAsset(request.getAssetId(), request.getProviderId()); - if (resolvedEdrs.isEmpty()) { + var resolvedEdrs = edrService.query(toQuery(request)); + + if (resolvedEdrs.failed()) { + throw new BadRequestException(EDR_ERROR_MESSAGE.formatted(request.getTransferProcessId(), resolvedEdrs.getFailureDetail())); + } + + var edrs = resolvedEdrs.getContent(); + if (edrs.isEmpty()) { throw new BadRequestException("No EDR for asset: " + request.getAssetId()); - } else if (resolvedEdrs.size() > 1) { + } else if (edrs.size() > 1) { throw new PreconditionFailedException("More than one EDR for asset: " + request.getAssetId()); } - return resolvedEdrs.get(0); + + var edrEntry = edrs.get(0); + + var edr = edrService.resolveByTransferProcess(edrEntry.getTransferProcessId(), AUTO_REFRESH); + if (edr.failed()) { + throw new BadRequestException(EDR_ERROR_MESSAGE.formatted(edrEntry.getTransferProcessId(), edr.getFailureDetail())); + } + return edr.getContent(); + + } + } + + private QuerySpec toQuery(AssetRequest request) { + var specBuilder = QuerySpec.Builder.newInstance(); + mappers.entrySet() + .stream() + .filter(entry -> entry.getValue().apply(request) != null) + .forEach(entry -> specBuilder.filter(Criterion.criterion(entry.getKey(), "=", entry.getValue().apply(request)))); + + var spec = specBuilder.build(); + + if (spec.getFilterExpression().isEmpty()) { + throw new BadRequestException("No Filter provided in the request"); } + return spec; } /** diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/PreconditionFailedException.java b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/PreconditionFailedException.java similarity index 100% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/PreconditionFailedException.java rename to edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/PreconditionFailedException.java diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequest.java b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequest.java similarity index 93% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequest.java rename to edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequest.java index fb1bbd468..28c146c00 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequest.java +++ b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequest.java @@ -34,7 +34,6 @@ public class AssetRequest { private String assetId; private String providerId; - private String endpointUrl; private String queryParams; @@ -51,10 +50,6 @@ public String getAssetId() { return assetId; } - public String getEndpointUrl() { - return endpointUrl; - } - public String getProviderId() { return providerId; } @@ -89,12 +84,7 @@ public Builder assetId(String assetId) { request.assetId = assetId; return this; } - - public Builder endpointUrl(String endpointUrl) { - request.endpointUrl = endpointUrl; - return this; - } - + public Builder providerId(String providerId) { request.providerId = providerId; return this; diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 100% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequestTest.java b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequestTest.java similarity index 90% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequestTest.java rename to edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequestTest.java index cb89f5aa1..17e79270e 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequestTest.java +++ b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequestTest.java @@ -34,7 +34,6 @@ void verify_SerializeDeserialize() throws JsonProcessingException { var request = AssetRequest.Builder.newInstance() .assetId("asset1") - .endpointUrl("https://test.com") .providerId("providerId") .transferProcessId("tp1") .queryParams("params") @@ -47,7 +46,6 @@ void verify_SerializeDeserialize() throws JsonProcessingException { assertThat(deserialized.getAssetId()).isEqualTo(request.getAssetId()); assertThat(deserialized.getTransferProcessId()).isEqualTo(request.getTransferProcessId()); - assertThat(deserialized.getEndpointUrl()).isEqualTo(request.getEndpointUrl()); assertThat(deserialized.getProviderId()).isEqualTo(request.getProviderId()); assertThat(deserialized.getPathSegments()).isEqualTo(request.getPathSegments()); assertThat(deserialized.getQueryParams()).isEqualTo(request.getQueryParams()); @@ -61,7 +59,7 @@ void verify_NullArguments() { @Test void verify_AssetIdOrTransferProcessId() { - AssetRequest.Builder.newInstance().assetId("asset1").endpointUrl("https://test.com").build(); - AssetRequest.Builder.newInstance().transferProcessId("tp1").endpointUrl("https://test.com").build(); + AssetRequest.Builder.newInstance().assetId("asset1").build(); + AssetRequest.Builder.newInstance().transferProcessId("tp1").build(); } } diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/ConsumerAssetRequestControllerTest.java b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/ConsumerAssetRequestControllerTest.java similarity index 76% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/ConsumerAssetRequestControllerTest.java rename to edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/ConsumerAssetRequestControllerTest.java index 7c45031bd..9a383d5aa 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/ConsumerAssetRequestControllerTest.java +++ b/edc-extensions/dataplane/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/ConsumerAssetRequestControllerTest.java @@ -26,14 +26,16 @@ import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSource; import org.eclipse.edc.connector.dataplane.spi.pipeline.PipelineService; import org.eclipse.edc.connector.dataplane.spi.pipeline.StreamResult; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; import org.eclipse.edc.junit.annotations.ApiTest; import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; +import org.eclipse.edc.spi.result.ServiceResult; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; import org.eclipse.tractusx.edc.dataplane.proxy.consumer.api.asset.ClientErrorExceptionMapper; import org.eclipse.tractusx.edc.dataplane.proxy.consumer.api.asset.ConsumerAssetRequestController; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.edr.spi.service.EdrService; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -57,6 +59,7 @@ import static org.eclipse.edc.connector.dataplane.spi.schema.DataFlowRequestSchema.PATH; import static org.eclipse.edc.connector.dataplane.spi.schema.DataFlowRequestSchema.QUERY_PARAMS; import static org.eclipse.tractusx.edc.dataplane.proxy.consumer.api.asset.ConsumerAssetRequestController.BASE_URL; +import static org.eclipse.tractusx.edc.edr.spi.types.RefreshMode.AUTO_REFRESH; import static org.hamcrest.Matchers.notNullValue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -67,24 +70,23 @@ public class ConsumerAssetRequestControllerTest extends RestControllerTestBase { public static final String ASSET_REQUEST_PATH = "/aas/request"; - private final EndpointDataReferenceCache cache = mock(EndpointDataReferenceCache.class); + private final EdrService edrService = mock(EdrService.class); private final PipelineService pipelineService = mock(); private final ObjectMapper mapper = new ObjectMapper(); + private static Stream provideServiceResultForProxyCall() { + return Stream.of( + Arguments.of(StreamResult.notFound(), NOT_FOUND.getStatusCode()), + Arguments.of(StreamResult.notAuthorized(), UNAUTHORIZED.getStatusCode()), + Arguments.of(StreamResult.error("error"), INTERNAL_SERVER_ERROR.getStatusCode())); + } + @Test void requestAsset_shouldReturnData_withAssetId() throws IOException { var assetId = "assetId"; var transferProcessId = "tp"; - var url = "http://localhost:8080/test"; - var request = Map.of("assetId", assetId, "endpointUrl", url); - var edr = EndpointDataReference.Builder.newInstance() - .id(transferProcessId) - .authKey("authKey") - .authCode("authCode") - .contractId("contract-id") - .endpoint(url) - .build(); + var request = Map.of("assetId", assetId); var response = Map.of("response", "ok"); var responseBytes = mapper.writeValueAsBytes(response); @@ -95,7 +97,8 @@ void requestAsset_shouldReturnData_withAssetId() throws IOException { when(datasource.openPartStream()).thenReturn(StreamResult.success(Stream.of(partStream))); when(partStream.openStream()).thenReturn(new ByteArrayInputStream(responseBytes)); - when(cache.referencesForAsset(assetId, null)).thenReturn(List.of(edr)); + when(edrService.query(any())).thenReturn(ServiceResult.success(List.of(edrEntry(assetId, transferProcessId)))); + when(edrService.resolveByTransferProcess(transferProcessId, AUTO_REFRESH)).thenReturn(ServiceResult.success(edr())); when(pipelineService.transfer(any(), any())) .thenAnswer(a -> CompletableFuture.completedFuture(StreamResult.success(response))); @@ -119,17 +122,10 @@ void requestAsset_shouldReturnError_WhenProxyCallFails(StreamResult resu var assetId = "assetId"; var transferProcessId = "tp"; - var url = "http://localhost:8080/test"; - var request = Map.of("assetId", assetId, "endpointUrl", url); - var edr = EndpointDataReference.Builder.newInstance() - .id(transferProcessId) - .authKey("authKey") - .authCode("authCode") - .endpoint(url) - .contractId("contract-id") - .build(); + var request = Map.of("assetId", assetId); - when(cache.referencesForAsset(assetId, null)).thenReturn(List.of(edr)); + when(edrService.query(any())).thenReturn(ServiceResult.success(List.of(edrEntry(assetId, transferProcessId)))); + when(edrService.resolveByTransferProcess(transferProcessId, AUTO_REFRESH)).thenReturn(ServiceResult.success(edr())); when(pipelineService.transfer(any(), any())) .thenReturn(CompletableFuture.completedFuture(result)); @@ -147,9 +143,9 @@ void requestAsset_shouldReturnError_whenEdrByAssetIdNotFound() { var assetId = "assetId"; var url = "http://localhost:8080/test"; - var request = Map.of("assetId", assetId, "endpointUrl", url); + var request = Map.of("assetId", assetId); - when(cache.referencesForAsset(assetId, null)).thenReturn(List.of()); + when(edrService.query(any())).thenReturn(ServiceResult.success(List.of())); baseRequest() .contentType(MediaType.APPLICATION_JSON) @@ -165,18 +161,9 @@ void requestAsset_shouldReturnError_whenEdrByAssetIdNotFound() { void requestAsset_shouldReturnError_whenMultipleEdrsByAssetIdFound() { var assetId = "assetId"; - var url = "http://localhost:8080/test"; - var request = Map.of("assetId", assetId, "endpointUrl", url); - - var edr = EndpointDataReference.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .authKey("authKey") - .authCode("authCode") - .contractId("contract-id") - .endpoint(url) - .build(); + var request = Map.of("assetId", assetId); - when(cache.referencesForAsset(assetId, null)).thenReturn(List.of(edr, edr)); + when(edrService.query(any())).thenReturn(ServiceResult.success(List.of(edrEntry(assetId), edrEntry(assetId)))); baseRequest() .contentType(MediaType.APPLICATION_JSON) @@ -192,15 +179,7 @@ void requestAsset_shouldReturnError_whenMultipleEdrsByAssetIdFound() { void requestAsset_shouldReturnData_withTransferProcessId() throws IOException { var transferProcessId = "tp"; - var url = "http://localhost:8080/test"; - var request = Map.of("transferProcessId", transferProcessId, "endpointUrl", url); - var edr = EndpointDataReference.Builder.newInstance() - .id(transferProcessId) - .authKey("authKey") - .authCode("authCode") - .contractId("contract-id") - .endpoint(url) - .build(); + var request = Map.of("transferProcessId", transferProcessId); var response = Map.of("response", "ok"); var responseBytes = mapper.writeValueAsBytes(response); @@ -211,7 +190,7 @@ void requestAsset_shouldReturnData_withTransferProcessId() throws IOException { when(datasource.openPartStream()).thenReturn(StreamResult.success(Stream.of(partStream))); when(partStream.openStream()).thenReturn(new ByteArrayInputStream(responseBytes)); - when(cache.resolveReference(transferProcessId)).thenReturn(edr); + when(edrService.resolveByTransferProcess(transferProcessId, AUTO_REFRESH)).thenReturn(ServiceResult.success(edr())); when(pipelineService.transfer(any(), any())) .thenAnswer(a -> CompletableFuture.completedFuture(StreamResult.success(response))); @@ -233,10 +212,9 @@ void requestAsset_shouldReturnData_withTransferProcessId() throws IOException { void requestAsset_shouldReturnError_whenEdrByTransferProcessIdNotFound() { var tp = "tp"; - var url = "http://localhost:8080/test"; - var request = Map.of("transferProcessId", tp, "endpointUrl", url); + var request = Map.of("transferProcessId", tp); - when(cache.resolveReference(tp)).thenReturn(null); + when(edrService.resolveByTransferProcess(tp, AUTO_REFRESH)).thenReturn(ServiceResult.notFound("Not found")); baseRequest() .contentType(MediaType.APPLICATION_JSON) @@ -254,13 +232,7 @@ void requestAsset_shouldReturnData_withDataPlaneUrl() throws IOException { var transferProcessId = "tp"; var url = "http://localhost:8080/test"; var request = Map.of("transferProcessId", transferProcessId, PATH, "/path", QUERY_PARAMS, "test=10&foo=bar"); - var edr = EndpointDataReference.Builder.newInstance() - .id(transferProcessId) - .authKey("authKey") - .authCode("authCode") - .contractId("contract-id") - .endpoint(url) - .build(); + var response = Map.of("response", "ok"); var responseBytes = mapper.writeValueAsBytes(response); @@ -271,7 +243,7 @@ void requestAsset_shouldReturnData_withDataPlaneUrl() throws IOException { when(datasource.openPartStream()).thenReturn(StreamResult.success(Stream.of(partStream))); when(partStream.openStream()).thenReturn(new ByteArrayInputStream(responseBytes)); - when(cache.resolveReference(transferProcessId)).thenReturn(edr); + when(edrService.resolveByTransferProcess(transferProcessId, AUTO_REFRESH)).thenReturn(ServiceResult.success(edr(url))); when(pipelineService.transfer(any(), any())) .thenAnswer(a -> CompletableFuture.completedFuture(StreamResult.success(response))); @@ -288,13 +260,13 @@ void requestAsset_shouldReturnData_withDataPlaneUrl() throws IOException { assertThat(proxyResponse).containsAllEntriesOf(response); - var captor = ArgumentCaptor.forClass(DataFlowRequest.class); + var captor = ArgumentCaptor.forClass(DataFlowStartMessage.class); verify(pipelineService).transfer(captor.capture(), any()); var flowRequest = captor.getValue(); - assertThat(flowRequest.getSourceDataAddress().getStringProperty(BASE_URL)).isEqualTo(edr.getEndpoint()); + assertThat(flowRequest.getSourceDataAddress().getStringProperty("baseUrl")).isEqualTo(url); assertThat(flowRequest.getProperties().get(QUERY_PARAMS)).isEqualTo(request.get(QUERY_PARAMS)); assertThat(flowRequest.getProperties().get(PATH)).isEqualTo(request.get(PATH)); @@ -303,7 +275,7 @@ void requestAsset_shouldReturnData_withDataPlaneUrl() throws IOException { @Override protected Object controller() { - return new ConsumerAssetRequestController(cache, pipelineService, Executors.newSingleThreadExecutor(), mock(Monitor.class)); + return new ConsumerAssetRequestController(edrService, pipelineService, Executors.newSingleThreadExecutor(), mock(Monitor.class)); } @Override @@ -311,11 +283,27 @@ protected Object additionalResource() { return new ClientErrorExceptionMapper(); } - private static Stream provideServiceResultForProxyCall() { - return Stream.of( - Arguments.of(StreamResult.notFound(), NOT_FOUND.getStatusCode()), - Arguments.of(StreamResult.notAuthorized(), UNAUTHORIZED.getStatusCode()), - Arguments.of(StreamResult.error("error"), INTERNAL_SERVER_ERROR.getStatusCode())); + private DataAddress edr() { + return edr(null); + } + + private DataAddress edr(String baseUrl) { + return DataAddress.Builder.newInstance().type("test").property(BASE_URL, baseUrl).build(); + } + + + private EndpointDataReferenceEntry edrEntry(String assetId) { + return edrEntry(assetId, UUID.randomUUID().toString()); + } + + private EndpointDataReferenceEntry edrEntry(String assetId, String transferProcessId) { + return EndpointDataReferenceEntry.Builder.newInstance() + .assetId(assetId) + .transferProcessId(transferProcessId) + .contractNegotiationId(UUID.randomUUID().toString()) + .agreementId(UUID.randomUUID().toString()) + .providerId(UUID.randomUUID().toString()) + .build(); } private RequestSpecification baseRequest() { diff --git a/edc-extensions/dataplane-selector-configuration/README.md b/edc-extensions/dataplane/dataplane-selector-configuration/README.md similarity index 100% rename from edc-extensions/dataplane-selector-configuration/README.md rename to edc-extensions/dataplane/dataplane-selector-configuration/README.md diff --git a/edc-extensions/dataplane-selector-configuration/build.gradle.kts b/edc-extensions/dataplane/dataplane-selector-configuration/build.gradle.kts similarity index 100% rename from edc-extensions/dataplane-selector-configuration/build.gradle.kts rename to edc-extensions/dataplane/dataplane-selector-configuration/build.gradle.kts diff --git a/edc-extensions/dataplane-selector-configuration/src/main/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java b/edc-extensions/dataplane/dataplane-selector-configuration/src/main/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java similarity index 70% rename from edc-extensions/dataplane-selector-configuration/src/main/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java rename to edc-extensions/dataplane/dataplane-selector-configuration/src/main/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java index ab156d163..a1f175d58 100644 --- a/edc-extensions/dataplane-selector-configuration/src/main/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java +++ b/edc-extensions/dataplane/dataplane-selector-configuration/src/main/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java @@ -33,18 +33,21 @@ import org.eclipse.edc.spi.system.configuration.Config; import java.util.Arrays; -import java.util.List; import java.util.Map; import java.util.function.Predicate; import java.util.stream.Collectors; -@Requires({DataPlaneSelectorService.class}) +import static java.lang.String.format; +import static java.lang.String.join; + +@Requires({ DataPlaneSelectorService.class }) public class DataPlaneSelectorConfigurationServiceExtension implements ServiceExtension { public static final String CONFIG_PREFIX = "edc.dataplane.selector"; public static final String URL_SUFFIX = "url"; public static final String DESTINATION_TYPES_SUFFIX = "destinationtypes"; public static final String SOURCE_TYPES_SUFFIX = "sourcetypes"; + public static final String TRANSFER_TYPES_SUFFIX = "transfertypes"; public static final String PROPERTIES_SUFFIX = "properties"; public static final String PUBLIC_API_URL_PROPERTY = "publicApiUrl"; @@ -52,7 +55,7 @@ public class DataPlaneSelectorConfigurationServiceExtension implements ServiceEx private static final String COMMA = ","; private static final String LOG_MISSING_CONFIGURATION = NAME + ": Missing configuration for " + CONFIG_PREFIX + ".%s.%s"; private static final String LOG_SKIP_BC_MISSING_CONFIGURATION = NAME + ": Configuration issues. Skip registering of Data Plane Instance '%s'"; - private static final String LOG_REGISTERED = NAME + ": Registered Data Plane Instance. (id=%s, url=%s, sourceTypes=%s, destinationTypes=%s, properties=)"; + private static final String LOG_REGISTERED = NAME + ": Registered Data Plane Instance. (id=%s, url=%s, sourceTypes=%s, destinationTypes=%s, transferType=%s, properties=)"; private Monitor monitor; private DataPlaneSelectorService dataPlaneSelectorService; @@ -68,77 +71,80 @@ public void initialize(final ServiceExtensionContext serviceExtensionContext) { serviceExtensionContext.getService(DataPlaneSelectorService.class); this.monitor = serviceExtensionContext.getMonitor(); - final Config config = serviceExtensionContext.getConfig(CONFIG_PREFIX); + var config = serviceExtensionContext.getConfig(CONFIG_PREFIX); config.partition().forEach(this::configureDataPlaneInstance); } private void configureDataPlaneInstance(final Config config) { - final String id = config.currentNode(); + var id = config.currentNode(); - final String url = config.getString(URL_SUFFIX, ""); - final List sourceTypes = + var url = config.getString(URL_SUFFIX, ""); + var sourceTypes = Arrays.stream(config.getString(SOURCE_TYPES_SUFFIX, "").split(COMMA)) .map(String::trim) .filter(Predicate.not(String::isEmpty)) .distinct() .collect(Collectors.toList()); - final List destinationTypes = + var destinationTypes = Arrays.stream(config.getString(DESTINATION_TYPES_SUFFIX, "").split(COMMA)) .map(String::trim) .filter(Predicate.not(String::isEmpty)) .distinct() .collect(Collectors.toList()); - final String propertiesJson = config.getString(PROPERTIES_SUFFIX, "{}"); + var propertiesJson = config.getString(PROPERTIES_SUFFIX, "{}"); + + var transferTypes = Arrays.stream(config.getString(TRANSFER_TYPES_SUFFIX, "").split(COMMA)) + .map(String::trim) + .filter(Predicate.not(String::isEmpty)) + .distinct() + .collect(Collectors.toList()); if (url.isEmpty()) { - monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, URL_SUFFIX)); + monitor.warning(format(LOG_MISSING_CONFIGURATION, id, URL_SUFFIX)); } if (sourceTypes.isEmpty()) { - monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, SOURCE_TYPES_SUFFIX)); + monitor.warning(format(LOG_MISSING_CONFIGURATION, id, SOURCE_TYPES_SUFFIX)); } if (destinationTypes.isEmpty()) { - monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, DESTINATION_TYPES_SUFFIX)); + monitor.warning(format(LOG_MISSING_CONFIGURATION, id, DESTINATION_TYPES_SUFFIX)); + } + + if (transferTypes.isEmpty()) { + monitor.warning(format(LOG_MISSING_CONFIGURATION, id, TRANSFER_TYPES_SUFFIX)); } - final Map properties; + Map properties; try { - ObjectMapper mapper = new ObjectMapper(); - properties = mapper.readValue(propertiesJson, new TypeReference>() { + var mapper = new ObjectMapper(); + properties = mapper.readValue(propertiesJson, new TypeReference<>() { }); } catch (JsonProcessingException e) { throw new EdcException(e); } - final boolean missingPublicApiProperty = !properties.containsKey(PUBLIC_API_URL_PROPERTY); + var missingPublicApiProperty = !properties.containsKey(PUBLIC_API_URL_PROPERTY); if (missingPublicApiProperty) { - monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, PROPERTIES_SUFFIX) + "." + PUBLIC_API_URL_PROPERTY); + monitor.warning(format(LOG_MISSING_CONFIGURATION, id, PROPERTIES_SUFFIX) + "." + PUBLIC_API_URL_PROPERTY); } - final boolean invalidConfiguration = - url.isEmpty() || sourceTypes.isEmpty() || destinationTypes.isEmpty(); + var invalidConfiguration = url.isEmpty() || sourceTypes.isEmpty() || destinationTypes.isEmpty(); if (invalidConfiguration || missingPublicApiProperty) { - monitor.warning(String.format(LOG_SKIP_BC_MISSING_CONFIGURATION, id)); + monitor.warning(format(LOG_SKIP_BC_MISSING_CONFIGURATION, id)); return; } - final DataPlaneInstance.Builder builder = - DataPlaneInstance.Builder.newInstance().id(id).url(url); + var builder = DataPlaneInstance.Builder.newInstance().id(id).url(url); sourceTypes.forEach(builder::allowedSourceType); destinationTypes.forEach(builder::allowedDestType); + transferTypes.forEach(builder::allowedTransferType); properties.forEach(builder::property); dataPlaneSelectorService.addInstance(builder.build()); - monitor.debug( - String.format( - LOG_REGISTERED, - id, - url, - String.join(", ", sourceTypes), - String.join(", ", destinationTypes))); + monitor.debug(LOG_REGISTERED.formatted(id, url, join(", ", sourceTypes), join(", ", destinationTypes), join(", ", transferTypes))); } } diff --git a/edc-extensions/dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/dataplane/dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 100% rename from edc-extensions/dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/dataplane/dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension diff --git a/edc-extensions/dataplane/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java b/edc-extensions/dataplane/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java new file mode 100644 index 000000000..549d23705 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java @@ -0,0 +1,162 @@ +/******************************************************************************** + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 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.edc.dataplane.selector.configuration; + +import org.eclipse.edc.connector.dataplane.selector.spi.DataPlaneSelectorService; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.configuration.ConfigFactory; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import static org.eclipse.tractusx.edc.dataplane.selector.configuration.DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX; +import static org.eclipse.tractusx.edc.dataplane.selector.configuration.DataPlaneSelectorConfigurationServiceExtension.DESTINATION_TYPES_SUFFIX; +import static org.eclipse.tractusx.edc.dataplane.selector.configuration.DataPlaneSelectorConfigurationServiceExtension.PROPERTIES_SUFFIX; +import static org.eclipse.tractusx.edc.dataplane.selector.configuration.DataPlaneSelectorConfigurationServiceExtension.PUBLIC_API_URL_PROPERTY; +import static org.eclipse.tractusx.edc.dataplane.selector.configuration.DataPlaneSelectorConfigurationServiceExtension.SOURCE_TYPES_SUFFIX; +import static org.eclipse.tractusx.edc.dataplane.selector.configuration.DataPlaneSelectorConfigurationServiceExtension.URL_SUFFIX; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class DataPlaneSelectorConfigurationServiceExtensionTest { + private static final String S3_BUCKET = "s3-bucket"; + private static final String BLOB_STORAGE = "blob-storage"; + private static final String LOCAL_FILE_SYSTEM = "local-file-system"; + + private static final String DATA_PLANE_INSTANCE_ID = "test-plane"; + private static final String DATA_PLANE_INSTANCE_URL = "http://127.0.0.1:8080/test"; + private static final String DATA_PLANE_INSTANCE_SOURCE_TYPES = "%s, %s".formatted(S3_BUCKET, BLOB_STORAGE); + private static final String DATA_PLANE_INSTANCE_DESTINATION_TYPES = LOCAL_FILE_SYSTEM; + + private static final String URL_KEY = "%s.%s".formatted(DATA_PLANE_INSTANCE_ID, URL_SUFFIX); + private static final String SOURCE_TYPES_KEY = "%s.%s".formatted(DATA_PLANE_INSTANCE_ID, SOURCE_TYPES_SUFFIX); + private static final String DESTINATION_TYPES_KEY = "%s.%s".formatted(DATA_PLANE_INSTANCE_ID, DESTINATION_TYPES_SUFFIX); + private static final String PROPERTIES_KEY = "%s.%s".formatted(DATA_PLANE_INSTANCE_ID, PROPERTIES_SUFFIX); + private final ServiceExtensionContext serviceExtensionContext = mock(); + private final DataPlaneSelectorService dataPlaneSelectorService = mock(); + private final Monitor monitor = mock(); + private DataPlaneSelectorConfigurationServiceExtension extension; + + @BeforeEach + void setup() { + extension = new DataPlaneSelectorConfigurationServiceExtension(); + + when(serviceExtensionContext.getService(DataPlaneSelectorService.class)) + .thenReturn(dataPlaneSelectorService); + when(serviceExtensionContext.getMonitor()).thenReturn(monitor); + } + + @Test + void testName() { + var extension = new DataPlaneSelectorConfigurationServiceExtension(); + + assertNotNull(extension.name()); + assertEquals("Data Plane Selector Configuration Extension", extension.name()); + } + + @Test + void testInitialize() { + + var config = ConfigFactory.fromMap(getConfig()); + when(serviceExtensionContext.getConfig("edc.dataplane.selector")).thenReturn(config); + extension.initialize(serviceExtensionContext); + + verify(serviceExtensionContext, times(1)).getService(DataPlaneSelectorService.class); + verify(serviceExtensionContext, times(1)).getMonitor(); + when(serviceExtensionContext.getConfig(CONFIG_PREFIX)) + .thenReturn(config); + + verify(dataPlaneSelectorService, times(1)) + .addInstance(argThat(dataPlaneInstance -> { + var s3Source = DataAddress.Builder.newInstance().type(S3_BUCKET).build(); + var blobSource = DataAddress.Builder.newInstance().type(BLOB_STORAGE).build(); + var fsSink = DataAddress.Builder.newInstance().type(LOCAL_FILE_SYSTEM).build(); + + return dataPlaneInstance.getId().equals(DATA_PLANE_INSTANCE_ID) && + dataPlaneInstance.getUrl().toString().equals(DATA_PLANE_INSTANCE_URL) && + dataPlaneInstance.canHandle(s3Source, fsSink) && + dataPlaneInstance.canHandle(blobSource, fsSink); + })); + } + + @ParameterizedTest + @ArgumentsSource(MissingConfigArgumentsProvider.class) + void testWarningOnPropertyMissing(String configKey, String configValue) { + var configMap = getConfig(); + configMap.put(configKey, configValue); + var config = ConfigFactory.fromMap(configMap); + when(serviceExtensionContext.getConfig(CONFIG_PREFIX)).thenReturn(config); + extension.initialize(serviceExtensionContext); + + // one warning config missing, one warning data plane instance skipped + verify(monitor, times(3)).warning(anyString()); + } + + @Test + void throwsExceptionOnPropertiesNoJson() { + var configMap = getConfig(); + configMap.put(PROPERTIES_KEY, "no json"); + var config = ConfigFactory.fromMap(configMap); + when(serviceExtensionContext.getConfig(CONFIG_PREFIX)).thenReturn(config); + + assertThrows(EdcException.class, () -> extension.initialize(serviceExtensionContext)); + } + + private Map getConfig() { + return new HashMap<>() { + { + put(URL_KEY, DATA_PLANE_INSTANCE_URL); + put(SOURCE_TYPES_KEY, DATA_PLANE_INSTANCE_SOURCE_TYPES); + put(DESTINATION_TYPES_KEY, DATA_PLANE_INSTANCE_DESTINATION_TYPES); + put(PROPERTIES_KEY, "{ \"%s\": \"%s\" }".formatted(PUBLIC_API_URL_PROPERTY, DATA_PLANE_INSTANCE_URL)); + } + }; + } + + private static class MissingConfigArgumentsProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + Arguments.of(URL_KEY, ""), + Arguments.of(SOURCE_TYPES_KEY, ""), + Arguments.of(DESTINATION_TYPES_KEY, ""), + Arguments.of(PROPERTIES_KEY, "{}")); + } + } +} diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/build.gradle.kts b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/build.gradle.kts new file mode 100644 index 000000000..c99448fc0 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/build.gradle.kts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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 { + `java-library` + `maven-publish` + id("io.swagger.core.v3.swagger-gradle-plugin") +} + +dependencies { + api(project(":spi:tokenrefresh-spi")) + implementation(libs.edc.spi.core) + implementation(libs.edc.spi.web) + implementation(libs.jakarta.rsApi) + implementation(libs.edc.lib.util) + + + testImplementation(libs.edc.junit) + testImplementation(libs.restAssured) + testImplementation(testFixtures(libs.edc.core.jersey)) +} + diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/TokenRefreshApiExtension.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/TokenRefreshApiExtension.java new file mode 100644 index 000000000..907e50169 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/TokenRefreshApiExtension.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.api; + +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.web.spi.WebService; +import org.eclipse.tractusx.edc.dataplane.tokenrefresh.api.v1.TokenRefreshApiController; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.DataPlaneTokenRefreshService; + +import static org.eclipse.tractusx.edc.dataplane.tokenrefresh.api.TokenRefreshApiExtension.NAME; + +@Extension(value = NAME) +public class TokenRefreshApiExtension implements ServiceExtension { + + public static final String NAME = "DataPlane Token Refresh API Extension"; + private static final String PUBLIC_API_CONTEXT = "public"; + @Inject + private DataPlaneTokenRefreshService refreshService; + + @Inject + private WebService webService; + + @Override + public String name() { + return NAME; + } + + @Override + public void initialize(ServiceExtensionContext context) { + var controller = new TokenRefreshApiController(refreshService); + webService.registerResource(PUBLIC_API_CONTEXT, controller); + } +} diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApi.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApi.java new file mode 100644 index 000000000..6d506b719 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApi.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.api.v1; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.eclipse.edc.web.spi.ApiErrorDetail; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; + +@SecurityScheme(name = "Authentication", + description = "Self-Issued ID token containing a token", + type = SecuritySchemeType.HTTP, + scheme = "bearer", + bearerFormat = "JWT") +@OpenAPIDefinition(info = @Info(description = "With this API clients can refresh their access token for a provider's HTTP data plane using an authentication token and a refresh token.", title = "Token Refresh API")) +@Tag(name = "Token Refresh API") +public interface TokenRefreshApi { + + @Operation(description = "Resolves all groups for a particular BPN", + parameters = { @Parameter(name = "grant_type", description = "The grant type. Must be \"refresh_token\""), + @Parameter(name = "refresh_token", description = "The refresh token") }, + responses = { + @ApiResponse(responseCode = "200", description = "The access token and refresh token were updated. Expiry should be " + + "interpreted as starting from the time of message reception, allowing for some leeway.", + content = @Content(schema = @Schema(implementation = TokenResponse.class))), + @ApiResponse(responseCode = "401", description = "The token could not be refreshed due to an authentication error, either the refresh token or the Authorization header were invalid.", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "400", description = "Request body was malformed, query parameters were missing, etc.", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) + }) + TokenResponse refreshToken(String grantType, String refreshToken, String bearerToken); +} diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiController.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiController.java new file mode 100644 index 000000000..ce0c468d2 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiController.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.api.v1; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.edc.util.string.StringUtils; +import org.eclipse.edc.web.spi.exception.AuthenticationFailedException; +import org.eclipse.edc.web.spi.exception.InvalidRequestException; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.DataPlaneTokenRefreshService; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; + +import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION; + +@Produces({ MediaType.APPLICATION_JSON }) +@Path("/token") +public class TokenRefreshApiController implements TokenRefreshApi { + private static final String REFRESH_TOKEN_GRANT = "refresh_token"; + private final DataPlaneTokenRefreshService tokenRefreshService; + + public TokenRefreshApiController(DataPlaneTokenRefreshService tokenRefreshService) { + this.tokenRefreshService = tokenRefreshService; + } + + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Override + public TokenResponse refreshToken(@QueryParam("grant_type") String grantType, + @QueryParam("refresh_token") String refreshToken, + @HeaderParam(AUTHORIZATION) String bearerToken) { + if (!REFRESH_TOKEN_GRANT.equals(grantType)) { + throw new InvalidRequestException("Grant type MUST be '%s' but was '%s'".formatted(REFRESH_TOKEN_GRANT, grantType)); + } + if (StringUtils.isNullOrBlank(refreshToken)) { + throw new InvalidRequestException("Parameter 'refresh_token' cannot be null"); + } + if (StringUtils.isNullOrBlank(bearerToken)) { + throw new AuthenticationFailedException("Authorization header missing"); + } + + return tokenRefreshService.refreshToken(refreshToken, bearerToken) + .orElseThrow(f -> new AuthenticationFailedException(f.getFailureDetail())); + } +} diff --git a/edc-extensions/ssi/ssi-identity-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 85% rename from edc-extensions/ssi/ssi-identity-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 858acb7ef..db16e33f7 100644 --- a/edc-extensions/ssi/ssi-identity-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -1,5 +1,5 @@ ################################################################################# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. @@ -17,4 +17,4 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# -org.eclipse.tractusx.edc.iam.ssi.identity.SsiIdentityServiceExtension +org.eclipse.tractusx.edc.dataplane.tokenrefresh.api.TokenRefreshApiExtension \ No newline at end of file diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiControllerTest.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiControllerTest.java new file mode 100644 index 000000000..d8fc82d13 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiControllerTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.api.v1; + +import io.restassured.http.ContentType; +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.DataPlaneTokenRefreshService; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EmptySource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION; +import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class TokenRefreshApiControllerTest extends RestControllerTestBase { + + private final DataPlaneTokenRefreshService refreshService = mock(); + + @DisplayName("Expect HTTP 400 when no Authorization header is present") + @Test + void refresh_noAuthHeader_expect401() { + baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", "foo-token") + /* missing: .header(AUTHORIZATION, "auth-token") */ + .contentType(ContentType.URLENC) + .then() + .statusCode(401); + } + + @DisplayName("Expect HTTP 200 when the token was successfully refreshed") + @Test + void refresh_expect200() { + when(refreshService.refreshToken(any(), any())).thenReturn(Result.success(new TokenResponse("new-accesstoken", "new-refreshtoken", 3000L, "bearer"))); + baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", "foo-token") + .header(AUTHORIZATION, "auth-token") + .contentType(ContentType.URLENC) + .then() + .statusCode(200) + .body(Matchers.isA(TokenResponse.class)); + } + + @DisplayName("Expect HTTP 400 when an invalid grant type was provided") + @ParameterizedTest(name = "Invalid grant_type: {0}") + @ValueSource(strings = { "REFRESH_TOKEN", "refreshToken", "invalid_grant", "client_credentials", "" }) + @NullSource + void refresh_invalidGrantType_expect400(String grant) { + baseRequest() + .queryParam("grant_type", grant) + .queryParam("refresh_token", "foo-token") + .header(AUTHORIZATION, "auth-token") + .contentType(ContentType.URLENC) + .then() + .statusCode(400); + } + + @DisplayName("Expect HTTP 400 when an invalid refresh token was provided") + @ParameterizedTest(name = "Invalid refresh_token: {0}") + @NullSource + @EmptySource + void refresh_invalidRefreshToken_expect400(String refreshToken) { + baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", refreshToken) + .header(AUTHORIZATION, "auth-token") + .contentType(ContentType.URLENC) + .then() + .statusCode(400); + } + + @DisplayName("Expect HTTP 400 when one of the query params was missing") + @Test + void refresh_queryParamsMissing() { + baseRequest() + .queryParam("grant_type", "refresh_token") + .header(AUTHORIZATION, "auth-token") + .contentType(ContentType.URLENC) + .then() + .statusCode(400); + + baseRequest() + .queryParam("refresh_token", "foo-token") + .header(AUTHORIZATION, "auth-token") + .contentType(ContentType.URLENC) + .then() + .statusCode(400); + } + + @DisplayName("Expect HTTP 401 if the auth header or refresh token are invalid") + @Test + void refresh_tokenInvalid_expect401() { + when(refreshService.refreshToken(any(), any())).thenReturn(Result.failure("Invalid auth token")); + + baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", "foo-token") + .header(AUTHORIZATION, "auth-token") + .contentType(ContentType.URLENC) + .then() + .statusCode(401) + .body(containsString("Invalid auth token")); + } + + @Override + protected Object controller() { + return new TokenRefreshApiController(refreshService); + } + + private RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port) + .basePath("/token") + .when(); + } + +} \ No newline at end of file diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/build.gradle.kts b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/build.gradle.kts new file mode 100644 index 000000000..19c54f505 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/build.gradle.kts @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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 { + `java-library` +} + +dependencies { + api(project(":spi:tokenrefresh-spi")) + api(project(":spi:core-spi")) + implementation(project(":core:core-utils")) + implementation(libs.edc.spi.core) + implementation(libs.edc.spi.token) + implementation(libs.edc.spi.keys) + implementation(libs.edc.spi.identity.did) + implementation(libs.edc.spi.dataplane.dataplane) + implementation(libs.edc.core.token) + implementation(libs.edc.lib.query) + implementation(libs.edc.lib.cryptocommon) + + testImplementation(libs.edc.junit) + testImplementation(libs.edc.dpf.core) + testImplementation(libs.edc.core.connector) + testImplementation(libs.edc.lib.boot) +} + diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceExtension.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceExtension.java new file mode 100644 index 000000000..75c5d8b5e --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceExtension.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core; + +import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAccessTokenService; +import org.eclipse.edc.connector.dataplane.spi.store.AccessTokenDataStore; +import org.eclipse.edc.iam.did.spi.resolution.DidPublicKeyResolver; +import org.eclipse.edc.keys.spi.LocalPublicKeyService; +import org.eclipse.edc.keys.spi.PrivateKeyResolver; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.system.Hostname; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.token.JwtGenerationService; +import org.eclipse.edc.token.spi.TokenValidationService; +import org.eclipse.tractusx.edc.core.utils.RequiredConfigWarnings; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.DataPlaneTokenRefreshService; +import org.jetbrains.annotations.NotNull; + +import java.security.PrivateKey; +import java.time.Clock; +import java.util.function.Supplier; + +import static org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.DataPlaneTokenRefreshServiceExtension.NAME; + +@Extension(value = NAME) +public class DataPlaneTokenRefreshServiceExtension implements ServiceExtension { + public static final String NAME = "DataPlane Token Refresh Service extension"; + public static final int DEFAULT_TOKEN_EXPIRY_TOLERANCE_SECONDS = 5; + public static final long DEFAULT_TOKEN_EXPIRY_SECONDS = 300L; + @Setting(value = "Token expiry tolerance period in seconds to allow for clock skew", defaultValue = "" + DEFAULT_TOKEN_EXPIRY_TOLERANCE_SECONDS) + public static final String TOKEN_EXPIRY_TOLERANCE_SECONDS_PROPERTY = "edc.dataplane.token.expiry.tolerance"; + @Setting(value = "The HTTP endpoint where clients can request a renewal of their access token for the public dataplane API") + public static final String REFRESH_ENDPOINT_PROPERTY = "edc.dataplane.token.refresh.endpoint"; + @Setting(value = "Alias of private key used for signing tokens, retrieved from private key resolver") + public static final String TOKEN_SIGNER_PRIVATE_KEY_ALIAS = "edc.transfer.proxy.token.signer.privatekey.alias"; + @Setting(value = "Alias of public key used for verifying the tokens, retrieved from the vault") + public static final String TOKEN_VERIFIER_PUBLIC_KEY_ALIAS = "edc.transfer.proxy.token.verifier.publickey.alias"; + @Setting(value = "Expiry time of access token in seconds", defaultValue = DEFAULT_TOKEN_EXPIRY_SECONDS + "") + public static final String TOKEN_EXPIRY_SECONDS_PROPERTY = "edc.dataplane.token.expiry"; + + @Setting(value = "DID of this connector", required = true) + private static final String PARTICIPANT_DID_PROPERTY = "edc.iam.issuer.id"; + + @Inject + private TokenValidationService tokenValidationService; + @Inject + private DidPublicKeyResolver didPkResolver; + @Inject + private LocalPublicKeyService localPublicKeyService; + @Inject + private AccessTokenDataStore accessTokenDataStore; + @Inject + private PrivateKeyResolver privateKeyResolver; + @Inject + private Clock clock; + @Inject + private Vault vault; + @Inject + private TypeManager typeManager; + @Inject + private Hostname hostname; + + private DataPlaneTokenRefreshServiceImpl tokenRefreshService; + + @Override + public String name() { + return NAME; + } + + // exposes the service as access token service + @Provider + public DataPlaneAccessTokenService createAccessTokenService(ServiceExtensionContext context) { + return getTokenRefreshService(context); + } + + // exposes the service as pure refresh service + @Provider + public DataPlaneTokenRefreshService createRefreshTokenService(ServiceExtensionContext context) { + return getTokenRefreshService(context); + } + + private int getExpiryToleranceConfig(ServiceExtensionContext context) { + return context.getConfig().getInteger(TOKEN_EXPIRY_TOLERANCE_SECONDS_PROPERTY, DEFAULT_TOKEN_EXPIRY_TOLERANCE_SECONDS); + } + + @NotNull + private DataPlaneTokenRefreshServiceImpl getTokenRefreshService(ServiceExtensionContext context) { + if (tokenRefreshService == null) { + var monitor = context.getMonitor().withPrefix("DataPlane Token Refresh"); + var expiryTolerance = getExpiryToleranceConfig(context); + var refreshEndpoint = getRefreshEndpointConfig(context, monitor); + var tokenExpiry = getExpiryConfig(context); + monitor.debug("Token refresh endpoint: %s".formatted(refreshEndpoint)); + monitor.debug("Token refresh time tolerance: %d s".formatted(expiryTolerance)); + tokenRefreshService = new DataPlaneTokenRefreshServiceImpl(clock, tokenValidationService, didPkResolver, localPublicKeyService, accessTokenDataStore, new JwtGenerationService(), + getPrivateKeySupplier(context), context.getMonitor(), refreshEndpoint, getOwnDid(context), expiryTolerance, tokenExpiry, + () -> context.getConfig().getString(TOKEN_VERIFIER_PUBLIC_KEY_ALIAS), vault, typeManager.getMapper()); + } + return tokenRefreshService; + } + + private Long getExpiryConfig(ServiceExtensionContext context) { + return context.getConfig().getLong(TOKEN_EXPIRY_SECONDS_PROPERTY, DEFAULT_TOKEN_EXPIRY_SECONDS); + } + + private String getRefreshEndpointConfig(ServiceExtensionContext context, Monitor monitor) { + var refreshEndpoint = context.getConfig().getString(REFRESH_ENDPOINT_PROPERTY, null); + if (refreshEndpoint == null) { + var port = context.getConfig().getInteger("web.http.public.port", 8185); + var path = context.getConfig().getString("web.http.public.path", "/api/v2/public"); + refreshEndpoint = "http://%s:%d%s".formatted(hostname.get(), port, path); + monitor.warning("Config property '%s' was not specified, the default '%s' will be used.".formatted(REFRESH_ENDPOINT_PROPERTY, refreshEndpoint)); + } + return refreshEndpoint; + } + + private String getOwnDid(ServiceExtensionContext context) { + var did = context.getConfig().getString(PARTICIPANT_DID_PROPERTY, null); + if (did == null) { + RequiredConfigWarnings.warningNotPresent(context.getMonitor().withPrefix("DataPlane Token Refresh"), PARTICIPANT_DID_PROPERTY); + } + return did; + } + + @NotNull + private Supplier getPrivateKeySupplier(ServiceExtensionContext context) { + return () -> { + var alias = context.getConfig().getString(TOKEN_SIGNER_PRIVATE_KEY_ALIAS); + return privateKeyResolver.resolvePrivateKey(alias) + .orElse(f -> { + context.getMonitor().warning("Cannot resolve private key: " + f.getFailureDetail()); + return null; + }); + }; + } +} diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImpl.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImpl.java new file mode 100644 index 000000000..06c7c2000 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImpl.java @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jwt.JWTClaimNames; +import org.eclipse.edc.connector.dataplane.spi.AccessTokenData; +import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAccessTokenService; +import org.eclipse.edc.connector.dataplane.spi.store.AccessTokenDataStore; +import org.eclipse.edc.iam.did.spi.resolution.DidPublicKeyResolver; +import org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames; +import org.eclipse.edc.keys.spi.LocalPublicKeyService; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.iam.TokenParameters; +import org.eclipse.edc.spi.iam.TokenRepresentation; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.query.Criterion; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.token.rules.ExpirationIssuedAtValidationRule; +import org.eclipse.edc.token.spi.KeyIdDecorator; +import org.eclipse.edc.token.spi.TokenDecorator; +import org.eclipse.edc.token.spi.TokenGenerationService; +import org.eclipse.edc.token.spi.TokenValidationRule; +import org.eclipse.edc.token.spi.TokenValidationService; +import org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.rules.AuthTokenAudienceRule; +import org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.rules.ClaimIsPresentRule; +import org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.rules.IssuerEqualsSubjectRule; +import org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.rules.RefreshTokenValidationRule; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.DataPlaneTokenRefreshService; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; + +import java.security.PrivateKey; +import java.time.Clock; +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 java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.EXPIRATION_TIME; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.AUDIENCE_PROPERTY; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_EXPIRES_IN; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_AUDIENCE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_ENDPOINT; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_TOKEN; + +/** + * This implementation of the {@link DataPlaneTokenRefreshService} validates an incoming authentication token. + */ +public class DataPlaneTokenRefreshServiceImpl implements DataPlaneTokenRefreshService, DataPlaneAccessTokenService { + public static final String ACCESS_TOKEN_CLAIM = "token"; + public static final String TOKEN_ID_CLAIM = "jti"; + private final long tokenExpirySeconds; + private final List authenticationTokenValidationRules; + private final List accessTokenAuthorizationRules; + private final TokenValidationService tokenValidationService; + private final DidPublicKeyResolver publicKeyResolver; + private final LocalPublicKeyService localPublicKeyService; + private final AccessTokenDataStore accessTokenDataStore; + private final TokenGenerationService tokenGenerationService; + private final Supplier privateKeySupplier; + private final Supplier publicKeyIdSupplier; + private final Monitor monitor; + private final String refreshEndpoint; + private final String ownDid; + private final Clock clock; + private final Vault vault; + private final ObjectMapper objectMapper; + + + public DataPlaneTokenRefreshServiceImpl(Clock clock, + TokenValidationService tokenValidationService, + DidPublicKeyResolver publicKeyResolver, + LocalPublicKeyService localPublicKeyService, + AccessTokenDataStore accessTokenDataStore, + TokenGenerationService tokenGenerationService, + Supplier privateKeySupplier, + Monitor monitor, + String refreshEndpoint, + String ownDid, + int tokenExpiryToleranceSeconds, + long tokenExpirySeconds, + Supplier publicKeyIdSupplier, + Vault vault, + ObjectMapper objectMapper) { + this.tokenValidationService = tokenValidationService; + this.publicKeyResolver = publicKeyResolver; + this.localPublicKeyService = localPublicKeyService; + this.accessTokenDataStore = accessTokenDataStore; + this.tokenGenerationService = tokenGenerationService; + this.privateKeySupplier = privateKeySupplier; + this.monitor = monitor; + this.refreshEndpoint = refreshEndpoint; + this.clock = clock; + this.publicKeyIdSupplier = publicKeyIdSupplier; + this.ownDid = ownDid; + this.vault = vault; + this.objectMapper = objectMapper; + this.tokenExpirySeconds = tokenExpirySeconds; + authenticationTokenValidationRules = List.of(new IssuerEqualsSubjectRule(), + new ClaimIsPresentRule(AUDIENCE), // we don't check the contents, only it is present + new ClaimIsPresentRule(ACCESS_TOKEN_CLAIM), + new ClaimIsPresentRule(TOKEN_ID_CLAIM), + new AuthTokenAudienceRule(accessTokenDataStore)); + accessTokenAuthorizationRules = List.of(new IssuerEqualsSubjectRule(), + new ClaimIsPresentRule(AUDIENCE), + new ClaimIsPresentRule(TOKEN_ID_CLAIM), + new ExpirationIssuedAtValidationRule(clock, tokenExpiryToleranceSeconds)); + } + + /** + * Refreshes an incoming refresh token and authentication token. During validation of those tokens, the following steps are performed: + * + *
    + *
  • parse the {@code authenticationToken} into a {@link com.nimbusds.jwt.JWT}
  • + *
  • resolve the DID based on the {@code iss} claim
  • + *
  • resolve the public key material in the DID Document identified by the {@code kid} header
  • + *
  • verify the token's signature
  • + *
  • assert {@code iss} and {@code sub} claims are identical
  • + *
  • assert the the token contains an {@code token} claim, and that the value is identical to the access token we have on record
  • + *
  • assert that the {@code refreshToken} parameter is identical to the refresh token we have on record
  • + *
+ * + * @param refreshToken The refresh token that was issued in the original/previous token request. + * @param authenticationToken A client authentication token + */ + @Override + public Result refreshToken(String refreshToken, String authenticationToken) { + + authenticationToken = authenticationToken.replace("Bearer", "").trim(); + + var authTokenRes = tokenValidationService.validate(authenticationToken, publicKeyResolver, authenticationTokenValidationRules); + if (authTokenRes.failed()) { + return Result.failure("Authentication token validation failed: %s".formatted(authTokenRes.getFailureDetail())); + } + + // 2. extract access token and validate it + var accessToken = authTokenRes.getContent().getStringClaim("token"); + var accessTokenDataResult = tokenValidationService.validate(accessToken, localPublicKeyService, new RefreshTokenValidationRule(vault, refreshToken, objectMapper)) + .map(accessTokenClaims -> accessTokenDataStore.getById(accessTokenClaims.getStringClaim(JwtRegisteredClaimNames.JWT_ID))); + + if (accessTokenDataResult.failed()) { + return Result.failure("Access token validation failed: %s".formatted(accessTokenDataResult.getFailureDetail())); + } + + var existingAccessTokenData = accessTokenDataResult.getContent(); + + var newTokenParams = TokenParameters.Builder.newInstance() + .claims(existingAccessTokenData.claimToken().getClaims()) + .build(); + + var newAccessToken = createToken(newTokenParams).map(tr -> tr.tokenRepresentation().getToken()); + var newRefreshToken = createToken(TokenParameters.Builder.newInstance().build()).map(tr -> tr.tokenRepresentation().getToken()); + if (newAccessToken.failed() || newRefreshToken.failed()) { + var errors = new ArrayList<>(newAccessToken.getFailureMessages()); + errors.addAll(newRefreshToken.getFailureMessages()); + return Result.failure("Failed to regenerate access/refresh token pair: %s".formatted(errors)); + } + + storeRefreshToken(existingAccessTokenData.id(), new RefreshToken(newRefreshToken.getContent(), tokenExpirySeconds, refreshEndpoint)); + + // the ClaimToken is created based solely on the TokenParameters. The additional information (refresh token...) is persisted separately + var claimToken = ClaimToken.Builder.newInstance().claims(newTokenParams.getClaims()).build(); + var accessTokenData = new AccessTokenData(existingAccessTokenData.id(), claimToken, existingAccessTokenData.dataAddress(), existingAccessTokenData.additionalProperties()); + + var storeResult = accessTokenDataStore.update(accessTokenData); + return storeResult.succeeded() ? + Result.success(new TokenResponse(newAccessToken.getContent(), + newRefreshToken.getContent(), tokenExpirySeconds, "bearer")) : + Result.failure(storeResult.getFailureMessages()); + } + + @Override + public Result obtainToken(TokenParameters tokenParameters, DataAddress backendDataAddress, Map additionalTokenData) { + Objects.requireNonNull(tokenParameters, "TokenParameters must be non-null."); + Objects.requireNonNull(backendDataAddress, "DataAddress must be non-null."); + + + //create a refresh token + var refreshTokenResult = createToken(TokenParameters.Builder.newInstance().build()); + if (refreshTokenResult.failed()) { + return Result.failure("Could not generate refresh token: %s".formatted(refreshTokenResult.getFailureDetail())); + } + + var accessTokenResult = createToken(tokenParameters); + if (accessTokenResult.failed()) { + return Result.failure("Could not generate access token: %s".formatted(accessTokenResult.getFailureDetail())); + } + + // the edrAdditionalData contains the refresh token, which is NOT supposed to be put in the DB + // note: can't use DBI (double-bracket initialization) here, because SonarCloud will complain about it + var additionalDataForStorage = new HashMap<>(additionalTokenData); + additionalDataForStorage.put("authType", "bearer"); + + // the ClaimToken is created based solely on the TokenParameters. The additional information (refresh token...) is persisted separately + var claimToken = ClaimToken.Builder.newInstance().claims(tokenParameters.getClaims()).build(); + var accessTokenData = new AccessTokenData(accessTokenResult.getContent().id(), claimToken, backendDataAddress, additionalDataForStorage); + var storeResult = accessTokenDataStore.store(accessTokenData); + + storeRefreshToken(accessTokenResult.getContent().id(), new RefreshToken(refreshTokenResult.getContent().tokenRepresentation().getToken(), + tokenExpirySeconds, refreshEndpoint)); + + // the refresh token information must be returned in the EDR + var audience = additionalDataForStorage.get(AUDIENCE_PROPERTY); + + if (audience == null) { + return Result.failure("Missing audience in the additional properties"); + } + var edrAdditionalData = new HashMap<>(additionalTokenData); + edrAdditionalData.put(EDR_PROPERTY_REFRESH_TOKEN, refreshTokenResult.getContent().tokenRepresentation().getToken()); + edrAdditionalData.put(EDR_PROPERTY_EXPIRES_IN, String.valueOf(tokenExpirySeconds)); + edrAdditionalData.put(EDR_PROPERTY_REFRESH_ENDPOINT, refreshEndpoint); + edrAdditionalData.put(EDR_PROPERTY_REFRESH_AUDIENCE, audience); + + var edrTokenRepresentation = TokenRepresentation.Builder.newInstance() + .token(accessTokenResult.getContent().tokenRepresentation().getToken()) // the access token + .additional(edrAdditionalData) //contains additional properties and the refresh token + .expiresIn(tokenExpirySeconds) //todo: needed? + .build(); + + + return storeResult.succeeded() ? Result.success(edrTokenRepresentation) : Result.failure(storeResult.getFailureMessages()); + } + + @Override + public Result resolve(String token) { + return tokenValidationService.validate(token, localPublicKeyService, accessTokenAuthorizationRules) + .compose(claimToken -> { + var id = claimToken.getStringClaim(JWTClaimNames.JWT_ID); + var tokenData = accessTokenDataStore.getById(id); + return tokenData != null ? Result.success(tokenData) : Result.failure("AccessTokenData with ID '%s' does not exist.".formatted(id)); + }); + } + + @Override + public Result revoke(String transferProcessId, String reason) { + var query = QuerySpec.Builder.newInstance() + .filter(new Criterion("additionalProperties.process_id", "=", transferProcessId)) + .build(); + + var tokens = accessTokenDataStore.query(query); + return tokens.stream().map(this::deleteTokenData) + .reduce(Result::merge) + .orElseGet(() -> Result.failure("AccessTokenData associated to the transfer with ID '%s' does not exist.".formatted(transferProcessId))); + } + + private Result deleteTokenData(AccessTokenData tokenData) { + var result = accessTokenDataStore.deleteById(tokenData.id()); + if (result.failed()) { + return Result.failure(result.getFailureDetail()); + } else { + return Result.success(); + } + } + + /** + * Creates a token that has an ID based on the given token parameters. If the token parameters don't contain a "jti" claim, one + * will be generated at random. + */ + private Result createToken(TokenParameters tokenParameters) { + var claims = new HashMap<>(tokenParameters.getClaims()); + claims.put(JwtRegisteredClaimNames.ISSUED_AT, clock.instant().getEpochSecond()); // iat is millis in upstream -> bug + var claimDecorators = claims.entrySet().stream().map(e -> (TokenDecorator) claimDecorator -> claimDecorator.claims(e.getKey(), e.getValue())); + var headerDecorators = tokenParameters.getHeaders().entrySet().stream().map(e -> (TokenDecorator) headerDecorator -> headerDecorator.header(e.getKey(), e.getValue())); + + var tokenId = new AtomicReference<>(tokenParameters.getStringClaim(TOKEN_ID_CLAIM)); + var allDecorators = new ArrayList<>(Stream.concat(claimDecorators, headerDecorators).toList()); + allDecorators.add(new KeyIdDecorator(publicKeyIdSupplier.get())); + + // if there is no "jti" header on the token params, we'll assign a random one, and add it back to the decorators + if (tokenId.get() == null) { + monitor.info("No '%s' claim found on TokenParameters. Will generate a random one.".formatted(TOKEN_ID_CLAIM)); + tokenId.set(UUID.randomUUID().toString()); + TokenDecorator tokenIdDecorator = params -> params.claims(TOKEN_ID_CLAIM, tokenId.get()); + allDecorators.add(tokenIdDecorator); + } + //if there is not "exp" header on the token params, we'll configure one + if (!tokenParameters.getClaims().containsKey(JwtRegisteredClaimNames.EXPIRATION_TIME)) { + monitor.info("No '%s' claim found on TokenParameters. Will use the configured default of %d seconds".formatted(EXPIRATION_TIME, tokenExpirySeconds)); + var exp = clock.instant().plusSeconds(tokenExpirySeconds).getEpochSecond(); + allDecorators.add(tp -> tp.claims(JwtRegisteredClaimNames.EXPIRATION_TIME, exp)); + } + + return tokenGenerationService.generate(privateKeySupplier, allDecorators.toArray(new TokenDecorator[0])) + .map(tr -> new TokenRepresentationWithId(tokenId.get(), tr)); + } + + private Result storeRefreshToken(String id, RefreshToken refreshToken) { + try { + return vault.storeSecret(id, objectMapper.writeValueAsString(refreshToken)); + } catch (JsonProcessingException e) { + return Result.failure(e.getMessage()); + } + } + + /** + * container object for a TokenRepresentation and an ID + */ + private record TokenRepresentationWithId(String id, TokenRepresentation tokenRepresentation) { + + } + +} diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/RefreshToken.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/RefreshToken.java new file mode 100644 index 000000000..57873e5bb --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/RefreshToken.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core; + +public record RefreshToken(String refreshToken, Long expiresIn, String refreshEndpoint) { + +} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/configuration/GatewayConfigurationRegistry.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/TokenFunctions.java similarity index 50% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/configuration/GatewayConfigurationRegistry.java rename to edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/TokenFunctions.java index 11d4454ea..30bd1f1be 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/configuration/GatewayConfigurationRegistry.java +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/TokenFunctions.java @@ -1,5 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -15,26 +15,27 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + */ -package org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.configuration; +package org.eclipse.tractusx.edc.dataplane.tokenrefresh.core; +import com.nimbusds.jwt.SignedJWT; +import org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames; import org.jetbrains.annotations.Nullable; -/** - * Manages {@link GatewayConfiguration}s. - */ -public interface GatewayConfigurationRegistry { +import java.text.ParseException; - /** - * Returns the configuration for the given alias or null if not found. - */ - @Nullable - GatewayConfiguration getConfiguration(String alias); +public class TokenFunctions { /** - * Registers a configuration for the given alias. + * Returns the "jti" claim of a JWT in serialized format. Will throw a {@link RuntimeException} if the token is not in valid + * serialized JWT format. */ - void register(GatewayConfiguration configuration); - + public static @Nullable String getTokenId(String serializedJwt) { + try { + return SignedJWT.parse(serializedJwt).getJWTClaimsSet().getStringClaim(JwtRegisteredClaimNames.JWT_ID); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } } diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/AuthTokenAudienceRule.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/AuthTokenAudienceRule.java new file mode 100644 index 000000000..49bf722e3 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/AuthTokenAudienceRule.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core.rules; + +import com.nimbusds.jwt.JWTClaimNames; +import org.eclipse.edc.connector.dataplane.spi.store.AccessTokenDataStore; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.token.spi.TokenValidationRule; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +import static org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.TokenFunctions.getTokenId; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.AUDIENCE_PROPERTY; + + +/** + * Validates that the {@code iss} claim of a token is equal to the {@code audience} property found on the {@link org.eclipse.edc.connector.dataplane.spi.AccessTokenData} + * that is associated with that token (using the {@code jti} claim). + */ +public class AuthTokenAudienceRule implements TokenValidationRule { + private final AccessTokenDataStore store; + + public AuthTokenAudienceRule(AccessTokenDataStore store) { + this.store = store; + } + + @Override + public Result checkRule(@NotNull ClaimToken authenticationToken, @Nullable Map map) { + var issuer = authenticationToken.getStringClaim(JWTClaimNames.ISSUER); + var accessToken = authenticationToken.getStringClaim("token"); + if (accessToken == null) { + return Result.failure("Authentication token must contain a 'token' claim"); + } + var tokenId = getTokenId(accessToken); + + var accessTokenData = store.getById(tokenId); + var expectedAudience = accessTokenData.additionalProperties().getOrDefault(AUDIENCE_PROPERTY, null); + if (expectedAudience instanceof String expectedAud) { + return expectedAud.equals(issuer) ? Result.success() : Result.failure("Principal '%s' is not authorized to refresh this token.".formatted(issuer)); + } + + return Result.failure("Property '%s' was expected to be java.lang.String but was %s.".formatted(AUDIENCE_PROPERTY, expectedAudience == null ? null : expectedAudience.getClass())); + } +} diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/ClaimIsPresentRule.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/ClaimIsPresentRule.java new file mode 100644 index 000000000..11cbe8dae --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/ClaimIsPresentRule.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core.rules; + +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.token.spi.TokenValidationRule; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public class ClaimIsPresentRule implements TokenValidationRule { + private final String claimName; + + public ClaimIsPresentRule(String claimName) { + this.claimName = claimName; + } + + @Override + public Result checkRule(@NotNull ClaimToken toVerify, @Nullable Map additional) { + return toVerify.getClaims().containsKey(claimName) ? + Result.success() : + Result.failure("Required claim '%s' not present on token.".formatted(claimName)); + } +} diff --git a/edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/rule/SsiAudienceValidationRule.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/IssuerEqualsSubjectRule.java similarity index 53% rename from edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/rule/SsiAudienceValidationRule.java rename to edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/IssuerEqualsSubjectRule.java index b9f15e5e1..6eba164cc 100644 --- a/edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/rule/SsiAudienceValidationRule.java +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/IssuerEqualsSubjectRule.java @@ -1,5 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -15,10 +15,11 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + */ -package org.eclipse.tractusx.edc.iam.ssi.identity.rule; +package org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.rules; +import com.nimbusds.jwt.JWTClaimNames; import org.eclipse.edc.spi.iam.ClaimToken; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.token.spi.TokenValidationRule; @@ -26,25 +27,18 @@ import org.jetbrains.annotations.Nullable; import java.util.Map; +import java.util.Objects; -import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE; - -public class SsiAudienceValidationRule implements TokenValidationRule { - - private final String endpointAudience; - - public SsiAudienceValidationRule(String endpointAudience) { - this.endpointAudience = endpointAudience; - } +import static org.eclipse.edc.spi.result.Result.failure; +public class IssuerEqualsSubjectRule implements TokenValidationRule { @Override public Result checkRule(@NotNull ClaimToken toVerify, @Nullable Map additional) { - var audiences = toVerify.getListClaim(AUDIENCE); - if (audiences.isEmpty()) { - return Result.failure("Required audience (aud) claim is missing in token"); - } else if (!audiences.contains(endpointAudience)) { - return Result.failure("Token audience (aud) claim did not contain audience: " + endpointAudience); - } - return Result.success(); + var iss = toVerify.getStringClaim(JWTClaimNames.ISSUER); + var sub = toVerify.getStringClaim(JWTClaimNames.SUBJECT); + + return iss != null && Objects.equals(iss, sub) ? + Result.success() : + failure("The 'iss' and 'sub' claims must be non-null and identical."); } } diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/RefreshTokenValidationRule.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/RefreshTokenValidationRule.java new file mode 100644 index 000000000..be7ad813b --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/RefreshTokenValidationRule.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core.rules; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jwt.JWTClaimNames; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.token.spi.TokenValidationRule; +import org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.RefreshToken; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +import static org.eclipse.edc.spi.result.Result.failure; +import static org.eclipse.edc.spi.result.Result.success; + +/** + * Validates that the refresh token information associated with a token's ID ({@code jti}), that is stored in the {@link Vault} + * matches a refresh token string. The refresh token in question is passed into the CTor. + */ +public class RefreshTokenValidationRule implements TokenValidationRule { + private final Vault vault; + private final String incomingRefreshToken; + private final ObjectMapper objectMapper; + + public RefreshTokenValidationRule(Vault vault, String incomingRefreshToken, ObjectMapper objectMapper) { + this.vault = vault; + this.incomingRefreshToken = incomingRefreshToken; + this.objectMapper = objectMapper; + } + + @Override + public Result checkRule(@NotNull ClaimToken accessToken, @Nullable Map additional) { + + var tokenId = accessToken.getStringClaim(JWTClaimNames.JWT_ID); + var storedRefreshTokenJson = vault.resolveSecret(tokenId); + if (storedRefreshTokenJson == null) { + return failure("No refresh token with the ID '%s' was found in the vault.".formatted(tokenId)); + } + return parse(storedRefreshTokenJson) + .compose(rt -> incomingRefreshToken.equals(rt.refreshToken()) ? + success() : + failure("Provided refresh token does not match the stored refresh token.")); + } + + private Result parse(String storedRefreshTokenJson) { + try { + return success(objectMapper.readValue(storedRefreshTokenJson, RefreshToken.class)); + } catch (JsonProcessingException e) { + return failure("Failed to parse stored secret: " + e.getMessage()); + } + } + +} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 84% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index cd51fc686..99b6a2cb5 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -1,5 +1,5 @@ ################################################################################# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. @@ -17,4 +17,4 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# -org.eclipse.tractusx.edc.dataplane.proxy.provider.api.DataPlaneProxyProviderApiExtension +org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.DataPlaneTokenRefreshServiceExtension \ No newline at end of file diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImplComponentTest.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImplComponentTest.java new file mode 100644 index 000000000..ee73ecc67 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImplComponentTest.java @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.KeyUse; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import org.eclipse.edc.boot.vault.InMemoryVault; +import org.eclipse.edc.connector.dataplane.framework.store.InMemoryAccessTokenDataStore; +import org.eclipse.edc.iam.did.spi.resolution.DidPublicKeyResolver; +import org.eclipse.edc.junit.annotations.ComponentTest; +import org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames; +import org.eclipse.edc.keys.spi.LocalPublicKeyService; +import org.eclipse.edc.query.CriterionOperatorRegistryImpl; +import org.eclipse.edc.security.token.jwt.CryptoConverter; +import org.eclipse.edc.spi.iam.TokenParameters; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.token.JwtGenerationService; +import org.eclipse.edc.token.TokenValidationServiceImpl; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.text.ParseException; +import java.time.Clock; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.AUDIENCE_PROPERTY; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_EXPIRES_IN; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_ENDPOINT; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_TOKEN; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ComponentTest +class DataPlaneTokenRefreshServiceImplComponentTest { + + public static final String PROVIDER_BPN = "BPN0000ALICE"; + public static final String CONSUMER_BPN = "BPN0000BOB"; + public static final String TEST_REFRESH_ENDPOINT = "https://fizz.buzz.com"; + public static final String CONSUMER_DID = "did:web:bob"; + public static final String PROVIDER_DID = "did:web:alice"; + private final DidPublicKeyResolver didPkResolverMock = mock(); + private final LocalPublicKeyService localPublicKeyService = mock(); + private DataPlaneTokenRefreshServiceImpl tokenRefreshService; + private InMemoryAccessTokenDataStore tokenDataStore; + private ECKey consumerKey; + private ECKey providerKey; + + @BeforeEach + void setup() throws JOSEException { + + providerKey = new ECKeyGenerator(Curve.P_384).keyID(PROVIDER_BPN + "#provider-key").keyUse(KeyUse.SIGNATURE).generate(); + consumerKey = new ECKeyGenerator(Curve.P_384).keyID(CONSUMER_DID + "#consumer-key").keyUse(KeyUse.SIGNATURE).generate(); + + var privateKey = providerKey.toPrivateKey(); + + tokenDataStore = new InMemoryAccessTokenDataStore(CriterionOperatorRegistryImpl.ofDefaults()); + tokenRefreshService = new DataPlaneTokenRefreshServiceImpl(Clock.systemUTC(), + new TokenValidationServiceImpl(), + didPkResolverMock, + localPublicKeyService, + tokenDataStore, + new JwtGenerationService(), + () -> privateKey, + mock(), + TEST_REFRESH_ENDPOINT, + PROVIDER_DID, + 1, + 300L, + () -> providerKey.getKeyID(), + new InMemoryVault(mock()), + new ObjectMapper()); + + when(localPublicKeyService.resolveKey(eq(consumerKey.getKeyID()))).thenReturn(Result.success(consumerKey.toPublicKey())); + when(localPublicKeyService.resolveKey(eq(providerKey.getKeyID()))).thenReturn(Result.success(providerKey.toPublicKey())); + + when(didPkResolverMock.resolveKey(eq(consumerKey.getKeyID()))).thenReturn(Result.success(consumerKey.toPublicKey())); + when(didPkResolverMock.resolveKey(eq(providerKey.getKeyID()))).thenReturn(Result.success(providerKey.toPublicKey())); + } + + @DisplayName("Verify that a correct EDR is obtained") + @Test + void obtainToken() { + var tokenId = "test-token-id"; + var edr = tokenRefreshService.obtainToken(tokenParams(tokenId), DataAddress.Builder.newInstance().type("test-type").build(), Map.of(AUDIENCE_PROPERTY, CONSUMER_DID)); + assertThat(edr).isSucceeded(); + // assert access token contents + assertThat(asClaims(edr.getContent().getToken())) + .containsEntry("iss", PROVIDER_BPN) + .containsEntry("sub", PROVIDER_BPN) + .containsEntry("aud", List.of(CONSUMER_BPN)) + .containsKey("exp"); + + // assert additional properties -> refresh token + assertThat(edr.getContent().getAdditional()) + .containsEntry(EDR_PROPERTY_REFRESH_ENDPOINT, TEST_REFRESH_ENDPOINT) + .containsKey(EDR_PROPERTY_REFRESH_TOKEN) + .containsKey(EDR_PROPERTY_EXPIRES_IN); + + // verify that the correct data was stored + var storedData = tokenDataStore.getById(tokenId); + assertThat(storedData).isNotNull(); + assertThat(storedData.additionalProperties()) + .hasSize(2) + .containsEntry(AUDIENCE_PROPERTY, CONSUMER_DID) + .containsEntry("authType", "bearer"); + + } + + @DisplayName("Verify that a token can be refreshed") + @Test + void refresh_success() throws JOSEException { + + var tokenId = "test-token-id"; + var edr = tokenRefreshService.obtainToken(tokenParams(tokenId), DataAddress.Builder.newInstance().type("test-type").build(), Map.of(AUDIENCE_PROPERTY, CONSUMER_DID)) + .orElseThrow(f -> new RuntimeException(f.getFailureDetail())); + + var accessToken = edr.getToken(); + var jwsHeader = new JWSHeader.Builder(JWSAlgorithm.ES384).keyID(consumerKey.getKeyID()).build(); + var claimsSet = getAuthTokenClaims(tokenId, accessToken).build(); + + var signedAuthToken = new SignedJWT(jwsHeader, claimsSet); + signedAuthToken.sign(CryptoConverter.createSigner(consumerKey)); + var tokenResponse = tokenRefreshService.refreshToken(edr.getAdditional().get(EDR_PROPERTY_REFRESH_TOKEN).toString(), signedAuthToken.serialize()); + + assertThat(tokenResponse).withFailMessage(tokenResponse::getFailureDetail).isSucceeded() + .satisfies(tr -> assertThat(tr.refreshToken()).isNotNull()) + .satisfies(tr -> assertThat(tr.accessToken()).isNotNull()); + + assertThat(tokenDataStore.getById(tokenId).additionalProperties()) + .hasSize(2) + .doesNotContainKey("refreshToken"); + } + + @DisplayName("Verify that a stolen refresh token cannot be used to refresh an access token") + @Test + void refresh_originalTokenWasIssuedToDifferentPrincipal() throws JOSEException { + var trudyKey = new ECKeyGenerator(Curve.P_256).keyID("did:web:trudy#trudy-key").generate(); + when(didPkResolverMock.resolveKey(eq("did:web:trudy#trudy-key"))).thenReturn(Result.success(trudyKey.toPublicKey())); + + + var tokenId = "test-token-id"; + var edr = tokenRefreshService.obtainToken(tokenParams(tokenId), DataAddress.Builder.newInstance().type("test-type").build(), Map.of(AUDIENCE_PROPERTY, "did:web:trudy")) + .orElseThrow(f -> new RuntimeException(f.getFailureDetail())); + + // bob attempts to create an auth token with an EDR he stole from trudy + var accessToken = edr.getToken(); + var jwsHeader = new JWSHeader.Builder(JWSAlgorithm.ES384).keyID(consumerKey.getKeyID()).build(); + var claimsSet = getAuthTokenClaims(tokenId, accessToken).build(); + + var signedAuthToken = new SignedJWT(jwsHeader, claimsSet); + signedAuthToken.sign(CryptoConverter.createSigner(consumerKey)); + var tokenResponse = tokenRefreshService.refreshToken(edr.getAdditional().get(EDR_PROPERTY_REFRESH_TOKEN).toString(), signedAuthToken.serialize()); + + // todo: once the AuthTokenAudienceRule is re-enabled in the DataPlaneTokenRefreshServiceImpl the following assertion needs to be uncommented + // assertThat(tokenResponse).isFailed().detail().isEqualTo("Authentication token validation failed: Principal 'did:web:bob' is not authorized to refresh this token."); + } + + @DisplayName("Verify that a spoofed refresh attempt is rejected ") + @Test + void refresh_issuerNotVerifiable() throws JOSEException { + var trudyKey = new ECKeyGenerator(Curve.P_384).keyID("did:web:trudy#trudy-key").generate(); + when(didPkResolverMock.resolveKey(eq(trudyKey.getKeyID()))).thenReturn(Result.success(trudyKey.toPublicKey())); + + var tokenId = "test-token-id"; + var edr = tokenRefreshService.obtainToken(tokenParams(tokenId), DataAddress.Builder.newInstance().type("test-type").build(), Map.of(AUDIENCE_PROPERTY, "did:web:trudy")) + .orElseThrow(f -> new RuntimeException(f.getFailureDetail())); + + // bob poses as trudy, using her key-ID and DID, but has to use his own private key + var accessToken = edr.getToken(); + var jwsHeader = new JWSHeader.Builder(JWSAlgorithm.ES384).keyID(trudyKey.getKeyID()).build(); + var claimsSet = getAuthTokenClaims(tokenId, accessToken).issuer("did:web:trudy").subject("did:web:trudy").build(); + + var signedAuthToken = new SignedJWT(jwsHeader, claimsSet); + signedAuthToken.sign(CryptoConverter.createSigner(consumerKey)); + var tokenResponse = tokenRefreshService.refreshToken(edr.getAdditional().get(EDR_PROPERTY_REFRESH_TOKEN).toString(), signedAuthToken.serialize()); + + assertThat(tokenResponse).isFailed().detail().isEqualTo("Authentication token validation failed: Token verification failed"); + } + + @DisplayName("Verify that a refresh attempt fails if no \"token\" claim is present") + @Test + void refresh_whenNoAccessTokenClaim() throws JOSEException { + var tokenId = "test-token-id"; + var edr = tokenRefreshService.obtainToken(tokenParams(tokenId), DataAddress.Builder.newInstance().type("test-type").build(), Map.of(AUDIENCE_PROPERTY, CONSUMER_DID)) + .orElseThrow(f -> new RuntimeException(f.getFailureDetail())); + + var accessToken = edr.getToken(); + var jwsHeader = new JWSHeader.Builder(JWSAlgorithm.ES384).keyID(consumerKey.getKeyID()).build(); + var claimsSet = getAuthTokenClaims(tokenId, accessToken).claim("token", null).build(); + + var signedAuthToken = new SignedJWT(jwsHeader, claimsSet); + signedAuthToken.sign(CryptoConverter.createSigner(consumerKey)); + var tokenResponse = tokenRefreshService.refreshToken(edr.getAdditional().get(EDR_PROPERTY_REFRESH_TOKEN).toString(), signedAuthToken.serialize()); + + assertThat(tokenResponse).isFailed() + .detail() + .contains(" Required claim 'token' not present on token."); + } + + @DisplayName("Verify that the equality of the 'iss' and the 'sub' claim of the authentication token") + @Test + void refresh_whenIssNotEqualToSub() throws JOSEException { + var tokenId = "test-token-id"; + var edr = tokenRefreshService.obtainToken(tokenParams(tokenId), DataAddress.Builder.newInstance().type("test-type").build(), Map.of(AUDIENCE_PROPERTY, CONSUMER_DID)) + .orElseThrow(f -> new RuntimeException(f.getFailureDetail())); + + var accessToken = edr.getToken(); + var jwsHeader = new JWSHeader.Builder(JWSAlgorithm.ES384).keyID(consumerKey.getKeyID()).build(); + var claimsSet = getAuthTokenClaims(tokenId, accessToken) + .issuer(CONSUMER_DID) + .subject("violating-subject") + .build(); + + var signedAuthToken = new SignedJWT(jwsHeader, claimsSet); + signedAuthToken.sign(CryptoConverter.createSigner(consumerKey)); + var tokenResponse = tokenRefreshService.refreshToken(edr.getAdditional().get(EDR_PROPERTY_REFRESH_TOKEN).toString(), signedAuthToken.serialize()); + + assertThat(tokenResponse).isFailed() + .detail() + .isEqualTo("Authentication token validation failed: The 'iss' and 'sub' claims must be non-null and identical."); + } + + @DisplayName("Verify that resolving an expired token fails") + @Test + void resolve_whenExpired_shouldFail() { + var tokenId = "test-token-id"; + var edr = tokenRefreshService.obtainToken(tokenParamsBuilder(tokenId) + //token was issued 10min ago, and expired 5min ago + .claims(JwtRegisteredClaimNames.ISSUED_AT, Instant.now().minusSeconds(600).getEpochSecond()) + .claims(JwtRegisteredClaimNames.EXPIRATION_TIME, Instant.now().minusSeconds(300).getEpochSecond()) + .build(), + DataAddress.Builder.newInstance().type("test-type").build(), Map.of(AUDIENCE_PROPERTY, CONSUMER_DID)) + .orElseThrow(f -> new RuntimeException(f.getFailureDetail())); + + assertThat(tokenRefreshService.resolve(edr.getToken())).isFailed() + .detail().isEqualTo("Token has expired (exp)"); + + } + + @DisplayName("Verify that resolving a valid token succeeds") + @Test + void resolve_success() { + var tokenId = "test-token-id"; + var edr = tokenRefreshService.obtainToken(tokenParamsBuilder(tokenId) + .claims(JwtRegisteredClaimNames.ISSUED_AT, Instant.now().getEpochSecond()) + .build(), + DataAddress.Builder.newInstance().type("test-type").build(), Map.of(AUDIENCE_PROPERTY, CONSUMER_DID)) + .orElseThrow(f -> new RuntimeException(f.getFailureDetail())); + + assertThat(tokenRefreshService.resolve(edr.getToken())).isSucceeded(); + } + + @DisplayName("Verify that attempting to resolve a non-existing token results in a failure") + @Test + void resolve_notFound() { + var tokenId = "test-token-id"; + var edr = tokenRefreshService.obtainToken(tokenParamsBuilder(tokenId) + .claims(JwtRegisteredClaimNames.ISSUED_AT, Instant.now().getEpochSecond()) + .build(), + DataAddress.Builder.newInstance().type("test-type").build(), Map.of(AUDIENCE_PROPERTY, CONSUMER_DID)) + .orElseThrow(f -> new RuntimeException(f.getFailureDetail())); + tokenDataStore.deleteById(tokenId).orElseThrow(f -> new AssertionError(f.getFailureDetail())); + + assertThat(tokenRefreshService.resolve(edr.getToken())) + .isFailed() + .detail().isEqualTo("AccessTokenData with ID '%s' does not exist.".formatted(tokenId)); + } + + private JWTClaimsSet.Builder getAuthTokenClaims(String tokenId, String accessToken) { + return new JWTClaimsSet.Builder() + .jwtID(tokenId) + .issuer(CONSUMER_DID) + .subject(CONSUMER_DID) + .audience(PROVIDER_DID) + .claim("token", accessToken); + } + + private TokenParameters tokenParams(String id) { + return tokenParamsBuilder(id).build(); + } + + private TokenParameters.Builder tokenParamsBuilder(String id) { + return TokenParameters.Builder.newInstance() + .claims(JwtRegisteredClaimNames.JWT_ID, id) + .claims(JwtRegisteredClaimNames.AUDIENCE, CONSUMER_BPN) + .claims(JwtRegisteredClaimNames.ISSUER, PROVIDER_BPN) + .claims(JwtRegisteredClaimNames.SUBJECT, PROVIDER_BPN) + .claims(JwtRegisteredClaimNames.EXPIRATION_TIME, Instant.now().plusSeconds(60).getEpochSecond()) + .claims(JwtRegisteredClaimNames.ISSUED_AT, Instant.now().getEpochSecond()) + .header("kid", providerKey.getKeyID()); + + } + + private Map asClaims(String serializedJwt) { + try { + var jwt = SignedJWT.parse(serializedJwt); + return jwt.getJWTClaimsSet().getClaims(); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImplTest.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImplTest.java new file mode 100644 index 000000000..1a7ec0ca7 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImplTest.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.connector.dataplane.spi.AccessTokenData; +import org.eclipse.edc.connector.dataplane.spi.store.AccessTokenDataStore; +import org.eclipse.edc.iam.did.spi.resolution.DidPublicKeyResolver; +import org.eclipse.edc.keys.spi.LocalPublicKeyService; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.iam.TokenParameters; +import org.eclipse.edc.spi.iam.TokenRepresentation; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.token.spi.TokenDecorator; +import org.eclipse.edc.token.spi.TokenGenerationService; +import org.eclipse.edc.token.spi.TokenValidationRule; +import org.eclipse.edc.token.spi.TokenValidationService; +import org.junit.jupiter.api.Test; + +import java.time.Clock; +import java.util.Map; +import java.util.regex.Pattern; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.TestFunctions.createJwt; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.AUDIENCE_PROPERTY; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_EXPIRES_IN; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_ENDPOINT; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_TOKEN; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +class DataPlaneTokenRefreshServiceImplTest { + private static final Pattern UUID_PATTERN = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); + private final AccessTokenDataStore accessTokenDataStore = mock(); + private final TokenGenerationService tokenGenService = mock(); + private final TokenValidationService tokenValidationService = mock(); + private final DidPublicKeyResolver didPublicKeyResolver = mock(); + + private final LocalPublicKeyService localPublicKeyService = mock(); + + private final DataPlaneTokenRefreshServiceImpl accessTokenService = new DataPlaneTokenRefreshServiceImpl(Clock.systemUTC(), + tokenValidationService, didPublicKeyResolver, localPublicKeyService, accessTokenDataStore, tokenGenService, mock(), mock(), + "https://example.com", "did:web:provider", 1, 300L, + () -> "keyid", mock(), new ObjectMapper()); + + + @Test + void obtainToken() { + var params = TokenParameters.Builder.newInstance().claims("foo", "bar").claims("jti", "baz").header("qux", "quz").build(); + var address = DataAddress.Builder.newInstance().type("test-type").build(); + + when(tokenGenService.generate(any(), any(TokenDecorator[].class))).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-token").build())); + when(accessTokenDataStore.store(any(AccessTokenData.class))).thenReturn(StoreResult.success()); + + var result = accessTokenService.obtainToken(params, address, Map.of("fizz", "buzz", "refreshToken", "getsOverwritten", AUDIENCE_PROPERTY, "audience")); + assertThat(result).isSucceeded().extracting(TokenRepresentation::getToken).isEqualTo("foo-token"); + assertThat(result.getContent().getAdditional()) + .containsKeys("fizz", EDR_PROPERTY_REFRESH_TOKEN, EDR_PROPERTY_EXPIRES_IN, EDR_PROPERTY_REFRESH_ENDPOINT) + .containsEntry(EDR_PROPERTY_REFRESH_TOKEN, "foo-token"); + + verify(tokenGenService, times(2)).generate(any(), any(TokenDecorator[].class)); + verify(accessTokenDataStore).store(any(AccessTokenData.class)); + } + + @Test + void obtainToken_withAdditionalProperties() { + var params = TokenParameters.Builder.newInstance().claims("foo", "bar").claims("jti", "baz").header("qux", "quz").build(); + var address = DataAddress.Builder.newInstance().type("test-type").build(); + + when(tokenGenService.generate(any(), any(TokenDecorator[].class))).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-token").build())); + when(accessTokenDataStore.store(any(AccessTokenData.class))).thenReturn(StoreResult.success()); + + var result = accessTokenService.obtainToken(params, address, Map.of("foo", "bar", AUDIENCE_PROPERTY, "audience")); + assertThat(result).isSucceeded().extracting(TokenRepresentation::getToken).isEqualTo("foo-token"); + + verify(tokenGenService, times(2)).generate(any(), any(TokenDecorator[].class)); + verify(accessTokenDataStore).store(argThat(accessTokenData -> accessTokenData.additionalProperties().get("foo").equals("bar"))); + } + + @Test + void obtainToken_invalidParams() { + assertThatThrownBy(() -> accessTokenService.obtainToken(null, DataAddress.Builder.newInstance().type("foo").build(), Map.of())) + .isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> accessTokenService.obtainToken(TokenParameters.Builder.newInstance().build(), null, Map.of())) + .isInstanceOf(NullPointerException.class); + + } + + @Test + void obtainToken_noTokenId() { + var params = TokenParameters.Builder.newInstance().claims("foo", "bar")/* missing: .claims("jti", "baz")*/.header("qux", "quz").build(); + var address = DataAddress.Builder.newInstance().type("test-type").build(); + + when(tokenGenService.generate(any(), any(TokenDecorator[].class))).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-token").build())); + when(accessTokenDataStore.store(any(AccessTokenData.class))).thenReturn(StoreResult.success()); + + var result = accessTokenService.obtainToken(params, address, Map.of(AUDIENCE_PROPERTY, "audience")); + assertThat(result).isSucceeded().extracting(TokenRepresentation::getToken).isEqualTo("foo-token"); + + verify(tokenGenService, times(2)).generate(any(), any(TokenDecorator[].class)); + verify(accessTokenDataStore).store(argThat(accessTokenData -> UUID_PATTERN.matcher(accessTokenData.id()).matches())); + } + + @Test + void obtainToken_creationFails() { + var params = TokenParameters.Builder.newInstance().claims("foo", "bar").claims("jti", "baz").header("qux", "quz").build(); + var address = DataAddress.Builder.newInstance().type("test-type").build(); + + when(tokenGenService.generate(any(), any(TokenDecorator[].class))).thenReturn(Result.failure("test failure")); + + var result = accessTokenService.obtainToken(params, address, Map.of()); + assertThat(result).isFailed().detail().contains("test failure"); + + verify(tokenGenService).generate(any(), any(TokenDecorator[].class)); + verifyNoMoreInteractions(accessTokenDataStore); + } + + @Test + void obtainToken_storingFails() { + var params = TokenParameters.Builder.newInstance().claims("foo", "bar").claims("jti", "baz").header("qux", "quz").build(); + var address = DataAddress.Builder.newInstance().type("test-type").build(); + + when(tokenGenService.generate(any(), any(TokenDecorator[].class))).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-token").build())); + when(accessTokenDataStore.store(any(AccessTokenData.class))).thenReturn(StoreResult.alreadyExists("test failure")); + + var result = accessTokenService.obtainToken(params, address, Map.of(AUDIENCE_PROPERTY, "audience")); + assertThat(result).isFailed().detail().isEqualTo("test failure"); + + verify(tokenGenService, times(2)).generate(any(), any(TokenDecorator[].class)); + verify(accessTokenDataStore).store(any(AccessTokenData.class)); + } + + @Test + void resolve() { + var tokenId = "test-id"; + var claimToken = ClaimToken.Builder.newInstance().claim("jti", tokenId).build(); + when(tokenValidationService.validate(anyString(), any(), anyList())) + .thenReturn(Result.success(claimToken)); + when(accessTokenDataStore.getById(eq(tokenId))).thenReturn(new AccessTokenData(tokenId, ClaimToken.Builder.newInstance().build(), + DataAddress.Builder.newInstance().type("test-type").build())); + + var result = accessTokenService.resolve("some-jwt"); + assertThat(result).isSucceeded() + .satisfies(atd -> assertThat(atd.id()).isEqualTo(tokenId)); + verify(tokenValidationService).validate(eq("some-jwt"), any(), anyList()); + verify(accessTokenDataStore).getById(eq(tokenId)); + } + + @Test + void resolve_whenValidationFails() { + var tokenId = "test-id"; + var claimToken = ClaimToken.Builder.newInstance().claim("jti", tokenId).build(); + when(tokenValidationService.validate(anyString(), any(), anyList())) + .thenReturn(Result.failure("test-failure")); + + var result = accessTokenService.resolve("some-jwt"); + assertThat(result).isFailed() + .detail().isEqualTo("test-failure"); + verify(tokenValidationService).validate(eq("some-jwt"), any(), anyList()); + verifyNoInteractions(accessTokenDataStore); + } + + @Test + void resolve_whenTokenIdNotFound() { + var tokenId = "test-id"; + var claimToken = ClaimToken.Builder.newInstance().claim("jti", tokenId).build(); + when(tokenValidationService.validate(anyString(), any(), anyList())) + .thenReturn(Result.success(claimToken)); + when(accessTokenDataStore.getById(eq(tokenId))).thenReturn(null); + + var result = accessTokenService.resolve("some-jwt"); + assertThat(result).isFailed() + .detail().isEqualTo("AccessTokenData with ID 'test-id' does not exist."); + verify(tokenValidationService).validate(eq("some-jwt"), any(), anyList()); + verify(accessTokenDataStore).getById(eq(tokenId)); + } + + @Test + void refresh_whenAccessTokenDataNotResolved() { + var accessToken = "foo-bar"; + var refreshToken = "fizz-buzz"; + when(tokenValidationService.validate(eq(accessToken), any(), anyList())) + .thenReturn(Result.failure("test-failure")); + + assertThat(accessTokenService.refreshToken(refreshToken, accessToken)) + .isFailed() + .detail() + .isEqualTo("Authentication token validation failed: test-failure"); + + verify(tokenValidationService).validate(eq(accessToken), any(), anyList()); + verifyNoMoreInteractions(tokenValidationService, tokenGenService, didPublicKeyResolver, accessTokenDataStore); + } + + @Test + void refresh_whenRegeneratingTokenFails() { + var accessToken = createJwt("quizz-quazz"); + var authenticationToken = createJwt("foo-bar"); + var refreshToken = createJwt("fizz-buzz"); + + var tokenId = "token-id"; + when(tokenValidationService.validate(eq(authenticationToken), any(), anyList())) + .thenReturn(Result.success(ClaimToken.Builder.newInstance().claim("token", accessToken).build())); + + when(tokenValidationService.validate(eq(accessToken), any(), any(TokenValidationRule[].class))) + .thenReturn(Result.success(ClaimToken.Builder.newInstance().claim("jti", tokenId).build())); + + when(accessTokenDataStore.getById(eq(tokenId))).thenReturn(new AccessTokenData(tokenId, ClaimToken.Builder.newInstance().claim("claim1", "value1").build(), + DataAddress.Builder.newInstance().type("type").build(), Map.of("fizz", "buzz"))); + + when(tokenGenService.generate(any(), any(TokenDecorator[].class))).thenReturn(Result.failure("generator-failure")); + + + assertThat(accessTokenService.refreshToken(refreshToken, authenticationToken)) + .isFailed() + .detail() + .startsWith("Failed to regenerate access/refresh token pair: "); + + verify(tokenValidationService).validate(eq(accessToken), any(), any(TokenValidationRule[].class)); + verify(tokenValidationService).validate(eq(authenticationToken), any(), anyList()); + verify(accessTokenDataStore).getById(tokenId); + verify(tokenGenService, times(2)).generate(any(), any(TokenDecorator[].class)); + verifyNoMoreInteractions(tokenValidationService, tokenGenService, didPublicKeyResolver, accessTokenDataStore); + } + + @Test + void refresh_whenStoreFails() { + var accessToken = createJwt("quizz-quazz"); + var authenticationToken = createJwt("foo-bar"); + var refreshToken = createJwt("fizz-buzz"); + + var tokenId = "token-id"; + when(tokenValidationService.validate(eq(authenticationToken), any(), anyList())) + .thenReturn(Result.success(ClaimToken.Builder.newInstance().claim("token", accessToken).build())); + + when(tokenValidationService.validate(eq(accessToken), any(), any(TokenValidationRule[].class))) + .thenReturn(Result.success(ClaimToken.Builder.newInstance().claim("jti", tokenId).build())); + + + when(accessTokenDataStore.getById(eq(tokenId))).thenReturn(new AccessTokenData(tokenId, ClaimToken.Builder.newInstance().claim("claim1", "value1").build(), + DataAddress.Builder.newInstance().type("type").build(), Map.of("fizz", "buzz"))); + + when(tokenGenService.generate(any(), any(TokenDecorator[].class))).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().build())); + + when(accessTokenDataStore.update(any())).thenReturn(StoreResult.alreadyExists("test-failure")); + + assertThat(accessTokenService.refreshToken(refreshToken, authenticationToken)) + .isFailed() + .detail() + .startsWith("test-failure"); + + verify(tokenValidationService).validate(eq(authenticationToken), any(), anyList()); + verify(tokenValidationService).validate(eq(accessToken), any(), any(TokenValidationRule[].class)); + verify(accessTokenDataStore).getById(tokenId); + verify(tokenGenService, times(2)).generate(any(), any(TokenDecorator[].class)); + verify(accessTokenDataStore).update(any()); + verifyNoMoreInteractions(tokenValidationService, tokenGenService, didPublicKeyResolver, accessTokenDataStore); + } + + @Test + void refresh_successful() { + var accessToken = createJwt("quizz-quazz"); + var authenticationToken = createJwt("foo-bar"); + var refreshToken = createJwt("fizz-buzz"); + + var tokenId = "token-id"; + when(tokenValidationService.validate(eq(authenticationToken), any(), anyList())) + .thenReturn(Result.success(ClaimToken.Builder.newInstance().claim("token", accessToken).build())); + + when(tokenValidationService.validate(eq(accessToken), any(), any(TokenValidationRule[].class))) + .thenReturn(Result.success(ClaimToken.Builder.newInstance().claim("jti", tokenId).build())); + + when(accessTokenDataStore.getById(eq(tokenId))).thenReturn(new AccessTokenData(tokenId, ClaimToken.Builder.newInstance().claim("claim1", "value1").build(), + DataAddress.Builder.newInstance().type("type").build(), Map.of("fizz", "buzz"))); + + when(tokenGenService.generate(any(), any(TokenDecorator[].class))) + .thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("fizz-token").build())) + .thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("buzz-token").build())); + + when(accessTokenDataStore.update(any())).thenReturn(StoreResult.success()); + + assertThat(accessTokenService.refreshToken(refreshToken, authenticationToken)) + .isSucceeded() + .satisfies(tr -> { + assertThat(tr.accessToken()).isEqualTo("fizz-token"); + assertThat(tr.refreshToken()).isEqualTo("buzz-token"); + }); + + verify(tokenValidationService).validate(eq(authenticationToken), any(), anyList()); + verify(tokenValidationService).validate(eq(accessToken), any(), any(TokenValidationRule[].class)); + verify(accessTokenDataStore).getById(tokenId); + verify(tokenGenService, times(2)).generate(any(), any(TokenDecorator[].class)); + verify(accessTokenDataStore).update(any()); + verifyNoMoreInteractions(tokenValidationService, tokenGenService, didPublicKeyResolver, accessTokenDataStore); + } +} \ No newline at end of file diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/TestFunctions.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/TestFunctions.java new file mode 100644 index 000000000..c0e43d771 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/TestFunctions.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import org.eclipse.edc.spi.iam.ClaimToken; + +import java.util.UUID; + +public class TestFunctions { + public static ClaimToken createAuthenticationToken(String id) { + return ClaimToken.Builder.newInstance() + .claim("token", createJwt(id)) + .claim("jti", UUID.randomUUID().toString()) + .claim("iss", "did:web:bob") + .build(); + } + + public static ClaimToken createAccessToken(String id) { + return ClaimToken.Builder.newInstance() + .claim("jti", id) + .claim("iss", "did:web:bob") + .build(); + } + + public static String createJwt(String id) { + try { + var key = new ECKeyGenerator(Curve.P_256).generate(); + var jwt = new SignedJWT(new JWSHeader(JWSAlgorithm.ES256), new JWTClaimsSet.Builder().jwtID(id).build()); + jwt.sign(new ECDSASigner(key)); + return jwt.serialize(); + } catch (JOSEException e) { + throw new RuntimeException(e); + } + } +} diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/AuthTokenAudienceRuleTest.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/AuthTokenAudienceRuleTest.java new file mode 100644 index 000000000..bf4637fe7 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/AuthTokenAudienceRuleTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core.rules; + +import org.eclipse.edc.connector.dataplane.spi.AccessTokenData; +import org.eclipse.edc.connector.dataplane.spi.store.AccessTokenDataStore; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.TestFunctions.createAuthenticationToken; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.AUDIENCE_PROPERTY; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class AuthTokenAudienceRuleTest { + + private static final String TEST_TOKEN_ID = "token-id"; + private static final Object TEST_REFRESH_TOKEN = "refresh-token"; + private final AccessTokenDataStore store = mock(); + private final AuthTokenAudienceRule rule = new AuthTokenAudienceRule(store); + + @Test + void checkRule_issuerDoesNotMatchAudience() { + when(store.getById(TEST_TOKEN_ID)).thenReturn(new AccessTokenData(TEST_TOKEN_ID, + ClaimToken.Builder.newInstance().build(), + DataAddress.Builder.newInstance().type("test-type").build(), + Map.of(AUDIENCE_PROPERTY, "did:web:alice"))); + + assertThat(rule.checkRule(createAuthenticationToken(TEST_TOKEN_ID), Map.of())) + .isFailed() + .detail() + .isEqualTo("Principal 'did:web:bob' is not authorized to refresh this token."); + } + + @Test + void checkRule_audienceNotString() { + when(store.getById(TEST_TOKEN_ID)).thenReturn(new AccessTokenData(TEST_TOKEN_ID, + ClaimToken.Builder.newInstance().build(), + DataAddress.Builder.newInstance().type("test-type").build(), + Map.of(AUDIENCE_PROPERTY, 42L))); + + assertThat(rule.checkRule(createAuthenticationToken(TEST_TOKEN_ID), Map.of())) + .isFailed() + .detail() + .isEqualTo("Property '%s' was expected to be java.lang.String but was class java.lang.Long.".formatted(AUDIENCE_PROPERTY)); + } + + @Test + void checkRule_audienceNotPresent() { + when(store.getById(TEST_TOKEN_ID)).thenReturn(new AccessTokenData(TEST_TOKEN_ID, + ClaimToken.Builder.newInstance().build(), + DataAddress.Builder.newInstance().type("test-type").build(), + Map.of())); + + assertThat(rule.checkRule(createAuthenticationToken(TEST_TOKEN_ID), Map.of())) + .isFailed() + .detail() + .isEqualTo("Property '%s' was expected to be java.lang.String but was null.".formatted(AUDIENCE_PROPERTY)); + } +} \ No newline at end of file diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/ClaimIsPresentRuleTest.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/ClaimIsPresentRuleTest.java new file mode 100644 index 000000000..741938653 --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/ClaimIsPresentRuleTest.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core.rules; + +import org.eclipse.edc.spi.iam.ClaimToken; +import org.junit.jupiter.api.Test; + +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; + + +class ClaimIsPresentRuleTest { + @Test + void checkRule_whenPresent() { + var claimToken = ClaimToken.Builder.newInstance().claim("foo", "bar").build(); + assertThat(new ClaimIsPresentRule("foo").checkRule(claimToken, null)).isSucceeded(); + } + + @Test + void checkRule_whenNotPresent() { + var claimToken = ClaimToken.Builder.newInstance().build(); + assertThat(new ClaimIsPresentRule("foo").checkRule(claimToken, null)).isFailed() + .detail().isEqualTo("Required claim 'foo' not present on token."); + } + +} \ No newline at end of file diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/RefreshTokenValidationRuleTest.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/RefreshTokenValidationRuleTest.java new file mode 100644 index 000000000..2de8f641e --- /dev/null +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/rules/RefreshTokenValidationRuleTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.core.rules; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.spi.security.Vault; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.TestFunctions.createAccessToken; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class RefreshTokenValidationRuleTest { + + private static final String TEST_TOKEN_ID = "test-jti"; + private static final String TEST_REFRESH_TOKEN = "test-refresh-token"; + private final Vault vault = mock(); + private final RefreshTokenValidationRule rule = new RefreshTokenValidationRule(vault, TEST_REFRESH_TOKEN, new ObjectMapper()); + + @Test + void checkRule_noAccessTokenDataEntryFound() { + when(vault.resolveSecret(TEST_TOKEN_ID)).thenReturn(null); + + assertThat(rule.checkRule(createAccessToken(TEST_TOKEN_ID), Map.of())) + .isFailed() + .detail() + .isEqualTo("No refresh token with the ID '%s' was found in the vault.".formatted(TEST_TOKEN_ID)); + } + + @Test + void checkRule_noRefreshTokenStored() { + when(vault.resolveSecret(TEST_TOKEN_ID)).thenReturn(null); + + assertThat(rule.checkRule(createAccessToken(TEST_TOKEN_ID), Map.of())) + .isFailed() + .detail() + .isEqualTo("No refresh token with the ID 'test-jti' was found in the vault."); + } + + @Test + void checkRule_refreshTokenNotString() { + when(vault.resolveSecret(TEST_TOKEN_ID)).thenReturn( + """ + { + "refreshToken": 42 + } + """); + + assertThat(rule.checkRule(createAccessToken(TEST_TOKEN_ID), Map.of())) + .isFailed() + .detail() + .isEqualTo("Provided refresh token does not match the stored refresh token."); + } + + @Test + void checkRule_refreshTokenDoesNotMatch() { + when(vault.resolveSecret(TEST_TOKEN_ID)).thenReturn( + """ + { + "refreshToken": "someRefreshToken" + } + """); + + assertThat(rule.checkRule(createAccessToken(TEST_TOKEN_ID), Map.of())) + .isFailed() + .detail() + .isEqualTo("Provided refresh token does not match the stored refresh token."); + } + + @Test + void checkRule_success() { + when(vault.resolveSecret(TEST_TOKEN_ID)).thenReturn( + """ + { + "refreshToken": "%s" + } + """.formatted(TEST_REFRESH_TOKEN)); + + assertThat(rule.checkRule(createAccessToken(TEST_TOKEN_ID), Map.of())) + .isSucceeded(); + } + + @Test + void checkRule_invalidJson() { + when(vault.resolveSecret(TEST_TOKEN_ID)).thenReturn( + "nope-thats-not-json"); + + assertThat(rule.checkRule(createAccessToken(TEST_TOKEN_ID), Map.of())) + .isFailed() + .detail() + .startsWith("Failed to parse stored secret"); + } + +} \ No newline at end of file diff --git a/edc-extensions/edr/edr-api/build.gradle.kts b/edc-extensions/edr/edr-api-v2/build.gradle.kts similarity index 83% rename from edc-extensions/edr/edr-api/build.gradle.kts rename to edc-extensions/edr/edr-api-v2/build.gradle.kts index bd0841441..4db499b1c 100644 --- a/edc-extensions/edr/edr-api/build.gradle.kts +++ b/edc-extensions/edr/edr-api-v2/build.gradle.kts @@ -27,14 +27,17 @@ dependencies { implementation(project(":spi:callback-spi")) implementation(project(":spi:edr-spi")) implementation(project(":spi:core-spi")) + implementation(project(":spi:tokenrefresh-spi")) implementation(libs.edc.api.management) - implementation(libs.edc.core.validator) + implementation(libs.edc.lib.validator) + implementation(libs.edc.spi.edrstore) implementation(libs.jakarta.rsApi) testImplementation(testFixtures(libs.edc.core.jersey)) testImplementation(libs.restAssured) testImplementation(libs.edc.junit) - testImplementation(libs.edc.ext.jersey.providers) - testImplementation(libs.edc.core.transform) + testImplementation(libs.edc.lib.jersey.providers) + testImplementation(libs.edc.lib.transform) + testImplementation(libs.edc.spi.contract) } diff --git a/edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApi.java b/edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApi.java new file mode 100644 index 000000000..29dd28843 --- /dev/null +++ b/edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApi.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.api.edr.v2; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import org.eclipse.edc.api.model.ApiCoreSchema; +import org.eclipse.edc.connector.api.management.configuration.ManagementApiSchema; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.ContractNegotiationApi; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; +import org.eclipse.edc.web.spi.ApiErrorDetail; + +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; + +@OpenAPIDefinition +@Tag(name = "Control Plane EDR Api") +public interface EdrCacheApi { + + @Operation(description = "Initiates an EDR negotiation by handling a contract negotiation first and then a transfer process for a given offer and with the given counter part. Please note that successfully invoking this endpoint " + + "only means that the negotiation was initiated.", + responses = { + @ApiResponse(responseCode = "200", description = "The negotiation was successfully initiated.", + content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class))), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + }) + JsonObject initiateEdrNegotiation(@Schema(implementation = ContractNegotiationApi.ContractRequestSchema.class) JsonObject dto); + + @Operation(description = "Request all Edr entries according to a particular query", + requestBody = @RequestBody( + content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class)) + ), + responses = { + @ApiResponse(responseCode = "200", description = "The edr entries matching the query", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = EndpointDataReferenceEntrySchema.class)))), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }) + JsonArray requestEdrEntries(JsonObject querySpecJson); + + @Operation(description = "Gets the EDR data address with the given transfer process ID", + parameters = { @Parameter(name = "transferProcessId", description = "The ID of the transferprocess for which the EDR should be fetched", required = true), + @Parameter(name = "auto_refresh", description = "Whether the access token that is stored on the EDR should be checked for expiry, and renewed if necessary. Default is true") + }, + responses = { + @ApiResponse(responseCode = "200", description = "The data address", + content = @Content(schema = @Schema(implementation = ManagementApiSchema.DataAddressSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An EDR data address with the given transfer process ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + } + ) + JsonObject getEdrEntryDataAddress(String transferProcessId, boolean autoRefresh); + + @Operation(description = "Removes an EDR entry given the transfer process ID", + responses = { + @ApiResponse(responseCode = "204", description = "EDR entry was deleted successfully"), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An EDR entry with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }) + void removeEdrEntry(String transferProcessId); + + @Operation(description = "Refreshes and returns the EDR data address with the given transfer process ID", + parameters = { @Parameter(name = "transferProcessId", description = "The ID of the transferprocess for which the EDR should be fetched", required = true), + }, + responses = { + @ApiResponse(responseCode = "200", description = "The data address", + content = @Content(schema = @Schema(implementation = ManagementApiSchema.DataAddressSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An EDR data address with the given transfer process ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + } + ) + JsonObject refreshEdr(String transferProcessId); + + + @ArraySchema() + @Schema(name = "EndpointDataReferenceEntry", example = EndpointDataReferenceEntrySchema.EDR_ENTRY_OUTPUT_EXAMPLE) + record EndpointDataReferenceEntrySchema( + @Schema(name = ID) + String id, + @Schema(name = TYPE, example = EndpointDataReferenceEntry.EDR_ENTRY_TYPE) + String type + ) { + public static final String EDR_ENTRY_OUTPUT_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@id": "transfer-process-id", + "transferProcessId": "transfer-process-id", + "agreementId": "agreement-id", + "contractNegotiationId": "contract-negotiation-id", + "assetId": "asset-id", + "providerId": "provider-id", + "createdAt": 1688465655 + } + """; + } +} diff --git a/edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiController.java b/edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiController.java new file mode 100644 index 000000000..ea201d0af --- /dev/null +++ b/edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiController.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.api.edr.v2; + +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.edc.api.model.IdResponse; +import org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractRequest; +import org.eclipse.edc.connector.controlplane.services.spi.contractnegotiation.ContractNegotiationService; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.result.ServiceResult; +import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.spi.exception.InvalidRequestException; +import org.eclipse.edc.web.spi.exception.ValidationFailureException; +import org.eclipse.tractusx.edc.edr.spi.service.EdrService; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static jakarta.json.stream.JsonCollectors.toJsonArray; +import static org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractRequest.CONTRACT_REQUEST_TYPE; +import static org.eclipse.edc.spi.query.QuerySpec.EDC_QUERY_SPEC_TYPE; +import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.exceptionMapper; +import static org.eclipse.tractusx.edc.edr.spi.types.RefreshMode.AUTO_REFRESH; +import static org.eclipse.tractusx.edc.edr.spi.types.RefreshMode.FORCE_REFRESH; +import static org.eclipse.tractusx.edc.edr.spi.types.RefreshMode.NO_REFRESH; + +@Consumes({ MediaType.APPLICATION_JSON }) +@Produces({ MediaType.APPLICATION_JSON }) +@Path("/v2/edrs") +public class EdrCacheApiController implements EdrCacheApi { + public static final String LOCAL_ADAPTER_URI = "local://adapter"; + public static final Set LOCAL_EVENTS = Set.of("contract.negotiation", "transfer.process"); + public static final CallbackAddress LOCAL_CALLBACK = CallbackAddress.Builder.newInstance() + .transactional(true) + .uri(LOCAL_ADAPTER_URI) + .events(LOCAL_EVENTS) + .build(); + private final EndpointDataReferenceStore edrStore; + private final TypeTransformerRegistry transformerRegistry; + private final JsonObjectValidatorRegistry validator; + private final Monitor monitor; + private final EdrService edrService; + + private final ContractNegotiationService contractNegotiationService; + + public EdrCacheApiController(EndpointDataReferenceStore edrStore, + TypeTransformerRegistry transformerRegistry, + JsonObjectValidatorRegistry validator, + Monitor monitor, + EdrService edrService, ContractNegotiationService contractNegotiationService) { + this.edrStore = edrStore; + this.transformerRegistry = transformerRegistry; + this.validator = validator; + this.monitor = monitor; + this.edrService = edrService; + this.contractNegotiationService = contractNegotiationService; + } + + @POST + @Override + public JsonObject initiateEdrNegotiation(JsonObject requestObject) { + + validator.validate(CONTRACT_REQUEST_TYPE, requestObject) + .orElseThrow(ValidationFailureException::new); + + var contractRequest = transformerRegistry.transform(requestObject, ContractRequest.class) + .orElseThrow(InvalidRequestException::new); + + var contractNegotiation = contractNegotiationService.initiateNegotiation(enrichContractRequest(contractRequest)); + + var idResponse = IdResponse.Builder.newInstance() + .id(contractNegotiation.getId()) + .createdAt(contractNegotiation.getCreatedAt()) + .build(); + + return transformerRegistry.transform(idResponse, JsonObject.class) + .orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail())); + } + + @POST + @Path("/request") + @Override + public JsonArray requestEdrEntries(JsonObject querySpecJson) { + QuerySpec querySpec; + if (querySpecJson == null) { + querySpec = QuerySpec.Builder.newInstance().build(); + } else { + validator.validate(EDC_QUERY_SPEC_TYPE, querySpecJson).orElseThrow(ValidationFailureException::new); + + querySpec = transformerRegistry.transform(querySpecJson, QuerySpec.class) + .orElseThrow(InvalidRequestException::new); + } + + return edrStore.query(querySpec) + .flatMap(ServiceResult::from) + .orElseThrow(exceptionMapper(QuerySpec.class, null)).stream() + .map(it -> transformerRegistry.transform(it, JsonObject.class)) + .peek(r -> r.onFailure(f -> monitor.warning(f.getFailureDetail()))) + .filter(Result::succeeded) + .map(Result::getContent) + .collect(toJsonArray()); + } + + @GET + @Path("{transferProcessId}/dataaddress") + @Override + public JsonObject getEdrEntryDataAddress(@PathParam("transferProcessId") String transferProcessId, @QueryParam("auto_refresh") boolean autoRefresh) { + var mode = autoRefresh ? AUTO_REFRESH : NO_REFRESH; + var dataAddress = edrService.resolveByTransferProcess(transferProcessId, mode) + .orElseThrow(exceptionMapper(EndpointDataReferenceEntry.class, transferProcessId)); + + return transformerRegistry.transform(dataAddress, JsonObject.class) + .orElseThrow(f -> new EdcException(f.getFailureDetail())); + + + } + + @DELETE + @Path("{transferProcessId}") + @Override + public void removeEdrEntry(@PathParam("transferProcessId") String transferProcessId) { + edrStore.delete(transferProcessId) + .flatMap(ServiceResult::from) + .orElseThrow(exceptionMapper(EndpointDataReferenceEntry.class, transferProcessId)); + } + + @POST + @Path("{transferProcessId}/refresh") + @Override + public JsonObject refreshEdr(@PathParam("transferProcessId") String transferProcessId) { + var updatedEdr = edrService.resolveByTransferProcess(transferProcessId, FORCE_REFRESH) + .orElseThrow(exceptionMapper(EndpointDataReferenceEntry.class, transferProcessId)); + + return transformerRegistry.transform(updatedEdr, JsonObject.class) + .orElseThrow(f -> new EdcException(f.getFailureDetail())); + } + + private ContractRequest enrichContractRequest(ContractRequest request) { + var callbacks = Stream.concat(request.getCallbackAddresses().stream(), Stream.of(LOCAL_CALLBACK)).collect(Collectors.toList()); + + return ContractRequest.Builder.newInstance() + .counterPartyAddress(request.getCounterPartyAddress()) + .contractOffer(request.getContractOffer()) + .protocol(request.getProtocol()) + .callbackAddresses(callbacks).build(); + } + +} diff --git a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtension.java b/edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiExtension.java similarity index 53% rename from edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtension.java rename to edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiExtension.java index 0e42b6ee4..585c0fcc7 100644 --- a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtension.java +++ b/edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiExtension.java @@ -1,5 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -15,31 +15,31 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + */ -package org.eclipse.tractusx.edc.api.edr; +package org.eclipse.tractusx.edc.api.edr.v2; +import jakarta.json.Json; import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration; -import org.eclipse.edc.connector.api.management.configuration.transform.ManagementApiTypeTransformerRegistry; +import org.eclipse.edc.connector.controlplane.services.spi.contractnegotiation.ContractNegotiationService; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; import org.eclipse.edc.web.spi.WebService; -import org.eclipse.tractusx.edc.api.edr.transform.EndpointDataReferenceToDataAddressTransformer; -import org.eclipse.tractusx.edc.api.edr.transform.JsonObjectFromEndpointDataReferenceEntryTransformer; -import org.eclipse.tractusx.edc.api.edr.transform.JsonObjectToNegotiateEdrRequestDtoTransformer; -import org.eclipse.tractusx.edc.api.edr.transform.NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer; -import org.eclipse.tractusx.edc.api.edr.validation.NegotiateEdrRequestDtoValidator; +import org.eclipse.tractusx.edc.api.edr.v2.transform.JsonObjectFromEndpointDataReferenceEntryTransformer; import org.eclipse.tractusx.edc.edr.spi.service.EdrService; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE; +import java.util.Map; + import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_PREFIX; -public class EdrApiExtension implements ServiceExtension { +public class EdrCacheApiExtension implements ServiceExtension { @Inject private WebService webService; @@ -50,7 +50,7 @@ public class EdrApiExtension implements ServiceExtension { private EdrService edrService; @Inject - private ManagementApiTypeTransformerRegistry transformerRegistry; + private TypeTransformerRegistry transformerRegistry; @Inject private JsonLd jsonLdService; @@ -58,17 +58,19 @@ public class EdrApiExtension implements ServiceExtension { @Inject private JsonObjectValidatorRegistry validatorRegistry; + @Inject + private ContractNegotiationService contractNegotiationService; + @Inject private Monitor monitor; + @Inject + private EndpointDataReferenceStore edrStore; @Override public void initialize(ServiceExtensionContext context) { jsonLdService.registerNamespace(TX_PREFIX, TX_NAMESPACE); - transformerRegistry.register(new NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer()); - transformerRegistry.register(new JsonObjectToNegotiateEdrRequestDtoTransformer()); - transformerRegistry.register(new JsonObjectFromEndpointDataReferenceEntryTransformer()); - transformerRegistry.register(new EndpointDataReferenceToDataAddressTransformer()); - validatorRegistry.register(EDR_REQUEST_DTO_TYPE, NegotiateEdrRequestDtoValidator.instance()); - webService.registerResource(apiConfig.getContextAlias(), new EdrController(edrService, transformerRegistry, validatorRegistry, monitor)); + var mgmtApiTransformerRegistry = transformerRegistry.forContext("management-api"); + mgmtApiTransformerRegistry.register(new JsonObjectFromEndpointDataReferenceEntryTransformer(Json.createBuilderFactory(Map.of()))); + webService.registerResource(apiConfig.getContextAlias(), new EdrCacheApiController(edrStore, mgmtApiTransformerRegistry, validatorRegistry, monitor, edrService, contractNegotiationService)); } } diff --git a/edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java b/edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java new file mode 100644 index 000000000..64ef0a33b --- /dev/null +++ b/edc-extensions/edr/edr-api-v2/src/main/java/org/eclipse/tractusx/edc/api/edr/v2/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.api.edr.v2.transform; + +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonObject; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; +import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer; +import org.eclipse.edc.transform.spi.TransformerContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CONTRACT_NEGOTIATION_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CREATED_AT; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_PROVIDER_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TYPE; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; + +public class JsonObjectFromEndpointDataReferenceEntryTransformer extends AbstractJsonLdTransformer { + private final JsonBuilderFactory jsonFactory; + + + public JsonObjectFromEndpointDataReferenceEntryTransformer(JsonBuilderFactory jsonFactory) { + super(EndpointDataReferenceEntry.class, JsonObject.class); + this.jsonFactory = jsonFactory; + } + + @Override + public @Nullable JsonObject transform(@NotNull EndpointDataReferenceEntry entry, @NotNull TransformerContext context) { + return jsonFactory.createObjectBuilder() + .add(ID, entry.getId()) + .add(TYPE, EDR_ENTRY_TYPE) + .add(EDR_ENTRY_PROVIDER_ID, entry.getProviderId()) + .add(EDR_ENTRY_ASSET_ID, entry.getAssetId()) + .add(EDR_ENTRY_AGREEMENT_ID, entry.getAgreementId()) + .add(EDR_ENTRY_TRANSFER_PROCESS_ID, entry.getTransferProcessId()) + .add(EDR_ENTRY_CREATED_AT, entry.getCreatedAt()) + .add(EDR_ENTRY_CONTRACT_NEGOTIATION_ID, entry.getContractNegotiationId()) + .build(); + } +} diff --git a/edc-extensions/edr/edr-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/edr/edr-api-v2/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 94% rename from edc-extensions/edr/edr-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/edr/edr-api-v2/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 3443e55a6..d415057f4 100644 --- a/edc-extensions/edr/edr-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/edr/edr-api-v2/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -17,4 +17,4 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# -org.eclipse.tractusx.edc.api.edr.EdrApiExtension +org.eclipse.tractusx.edc.api.edr.v2.EdrCacheApiExtension diff --git a/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiControllerTest.java b/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiControllerTest.java new file mode 100644 index 000000000..ae7017dfa --- /dev/null +++ b/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiControllerTest.java @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.api.edr.v2; + +import io.restassured.specification.RequestSpecification; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import org.eclipse.edc.api.model.IdResponse; +import org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractRequest; +import org.eclipse.edc.connector.controlplane.contract.spi.types.offer.ContractOffer; +import org.eclipse.edc.connector.controlplane.services.spi.contractnegotiation.ContractNegotiationService; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; +import org.eclipse.edc.junit.annotations.ApiTest; +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.result.ServiceResult; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.validator.spi.ValidationResult; +import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; +import org.eclipse.tractusx.edc.edr.spi.service.EdrService; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static io.restassured.RestAssured.given; +import static io.restassured.http.ContentType.JSON; +import static jakarta.json.Json.createObjectBuilder; +import static java.util.UUID.randomUUID; +import static org.eclipse.edc.api.model.IdResponse.ID_RESPONSE_TYPE; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CONTRACT_NEGOTIATION_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_PROVIDER_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TYPE; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VOCAB; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_PREFIX; +import static org.eclipse.tractusx.edc.api.edr.v2.TestFunctions.createContractNegotiation; +import static org.eclipse.tractusx.edc.api.edr.v2.TestFunctions.negotiationRequest; +import static org.eclipse.tractusx.edc.edr.spi.types.RefreshMode.AUTO_REFRESH; +import static org.eclipse.tractusx.edc.edr.spi.types.RefreshMode.NO_REFRESH; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@ApiTest +public class EdrCacheApiControllerTest extends RestControllerTestBase { + + private static final String TEST_TRANSFER_PROCESS_ID = "test-transfer-process-id"; + private static final String TEST_TRANSFER_NEGOTIATION_ID = "test-cn-id"; + private static final String TEST_AGREEMENT_ID = "test-agreement-id"; + private static final String TEST_PROVIDER_ID = "test-provider-id"; + private static final String TEST_ASSET_ID = "test-asset-id"; + + private final TypeTransformerRegistry transformerRegistry = mock(); + private final JsonObjectValidatorRegistry validator = mock(); + private final EndpointDataReferenceStore edrStore = mock(); + private final EdrService edrService = mock(); + private final ContractNegotiationService contractNegotiationService = mock(); + + + @Test + void initEdrNegotiation_shouldWork_whenValidRequest() { + when(validator.validate(any(), any())).thenReturn(ValidationResult.success()); + + var contractNegotiation = createContractNegotiation(); + var responseBody = Json.createObjectBuilder().add(TYPE, ID_RESPONSE_TYPE).add(ID, contractNegotiation.getId()).build(); + + when(transformerRegistry.transform(any(JsonObject.class), eq(ContractRequest.class))).thenReturn(Result.success(createContractRequest())); + when(contractNegotiationService.initiateNegotiation(any())).thenReturn(contractNegotiation); + when(transformerRegistry.transform(any(IdResponse.class), eq(JsonObject.class))).thenReturn(Result.success(responseBody)); + + var request = negotiationRequest(); + + baseRequest() + .contentType(JSON) + .body(request) + .post("/v2/edrs") + .then() + .statusCode(200) + .body(ID, is(contractNegotiation.getId())); + + } + + @Test + void initEdrNegotiation_shouldReturnBadRequest_whenValidInvalidRequest() { + when(validator.validate(any(), any())).thenReturn(ValidationResult.success()); + + when(transformerRegistry.transform(any(JsonObject.class), eq(ContractRequest.class))).thenReturn(Result.failure("fail")); + + baseRequest() + .contentType(JSON) + .body(Json.createObjectBuilder().build()) + .post("/v2/edrs") + .then() + .statusCode(400); + + } + + @Test + void requestEdrEntries() { + when(edrStore.query(any())) + .thenReturn(StoreResult.success(List.of(createEdrEntry()))); + when(transformerRegistry.transform(isA(EndpointDataReferenceEntry.class), eq(JsonObject.class))) + .thenReturn(Result.success(createEdrEntryJson().build())); + when(transformerRegistry.transform(isA(JsonObject.class), eq(QuerySpec.class))) + .thenReturn(Result.success(QuerySpec.Builder.newInstance().offset(10).build())); + when(validator.validate(any(), any())).thenReturn(ValidationResult.success()); + + baseRequest() + .contentType(JSON) + .body("{}") + .post("/v2/edrs/request") + .then() + .log().ifError() + .statusCode(200) + .contentType(JSON) + .body("size()", is(1)); + + verify(edrStore).query(argThat(s -> s.getOffset() == 10)); + verify(transformerRegistry).transform(isA(EndpointDataReferenceEntry.class), eq(JsonObject.class)); + verify(transformerRegistry).transform(isA(JsonObject.class), eq(QuerySpec.class)); + } + + @Test + void getEdrEntryDataAddress() { + + var dataAddressType = "type"; + var dataAddress = DataAddress.Builder.newInstance().type(dataAddressType).build(); + when(edrService.resolveByTransferProcess("transferProcessId", NO_REFRESH)) + .thenReturn(ServiceResult.success(dataAddress)); + + when(transformerRegistry.transform(isA(DataAddress.class), eq(JsonObject.class))) + .thenReturn(Result.success(createDataAddress(dataAddressType).build())); + + baseRequest() + .contentType(JSON) + .get("/v2/edrs/transferProcessId/dataaddress") + .then() + .log().ifError() + .statusCode(200) + .contentType(JSON) + .body("'%s'".formatted(DataAddress.EDC_DATA_ADDRESS_TYPE_PROPERTY), equalTo(dataAddressType)); + + verify(edrService).resolveByTransferProcess("transferProcessId", NO_REFRESH); + verify(transformerRegistry).transform(isA(DataAddress.class), eq(JsonObject.class)); + verifyNoMoreInteractions(transformerRegistry); + } + + @Test + void getEdrEntryDataAddress_withAutorefresh() { + + var dataAddressType = "type"; + var dataAddress = DataAddress.Builder.newInstance().type(dataAddressType).build(); + when(edrService.resolveByTransferProcess("transferProcessId", AUTO_REFRESH)) + .thenReturn(ServiceResult.success(dataAddress)); + + when(transformerRegistry.transform(isA(DataAddress.class), eq(JsonObject.class))) + .thenReturn(Result.success(createDataAddress(dataAddressType).build())); + + baseRequest() + .contentType(JSON) + .get("/v2/edrs/transferProcessId/dataaddress?auto_refresh=true") + .then() + .log().ifError() + .statusCode(200) + .contentType(JSON) + .body("'%s'".formatted(DataAddress.EDC_DATA_ADDRESS_TYPE_PROPERTY), equalTo(dataAddressType)); + + verify(edrService).resolveByTransferProcess("transferProcessId", AUTO_REFRESH); + verify(transformerRegistry).transform(isA(DataAddress.class), eq(JsonObject.class)); + verifyNoMoreInteractions(transformerRegistry); + } + + + @Test + void getEdrEntryDataAddress_whenNotFound() { + + when(edrService.resolveByTransferProcess("transferProcessId", NO_REFRESH)) + .thenReturn(ServiceResult.notFound("notFound")); + + + baseRequest() + .contentType(JSON) + .get("/v2/edrs/transferProcessId/dataaddress") + .then() + .log().ifError() + .statusCode(404) + .contentType(JSON); + + verify(edrService).resolveByTransferProcess("transferProcessId", NO_REFRESH); + verifyNoMoreInteractions(transformerRegistry); + } + + @Test + void removeEdrEntry() { + when(edrStore.delete("transferProcessId")) + .thenReturn(StoreResult.success(createEdrEntry())); + + baseRequest() + .contentType(JSON) + .delete("/v2/edrs/transferProcessId") + .then() + .statusCode(204); + verify(edrStore).delete("transferProcessId"); + } + + @Test + void removeEdrEntry_whenNotFound() { + when(edrStore.delete("transferProcessId")) + .thenReturn(StoreResult.notFound("not found")); + + baseRequest() + .contentType(JSON) + .delete("/v2/edrs/transferProcessId") + .then() + .statusCode(404); + + verify(edrStore).delete("transferProcessId"); + } + + @Override + protected Object controller() { + return new EdrCacheApiController(edrStore, transformerRegistry, validator, mock(), edrService, contractNegotiationService); + } + + private JsonObjectBuilder createEdrEntryJson() { + return createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, EDR_ENTRY_TYPE) + .add(ID, TEST_TRANSFER_PROCESS_ID) + .add(EDR_ENTRY_TRANSFER_PROCESS_ID, TEST_TRANSFER_PROCESS_ID) + .add(EDR_ENTRY_PROVIDER_ID, TEST_PROVIDER_ID) + .add(EDR_ENTRY_CONTRACT_NEGOTIATION_ID, TEST_TRANSFER_NEGOTIATION_ID) + .add(EDR_ENTRY_ASSET_ID, TEST_ASSET_ID) + .add(EDR_ENTRY_AGREEMENT_ID, TEST_AGREEMENT_ID); + } + + private JsonObjectBuilder createDataAddress(String type) { + return createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, DataAddress.EDC_DATA_ADDRESS_TYPE) + .add(DataAddress.EDC_DATA_ADDRESS_TYPE_PROPERTY, type); + } + + private EndpointDataReferenceEntry createEdrEntry() { + return EndpointDataReferenceEntry.Builder.newInstance() + .agreementId(TEST_AGREEMENT_ID) + .assetId(TEST_ASSET_ID) + .providerId(TEST_PROVIDER_ID) + .transferProcessId(TEST_TRANSFER_PROCESS_ID) + .contractNegotiationId(TEST_TRANSFER_NEGOTIATION_ID) + .build(); + + } + + private JsonObjectBuilder createContextBuilder() { + return createObjectBuilder() + .add(VOCAB, EDC_NAMESPACE) + .add(EDC_PREFIX, EDC_NAMESPACE); + } + + private ContractRequest createContractRequest() { + return ContractRequest.Builder.newInstance() + .protocol("test-protocol") + .counterPartyAddress("test-cb") + .contractOffer(ContractOffer.Builder.newInstance() + .id("test-offer-id") + .assetId(randomUUID().toString()) + .policy(Policy.Builder.newInstance().build()) + .build()) + .build(); + } + + private RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port) + .when(); + } +} diff --git a/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiExtensionTest.java b/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiExtensionTest.java new file mode 100644 index 000000000..1fd484120 --- /dev/null +++ b/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiExtensionTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.api.edr.v2; + +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.spi.WebService; +import org.eclipse.tractusx.edc.api.edr.v2.transform.JsonObjectFromEndpointDataReferenceEntryTransformer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(DependencyInjectionExtension.class) +class EdrCacheApiExtensionTest { + + private final JsonObjectValidatorRegistry validatorRegistry = mock(); + private final WebService webService = mock(); + + private final TypeTransformerRegistry transformerRegistry = mock(); + + + @BeforeEach + void setUp(ServiceExtensionContext context) { + context.registerService(JsonObjectValidatorRegistry.class, validatorRegistry); + context.registerService(WebService.class, webService); + context.registerService(TypeTransformerRegistry.class, transformerRegistry); + when(transformerRegistry.forContext("management-api")).thenReturn(transformerRegistry); + } + + @Test + void initialize_shouldRegisterControllers(EdrCacheApiExtension extension, ServiceExtensionContext context) { + extension.initialize(context); + + verify(webService).registerResource(any(), isA(EdrCacheApiController.class)); + } + + @Test + void initialize_shouldRegisterTransformers(EdrCacheApiExtension extension, ServiceExtensionContext context) { + extension.initialize(context); + verify(transformerRegistry).register(isA(JsonObjectFromEndpointDataReferenceEntryTransformer.class)); + } +} diff --git a/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiTest.java b/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiTest.java new file mode 100644 index 000000000..62b1b3c1e --- /dev/null +++ b/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/EdrCacheApiTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.api.edr.v2; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.json.JsonObject; +import org.eclipse.edc.jsonld.TitaniumJsonLd; +import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.jsonld.util.JacksonJsonLd; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CONTRACT_NEGOTIATION_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CREATED_AT; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_PROVIDER_ID; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.tractusx.edc.api.edr.v2.EdrCacheApi.EndpointDataReferenceEntrySchema.EDR_ENTRY_OUTPUT_EXAMPLE; +import static org.mockito.Mockito.mock; + +public class EdrCacheApiTest { + + private final ObjectMapper objectMapper = JacksonJsonLd.createObjectMapper(); + private final JsonLd jsonLd = new TitaniumJsonLd(mock()); + + @Test + void edrEntryOutputExample() throws JsonProcessingException { + var jsonObject = objectMapper.readValue(EDR_ENTRY_OUTPUT_EXAMPLE, JsonObject.class); + var expanded = jsonLd.expand(jsonObject); + + assertThat(expanded).isSucceeded().satisfies(content -> { + assertThat(content.getString(ID)).isNotBlank(); + assertThat(content.getJsonArray(EDR_ENTRY_AGREEMENT_ID).getJsonObject(0).getString(VALUE)).isNotBlank(); + assertThat(content.getJsonArray(EDR_ENTRY_CONTRACT_NEGOTIATION_ID).getJsonObject(0).getString(VALUE)).isNotBlank(); + assertThat(content.getJsonArray(EDR_ENTRY_PROVIDER_ID).getJsonObject(0).getString(VALUE)).isNotBlank(); + assertThat(content.getJsonArray(EDR_ENTRY_ASSET_ID).getJsonObject(0).getString(VALUE)).isNotBlank(); + assertThat(content.getJsonArray(EDR_ENTRY_CREATED_AT).getJsonObject(0).getJsonNumber(VALUE)).isNotNull(); + }); + } +} diff --git a/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/TestFunctions.java b/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/TestFunctions.java new file mode 100644 index 000000000..45ac84014 --- /dev/null +++ b/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/TestFunctions.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.api.edr.v2; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractNegotiation; +import org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractRequest; + +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; + +public class TestFunctions { + + public static JsonObject negotiationRequest() { + return Json.createObjectBuilder() + .add(TYPE, ContractRequest.CONTRACT_REQUEST_TYPE) + .add(EDC_NAMESPACE + "connectorId", "test") + .add(EDC_NAMESPACE + "providerId", "test") + .add(EDC_NAMESPACE + "connectorAddress", "test") + .add(EDC_NAMESPACE + "protocol", "dataspace-protocol-http") + .add(EDC_NAMESPACE + "offer", Json.createObjectBuilder() + .add(EDC_NAMESPACE + "offerId", "offerId") + .add(EDC_NAMESPACE + "assetId", "assetId") + .add(EDC_NAMESPACE + "policy", Json.createObjectBuilder().build()) + ) + .build(); + } + + public static ContractNegotiation createContractNegotiation() { + return ContractNegotiation.Builder.newInstance() + .id("id") + .counterPartyAddress("http://test") + .counterPartyId("provider") + .protocol("protocol") + .build(); + } +} diff --git a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java b/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java similarity index 59% rename from edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java rename to edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java index a0474eb60..80aceda85 100644 --- a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java +++ b/edc-extensions/edr/edr-api-v2/src/test/java/org/eclipse/tractusx/edc/api/edr/v2/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java @@ -1,5 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -15,26 +15,24 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + */ -package org.eclipse.tractusx.edc.api.edr.transform; +package org.eclipse.tractusx.edc.api.edr.v2.transform; +import jakarta.json.Json; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; import org.eclipse.edc.transform.spi.TransformerContext; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.time.Instant; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CONTRACT_NEGOTIATION_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_EXPIRATION_DATE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_PROVIDER_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_STATE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CONTRACT_NEGOTIATION_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_PROVIDER_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; import static org.mockito.Mockito.mock; class JsonObjectFromEndpointDataReferenceEntryTransformerTest { @@ -44,7 +42,7 @@ class JsonObjectFromEndpointDataReferenceEntryTransformerTest { @BeforeEach void setUp() { - transformer = new JsonObjectFromEndpointDataReferenceEntryTransformer(); + transformer = new JsonObjectFromEndpointDataReferenceEntryTransformer(Json.createBuilderFactory(Map.of())); } @Test @@ -56,8 +54,6 @@ void transform() { .agreementId("aId") .providerId("providerId") .contractNegotiationId("contractNegotiationId") - .state(EndpointDataReferenceEntryStates.NEGOTIATED.code()) - .expirationTimestamp(Instant.now().toEpochMilli()) .build(); var jsonObject = transformer.transform(dto, context); @@ -68,8 +64,6 @@ void transform() { assertThat(jsonObject.getJsonString(EDR_ENTRY_ASSET_ID).getString()).isNotNull().isEqualTo(dto.getAssetId()); assertThat(jsonObject.getJsonString(EDR_ENTRY_TRANSFER_PROCESS_ID).getString()).isNotNull().isEqualTo(dto.getTransferProcessId()); assertThat(jsonObject.getJsonString(EDR_ENTRY_PROVIDER_ID).getString()).isNotNull().isEqualTo(dto.getProviderId()); - assertThat(jsonObject.getJsonString(EDR_ENTRY_STATE).getString()).isNotNull().isEqualTo(dto.getEdrState()); - assertThat(jsonObject.getJsonNumber(EDR_ENTRY_EXPIRATION_DATE).longValue()).isNotNull().isEqualTo(dto.getExpirationTimestamp()); } } diff --git a/edc-extensions/edr/edr-api/README.md b/edc-extensions/edr/edr-api/README.md deleted file mode 100644 index 084715dcc..000000000 --- a/edc-extensions/edr/edr-api/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Control Plane EDR API - -This module provides extensions to the EDC management API for dealing with EDR tokens. - -The extensions are added to the same context as the management APIs, so no additional configuration is required. - -The base path of the API will be `/edrs` - -This module for now provides three APIs: - -- Initiating an EDR negotiation token -- Fetching the available EDRs -- Fetching the single EDR - -The initiate negotiation EDR leverage the callbacks mechanism introduced in the latest EDC, and it handles -the contract negotiation and the transfer request in one API call. Once the transfer has been completed -the provider will return the EDR that will be stored into the consumer EDR store/cache. Users can interact -with the EDR store/cache for fetching the EDR and then requesting the data, or can use the `proxy` API described [here](../../dataplane-proxy/edc-dataplane-proxy-consumer-api/README.md) - -An overview on how to use the EDR APIs is available [here](../../../docs/samples/edr-api-overview/edr-api-overview.md) diff --git a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApi.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApi.java deleted file mode 100644 index 3042ab67d..000000000 --- a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApi.java +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr; - -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.ArraySchema; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import org.eclipse.edc.api.model.ApiCoreSchema; -import org.eclipse.edc.connector.api.management.configuration.ManagementApiSchema; -import org.eclipse.edc.web.spi.ApiErrorDetail; -import org.eclipse.tractusx.edc.api.edr.schema.EdrSchema; - -@OpenAPIDefinition -@Tag(name = "Control Plane EDR Api") -public interface EdrApi { - - @Operation(description = "Initiates an EDR negotiation by handling a contract negotiation first and then a transfer process for a given offer and with the given counter part. Please note that successfully invoking this endpoint " + - "only means that the negotiation was initiated.", - responses = { - @ApiResponse(responseCode = "200", description = "The negotiation was successfully initiated.", - content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class))), - @ApiResponse(responseCode = "400", description = "Request body was malformed", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), - }) - JsonObject initiateEdrNegotiation(@Schema(implementation = EdrSchema.NegotiateEdrRequestSchema.class) JsonObject dto); - - @Operation(description = "Returns all EndpointDataReference entry according to a query", - responses = { - @ApiResponse(responseCode = "200", description = "The ERD cache was retrieved", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = EdrSchema.EndpointDataReferenceEntrySchema.class)))), - @ApiResponse(responseCode = "400", description = "Request was malformed", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) } - ) - JsonArray queryEdrs(String assetId, String agreementId, String contractNegotiationId, String providerId); - - @Operation(description = "Gets an EDR with the given transfer process ID", - responses = { - @ApiResponse(responseCode = "200", description = "The EDR cached", - content = @Content(schema = @Schema(implementation = ManagementApiSchema.DataAddressSchema.class))), - @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), - @ApiResponse(responseCode = "404", description = "An EDR with the given ID does not exist", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) - } - ) - JsonObject getEdr(String transferProcessId); - - @Operation(description = "Delete an EDR with the given transfer process ID", - responses = { - @ApiResponse(responseCode = "200", description = "The EDR cached was deleted successfully"), - @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), - @ApiResponse(responseCode = "404", description = "An EDR with the given ID does not exist", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) - } - ) - void deleteEdr(String transferProcessId); -} diff --git a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrController.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrController.java deleted file mode 100644 index 8b2fecf37..000000000 --- a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrController.java +++ /dev/null @@ -1,161 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr; - -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.MediaType; -import org.eclipse.edc.api.model.IdResponse; -import org.eclipse.edc.connector.api.management.configuration.transform.ManagementApiTypeTransformerRegistry; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.query.Criterion; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; -import org.eclipse.edc.web.spi.exception.InvalidRequestException; -import org.eclipse.edc.web.spi.exception.ValidationFailureException; -import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto; -import org.eclipse.tractusx.edc.edr.spi.service.EdrService; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; - -import static jakarta.json.stream.JsonCollectors.toJsonArray; -import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.exceptionMapper; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.AGREEMENT_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.ASSET_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.CONTRACT_NEGOTIATION_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.PROVIDER_ID; - -@Consumes({ MediaType.APPLICATION_JSON }) -@Produces({ MediaType.APPLICATION_JSON }) -@Path("/edrs") -public class EdrController implements EdrApi { - - private final EdrService edrService; - private final ManagementApiTypeTransformerRegistry transformerRegistry; - private final JsonObjectValidatorRegistry validatorRegistry; - private final Monitor monitor; - - public EdrController(EdrService edrService, ManagementApiTypeTransformerRegistry transformerRegistry, - JsonObjectValidatorRegistry validatorRegistry, Monitor monitor) { - this.edrService = edrService; - this.transformerRegistry = transformerRegistry; - this.validatorRegistry = validatorRegistry; - this.monitor = monitor; - } - - @POST - @Override - public JsonObject initiateEdrNegotiation(JsonObject requestObject) { - validatorRegistry.validate(EDR_REQUEST_DTO_TYPE, requestObject).orElseThrow(ValidationFailureException::new); - - var edrNegotiationRequest = transformerRegistry.transform(requestObject, NegotiateEdrRequestDto.class) - .compose(dto -> transformerRegistry.transform(dto, NegotiateEdrRequest.class)) - .orElseThrow(InvalidRequestException::new); - - var contractNegotiation = edrService.initiateEdrNegotiation(edrNegotiationRequest).orElseThrow(exceptionMapper(NegotiateEdrRequest.class)); - - var idResponse = IdResponse.Builder.newInstance() - .id(contractNegotiation.getId()) - .createdAt(contractNegotiation.getCreatedAt()) - .build(); - - return transformerRegistry.transform(idResponse, JsonObject.class) - .orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail())); - } - - @GET - @Override - public JsonArray queryEdrs(@QueryParam("assetId") String assetId, - @QueryParam("agreementId") String agreementId, - @QueryParam("contractNegotiationId") String contractNegotiationId, - @QueryParam("providerId") String providerId) { - if (assetId == null && agreementId == null && contractNegotiationId == null) { - throw new InvalidRequestException("At least one of this query parameter is required [assetId, agreementId, contractNegotiationId]"); - } - return edrService.findBy(querySpec(assetId, agreementId, contractNegotiationId, providerId)) - .orElseThrow(exceptionMapper(EndpointDataReferenceEntry.class)) - .stream() - .map(edrCached -> transformerRegistry.transform(edrCached, JsonObject.class)) - .peek(this::logIfError) - .filter(Result::succeeded) - .map(Result::getContent) - .collect(toJsonArray()); - } - - @GET - @Path("/{id}") - @Override - public JsonObject getEdr(@PathParam("id") String transferProcessId) { - var edr = edrService.findByTransferProcessId(transferProcessId).orElseThrow(exceptionMapper(EndpointDataReference.class, transferProcessId)); - return transformerRegistry.transform(edr, DataAddress.class) - .compose(dataAddress -> transformerRegistry.transform(dataAddress, JsonObject.class)) - .orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail())); - } - - @DELETE - @Path("/{id}") - @Override - public void deleteEdr(@PathParam("id") String transferProcessId) { - edrService.deleteByTransferProcessId(transferProcessId).orElseThrow(exceptionMapper(EndpointDataReference.class, transferProcessId)); - } - - private void logIfError(Result result) { - result.onFailure(f -> monitor.warning(f.getFailureDetail())); - } - - private QuerySpec querySpec(String assetId, String agreementId, String contractNegotiationId, String providerId) { - var queryBuilder = QuerySpec.Builder.newInstance(); - if (assetId != null) { - queryBuilder.filter(fieldFilter(ASSET_ID, assetId)); - } - if (agreementId != null) { - queryBuilder.filter(fieldFilter(AGREEMENT_ID, agreementId)); - } - if (contractNegotiationId != null) { - queryBuilder.filter(fieldFilter(CONTRACT_NEGOTIATION_ID, contractNegotiationId)); - } - if (providerId != null) { - queryBuilder.filter(fieldFilter(PROVIDER_ID, providerId)); - } - return queryBuilder.build(); - } - - - private Criterion fieldFilter(String field, String value) { - return Criterion.Builder.newInstance() - .operandLeft(field) - .operator("=") - .operandRight(value) - .build(); - } -} diff --git a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/dto/NegotiateEdrRequestDto.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/dto/NegotiateEdrRequestDto.java deleted file mode 100644 index cfce992ba..000000000 --- a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/dto/NegotiateEdrRequestDto.java +++ /dev/null @@ -1,124 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr.dto; - -import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription; -import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; - -import java.util.ArrayList; -import java.util.List; - -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; - -public class NegotiateEdrRequestDto { - - public static final String EDR_REQUEST_SIMPLE_DTO_TYPE = "NegotiateEdrRequestDto"; - public static final String EDR_REQUEST_DTO_TYPE = TX_NAMESPACE + EDR_REQUEST_SIMPLE_DTO_TYPE; - public static final String EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS = EDC_NAMESPACE + "counterPartyAddress"; - public static final String EDR_REQUEST_DTO_PROTOCOL = EDC_NAMESPACE + "protocol"; - public static final String EDR_REQUEST_DTO_COUNTERPARTY_ID = EDC_NAMESPACE + "counterPartyId"; - public static final String EDR_REQUEST_DTO_PROVIDER_ID = EDC_NAMESPACE + "providerId"; - public static final String EDR_REQUEST_DTO_OFFER = EDC_NAMESPACE + "offer"; - public static final String EDR_REQUEST_DTO_CALLBACK_ADDRESSES = EDC_NAMESPACE + "callbackAddresses"; - - private String counterPartyAddress; - private String protocol = "ids-multipart"; - private String counterPartyId; - - private String providerId; - - private ContractOfferDescription offer; - private List callbackAddresses = new ArrayList<>(); - - private NegotiateEdrRequestDto() { - - } - - public String getCounterPartyAddress() { - return counterPartyAddress; - } - - public String getProtocol() { - return protocol; - } - - public String getCounterPartyId() { - return counterPartyId; - } - - public String getProviderId() { - return providerId; - } - - public List getCallbackAddresses() { - return callbackAddresses; - } - - public ContractOfferDescription getOffer() { - return offer; - } - - public static final class Builder { - private final NegotiateEdrRequestDto dto; - - private Builder() { - dto = new NegotiateEdrRequestDto(); - } - - public Builder connectorAddress(String connectorAddress) { - dto.counterPartyAddress = connectorAddress; - return this; - } - - public Builder protocol(String protocol) { - dto.protocol = protocol; - return this; - } - - public Builder counterPartyId(String connectorId) { - dto.counterPartyId = connectorId; - return this; - } - - public Builder offer(ContractOfferDescription offer) { - dto.offer = offer; - return this; - } - - public Builder providerId(String providerId) { - dto.providerId = providerId; - return this; - } - - public Builder callbackAddresses(List callbackAddresses) { - dto.callbackAddresses = callbackAddresses; - return this; - } - - public NegotiateEdrRequestDto build() { - return dto; - } - - public static Builder newInstance() { - return new Builder(); - } - } -} diff --git a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/schema/EdrSchema.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/schema/EdrSchema.java deleted file mode 100644 index c7dd3fdcb..000000000 --- a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/schema/EdrSchema.java +++ /dev/null @@ -1,109 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr.schema; - -import io.swagger.v3.oas.annotations.media.Schema; -import org.eclipse.edc.connector.api.management.configuration.ManagementApiSchema; -import org.eclipse.edc.connector.api.management.contractnegotiation.ContractNegotiationApi; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; - -import java.util.List; - -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE; -import static org.eclipse.tractusx.edc.api.edr.schema.EdrSchema.EndpointDataReferenceEntrySchema.ENDPOINT_DATA_REFERENCE_ENTRY_EXAMPLE; -import static org.eclipse.tractusx.edc.api.edr.schema.EdrSchema.NegotiateEdrRequestSchema.NEGOTIATE_EDR_REQUEST_EXAMPLE; - -public class EdrSchema { - - @Schema(name = "NegotiateEdrRequest", example = NEGOTIATE_EDR_REQUEST_EXAMPLE) - public record NegotiateEdrRequestSchema( - @Schema(name = TYPE, example = EDR_REQUEST_DTO_TYPE) - String type, - String protocol, - String connectorAddress, - @Deprecated(since = "0.1.3") - @Schema(deprecated = true, description = "please use providerId instead") - String connectorId, - String providerId, - ContractNegotiationApi.ContractOfferDescriptionSchema offer, - List callbackAddresses) { - - public static final String NEGOTIATE_EDR_REQUEST_EXAMPLE = """ - { - "@context": { "edc": "https://w3id.org/edc/v0.0.1/ns/" }, - "@type": "NegotiateEdrRequestDto", - "counterPartyAddress": "http://provider-address", - "protocol": "dataspace-protocol-http", - "providerId": "provider-id", - "offer": { - "offerId": "offer-id", - "assetId": "asset-id", - "policy": { - "@context": "http://www.w3.org/ns/odrl.jsonld", - "@type": "Set", - "@id": "offer-id", - "permission": [{ - "target": "asset-id", - "action": "display" - }] - } - }, - "callbackAddresses": [{ - "transactional": false, - "uri": "http://callback/url", - "events": ["contract.negotiation", "transfer.process"] - }] - } - """; - } - - @Schema(name = "EndpointDataReferenceEntry", example = ENDPOINT_DATA_REFERENCE_ENTRY_EXAMPLE) - public record EndpointDataReferenceEntrySchema( - @Schema(name = TYPE, example = EndpointDataReferenceEntry.SIMPLE_TYPE) - String type, - String agreementId, - String assetId, - String providerId, - String edrState, - Long expirationDate - ) { - public static final String ENDPOINT_DATA_REFERENCE_ENTRY_EXAMPLE = """ - { - "@type": "tx:EndpointDataReferenceEntry", - "agreementId": "MQ==:MQ==:ZTY3MzQ4YWEtNTdmZC00YzA0LTg2ZmQtMGMxNzk0MWM3OTkw", - "transferProcessId": "78a66945-d638-4c0a-be71-b35a0318a410", - "assetId": "1", - "providerId": "BPNL00DATAP00001", - "tx:edrState": "NEGOTIATED", - "tx:expirationDate": 1690811364000, - "@context": { - "dct": "https://purl.org/dc/terms/", - "tx": "https://w3id.org/tractusx/v0.0.1/ns/", - "edc": "https://w3id.org/edc/v0.0.1/ns/", - "dcat": "https://www.w3.org/ns/dcat/", - "odrl": "http://www.w3.org/ns/odrl/2/", - "dspace": "https://w3id.org/dspace/v0.8/" - } - } - """; - } - -} diff --git a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformer.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformer.java deleted file mode 100644 index 369acc9a0..000000000 --- a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformer.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr.transform; - -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.transform.spi.TransformerContext; -import org.eclipse.edc.transform.spi.TypeTransformer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.AUTH_CODE; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.AUTH_KEY; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.EDR_SIMPLE_TYPE; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.ENDPOINT; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.ID; - -public class EndpointDataReferenceToDataAddressTransformer implements TypeTransformer { - @Override - public Class getInputType() { - return EndpointDataReference.class; - } - - @Override - public Class getOutputType() { - return DataAddress.class; - } - - @Override - public @Nullable DataAddress transform(@NotNull EndpointDataReference edr, @NotNull TransformerContext context) { - return DataAddress.Builder.newInstance() - .type(EDR_SIMPLE_TYPE) - .property(ID, edr.getId()) - .property(AUTH_CODE, edr.getAuthCode()) - .property(AUTH_KEY, edr.getAuthKey()) - .property(ENDPOINT, edr.getEndpoint()) - .properties(edr.getProperties()) - .build(); - } -} diff --git a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java deleted file mode 100644 index dc67473d8..000000000 --- a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr.transform; - -import jakarta.json.Json; -import jakarta.json.JsonObject; -import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer; -import org.eclipse.edc.transform.spi.TransformerContext; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CONTRACT_NEGOTIATION_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_EXPIRATION_DATE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_PROVIDER_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_STATE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TYPE; - - -public class JsonObjectFromEndpointDataReferenceEntryTransformer extends AbstractJsonLdTransformer { - - public JsonObjectFromEndpointDataReferenceEntryTransformer() { - super(EndpointDataReferenceEntry.class, JsonObject.class); - } - - @Override - public @Nullable JsonObject transform(@NotNull EndpointDataReferenceEntry dto, @NotNull TransformerContext context) { - - var builder = Json.createObjectBuilder() - .add(TYPE, EDR_ENTRY_TYPE) - .add(EDR_ENTRY_AGREEMENT_ID, dto.getAgreementId()) - .add(EDR_ENTRY_TRANSFER_PROCESS_ID, dto.getTransferProcessId()) - .add(EDR_ENTRY_ASSET_ID, dto.getAssetId()) - .add(EDR_ENTRY_STATE, dto.getEdrState()) - .add(EDR_ENTRY_EXPIRATION_DATE, dto.getExpirationTimestamp()); - - if (dto.getProviderId() != null) { - builder.add(EDR_ENTRY_PROVIDER_ID, dto.getProviderId()); - } - - if (dto.getContractNegotiationId() != null) { - builder.add(EDR_ENTRY_CONTRACT_NEGOTIATION_ID, dto.getContractNegotiationId()); - } - - return builder.build(); - } - - -} diff --git a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java deleted file mode 100644 index 78ee54cbf..000000000 --- a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java +++ /dev/null @@ -1,95 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr.transform; - -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; -import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription; -import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer; -import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; -import org.eclipse.edc.transform.spi.TransformerContext; -import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; - -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_CALLBACK_ADDRESSES; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_COUNTERPARTY_ID; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_OFFER; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_PROTOCOL; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_PROVIDER_ID; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE; - - -public class JsonObjectToNegotiateEdrRequestDtoTransformer extends AbstractJsonLdTransformer { - - public JsonObjectToNegotiateEdrRequestDtoTransformer() { - super(JsonObject.class, NegotiateEdrRequestDto.class); - } - - @Override - public @Nullable NegotiateEdrRequestDto transform(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) { - var builder = NegotiateEdrRequestDto.Builder.newInstance(); - - visitProperties(jsonObject, (k, v) -> setProperties(k, v, builder, context)); - return builder.build(); - } - - private void setProperties(String key, JsonValue value, NegotiateEdrRequestDto.Builder builder, TransformerContext context) { - switch (key) { - case EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS: - transformString(value, builder::connectorAddress, context); - break; - case EDR_REQUEST_DTO_PROTOCOL: - transformString(value, builder::protocol, context); - break; - case EDR_REQUEST_DTO_COUNTERPARTY_ID: - transformString(value, builder::counterPartyId, context); - break; - case EDR_REQUEST_DTO_PROVIDER_ID: - transformString(value, builder::providerId, context); - break; - case EDR_REQUEST_DTO_CALLBACK_ADDRESSES: - var addresses = new ArrayList(); - transformArrayOrObject(value, CallbackAddress.class, addresses::add, context); - builder.callbackAddresses(addresses); - break; - case EDR_REQUEST_DTO_OFFER: - transformArrayOrObject(value, ContractOfferDescription.class, builder::offer, context); - break; - default: - context.problem() - .unexpectedType() - .type(EDR_REQUEST_DTO_TYPE) - .property(key) - .actual(key) - .expected(EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS) - .expected(EDR_REQUEST_DTO_PROTOCOL) - .expected(EDR_REQUEST_DTO_COUNTERPARTY_ID) - .expected(EDR_REQUEST_DTO_PROVIDER_ID) - .expected(EDR_REQUEST_DTO_CALLBACK_ADDRESSES) - .expected(EDR_REQUEST_DTO_OFFER) - .report(); - break; - } - } -} diff --git a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java deleted file mode 100644 index 46bf3e022..000000000 --- a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr.transform; - -import org.eclipse.edc.spi.types.domain.offer.ContractOffer; -import org.eclipse.edc.transform.spi.TransformerContext; -import org.eclipse.edc.transform.spi.TypeTransformer; -import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto; -import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer implements TypeTransformer { - - @Override - public Class getInputType() { - return NegotiateEdrRequestDto.class; - } - - @Override - public Class getOutputType() { - return NegotiateEdrRequest.class; - } - - @Override - public @Nullable NegotiateEdrRequest transform(@NotNull NegotiateEdrRequestDto object, @NotNull TransformerContext context) { - var contractOffer = ContractOffer.Builder.newInstance() - .id(object.getOffer().getOfferId()) - .assetId(object.getOffer().getAssetId()) - .policy(object.getOffer().getPolicy()) - .build(); - - return NegotiateEdrRequest.Builder.newInstance() - .connectorId(object.getCounterPartyId()) - .connectorAddress(object.getCounterPartyAddress()) - .protocol(object.getProtocol()) - .offer(contractOffer) - .callbackAddresses(object.getCallbackAddresses()) - .build(); - } - - private String getId(String value, String defaultValue) { - return value != null ? value : defaultValue; - } - -} diff --git a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/validation/NegotiateEdrRequestDtoValidator.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/validation/NegotiateEdrRequestDtoValidator.java deleted file mode 100644 index 2650e2ba3..000000000 --- a/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/validation/NegotiateEdrRequestDtoValidator.java +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr.validation; - -import jakarta.json.JsonObject; -import org.eclipse.edc.validator.jsonobject.JsonObjectValidator; -import org.eclipse.edc.validator.jsonobject.validators.MandatoryObject; -import org.eclipse.edc.validator.jsonobject.validators.MandatoryValue; -import org.eclipse.edc.validator.spi.Validator; - -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.ASSET_ID; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.OFFER_ID; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.POLICY; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_OFFER; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_PROTOCOL; - - -public class NegotiateEdrRequestDtoValidator { - - private NegotiateEdrRequestDtoValidator() { - } - - public static Validator instance() { - return JsonObjectValidator.newValidator() - .verify(EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS, MandatoryValue::new) - .verify(EDR_REQUEST_DTO_PROTOCOL, MandatoryValue::new) - .verify(EDR_REQUEST_DTO_OFFER, MandatoryObject::new) - .verifyObject(EDR_REQUEST_DTO_OFFER, v -> v - .verify(OFFER_ID, MandatoryValue::new) - .verify(ASSET_ID, MandatoryValue::new) - .verify(POLICY, MandatoryObject::new) - ) - .build(); - } -} diff --git a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtensionTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtensionTest.java deleted file mode 100644 index ef2e3c4e9..000000000 --- a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtensionTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr; - -import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration; -import org.eclipse.edc.connector.api.management.configuration.transform.ManagementApiTypeTransformerRegistry; -import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.injection.ObjectFactory; -import org.eclipse.edc.web.spi.WebService; -import org.eclipse.tractusx.edc.api.edr.transform.JsonObjectFromEndpointDataReferenceEntryTransformer; -import org.eclipse.tractusx.edc.api.edr.transform.JsonObjectToNegotiateEdrRequestDtoTransformer; -import org.eclipse.tractusx.edc.api.edr.transform.NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(DependencyInjectionExtension.class) -public class EdrApiExtensionTest { - - private final ManagementApiTypeTransformerRegistry transformerRegistry = mock(); - private final WebService webService = mock(WebService.class); - private final ManagementApiConfiguration configuration = mock(ManagementApiConfiguration.class); - - @BeforeEach - void setUp(ObjectFactory factory, ServiceExtensionContext context) { - context.registerService(WebService.class, webService); - context.registerService(ManagementApiTypeTransformerRegistry.class, transformerRegistry); - context.registerService(ManagementApiConfiguration.class, configuration); - } - - @Test - void initialize_ShouldConfigureTheController(ServiceExtensionContext context, EdrApiExtension extension) { - var alias = "context"; - - when(configuration.getContextAlias()).thenReturn(alias); - extension.initialize(context); - - verify(webService).registerResource(eq(alias), isA(EdrController.class)); - verify(transformerRegistry).register(isA(NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.class)); - verify(transformerRegistry).register(isA(JsonObjectToNegotiateEdrRequestDtoTransformer.class)); - verify(transformerRegistry).register(isA(JsonObjectFromEndpointDataReferenceEntryTransformer.class)); - - } -} diff --git a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrApiTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrApiTest.java deleted file mode 100644 index 27a0a03dd..000000000 --- a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrApiTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.json.JsonObject; -import org.eclipse.edc.api.transformer.JsonObjectToCallbackAddressTransformer; -import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectToContractOfferDescriptionTransformer; -import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectToContractRequestTransformer; -import org.eclipse.edc.core.transform.TypeTransformerRegistryImpl; -import org.eclipse.edc.core.transform.transformer.OdrlTransformersFactory; -import org.eclipse.edc.jsonld.JsonLdExtension; -import org.eclipse.edc.jsonld.spi.JsonLd; -import org.eclipse.edc.jsonld.util.JacksonJsonLd; -import org.eclipse.edc.junit.assertions.AbstractResultAssert; -import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto; -import org.eclipse.tractusx.edc.api.edr.transform.JsonObjectToNegotiateEdrRequestDtoTransformer; -import org.eclipse.tractusx.edc.api.edr.validation.NegotiateEdrRequestDtoValidator; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; -import static org.eclipse.edc.junit.extensions.TestServiceExtensionContext.testServiceExtensionContext; -import static org.eclipse.tractusx.edc.api.edr.schema.EdrSchema.EndpointDataReferenceEntrySchema.ENDPOINT_DATA_REFERENCE_ENTRY_EXAMPLE; -import static org.eclipse.tractusx.edc.api.edr.schema.EdrSchema.NegotiateEdrRequestSchema.NEGOTIATE_EDR_REQUEST_EXAMPLE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_EXPIRATION_DATE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_PROVIDER_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_STATE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; - -public class EdrApiTest { - - private final ObjectMapper objectMapper = JacksonJsonLd.createObjectMapper(); - private final JsonLd jsonLd = new JsonLdExtension().createJsonLdService(testServiceExtensionContext()); - - private final TypeTransformerRegistry transformer = new TypeTransformerRegistryImpl(); - - - @BeforeEach - void setUp() { - transformer.register(new JsonObjectToContractRequestTransformer()); - transformer.register(new JsonObjectToContractOfferDescriptionTransformer()); - transformer.register(new JsonObjectToCallbackAddressTransformer()); - transformer.register(new JsonObjectToNegotiateEdrRequestDtoTransformer()); - OdrlTransformersFactory.jsonObjectToOdrlTransformers().forEach(transformer::register); - } - - @Test - void edrRequestExample() throws JsonProcessingException { - var validator = NegotiateEdrRequestDtoValidator.instance(); - - var jsonObject = objectMapper.readValue(NEGOTIATE_EDR_REQUEST_EXAMPLE, JsonObject.class); - assertThat(jsonObject).isNotNull(); - - var expanded = jsonLd.expand(jsonObject); - AbstractResultAssert.assertThat(expanded).isSucceeded() - .satisfies(exp -> AbstractResultAssert.assertThat(validator.validate(exp)).isSucceeded()) - .extracting(e -> transformer.transform(e, NegotiateEdrRequestDto.class)) - .satisfies(transformResult -> AbstractResultAssert.assertThat(transformResult).isSucceeded() - .satisfies(transformed -> { - assertThat(transformed.getOffer()).isNotNull(); - assertThat(transformed.getCallbackAddresses()).asList().hasSize(1); - assertThat(transformed.getProviderId()).isNotBlank(); - })); - } - - @Test - void edrEntryExample() throws JsonProcessingException { - - var jsonObject = objectMapper.readValue(ENDPOINT_DATA_REFERENCE_ENTRY_EXAMPLE, JsonObject.class); - assertThat(jsonObject).isNotNull(); - - var expanded = jsonLd.expand(jsonObject); - - AbstractResultAssert.assertThat(expanded).isSucceeded().satisfies(content -> { - - assertThat(first(content, EDR_ENTRY_STATE).getJsonString(VALUE).getString()) - .isEqualTo(jsonObject.getString("tx:edrState")); - - assertThat(first(content, EDR_ENTRY_ASSET_ID).getJsonString(VALUE).getString()) - .isEqualTo(jsonObject.getString("assetId")); - - assertThat(first(content, EDR_ENTRY_AGREEMENT_ID).getJsonString(VALUE).getString()) - .isEqualTo(jsonObject.getString("agreementId")); - - assertThat(first(content, EDR_ENTRY_TRANSFER_PROCESS_ID).getJsonString(VALUE).getString()) - .isEqualTo(jsonObject.getString("transferProcessId")); - - assertThat(first(content, EDR_ENTRY_PROVIDER_ID).getJsonString(VALUE).getString()) - .isEqualTo(jsonObject.getString("providerId")); - - assertThat(first(content, EDR_ENTRY_EXPIRATION_DATE).getJsonNumber(VALUE).longValue()) - .isEqualTo(jsonObject.getJsonNumber("tx:expirationDate").longValue()); - }); - } - - private JsonObject first(JsonObject content, String name) { - return content.getJsonArray(name).getJsonObject(0); - } -} diff --git a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrControllerTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrControllerTest.java deleted file mode 100644 index 5a99eeafd..000000000 --- a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrControllerTest.java +++ /dev/null @@ -1,397 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.restassured.specification.RequestSpecification; -import jakarta.json.Json; -import jakarta.json.JsonObject; -import jakarta.ws.rs.core.MediaType; -import org.eclipse.edc.api.model.IdResponse; -import org.eclipse.edc.connector.api.management.configuration.transform.ManagementApiTypeTransformerRegistry; -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; -import org.eclipse.edc.jsonld.TitaniumJsonLd; -import org.eclipse.edc.jsonld.spi.JsonLd; -import org.eclipse.edc.jsonld.util.JacksonJsonLd; -import org.eclipse.edc.junit.annotations.ApiTest; -import org.eclipse.edc.spi.query.Criterion; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.result.ServiceResult; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; -import org.eclipse.edc.validator.spi.ValidationResult; -import org.eclipse.edc.validator.spi.Violation; -import org.eclipse.edc.web.jersey.jsonld.JerseyJsonLdInterceptor; -import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; -import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto; -import org.eclipse.tractusx.edc.edr.spi.service.EdrService; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates; -import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static io.restassured.RestAssured.given; -import static java.lang.String.format; -import static org.eclipse.edc.api.model.IdResponse.ID_RESPONSE_TYPE; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -import static org.eclipse.tractusx.edc.api.edr.TestFunctions.negotiationRequest; -import static org.eclipse.tractusx.edc.api.edr.TestFunctions.openRequest; -import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.AGREEMENT_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.ASSET_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.CONTRACT_NEGOTIATION_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CONTRACT_NEGOTIATION_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_PROVIDER_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_STATE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TYPE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.PROVIDER_ID; -import static org.hamcrest.Matchers.is; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - -@ApiTest -public class EdrControllerTest extends RestControllerTestBase { - - public static final String EDR_PATH = "/edrs"; - private final JsonLd jsonLdService = new TitaniumJsonLd(monitor); - EdrService edrService = mock(EdrService.class); - ManagementApiTypeTransformerRegistry transformerRegistry = mock(); - JsonObjectValidatorRegistry validatorRegistry = mock(); - - @BeforeEach - void setup() { - jsonLdService.registerNamespace("edc", EDC_NAMESPACE); - jsonLdService.registerNamespace("tx", TX_NAMESPACE); - } - - @Test - void initEdrNegotiation_shouldWork_whenValidRequest() { - when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success()); - - var openRequest = openRequest(); - var contractNegotiation = getContractNegotiation(); - var responseBody = Json.createObjectBuilder().add(TYPE, ID_RESPONSE_TYPE).add(ID, contractNegotiation.getId()).build(); - - when(transformerRegistry.transform(any(JsonObject.class), eq(NegotiateEdrRequestDto.class))).thenReturn(Result.success(NegotiateEdrRequestDto.Builder.newInstance().build())); - when(transformerRegistry.transform(any(), eq(NegotiateEdrRequest.class))).thenReturn(Result.success(openRequest)); - when(edrService.initiateEdrNegotiation(openRequest)).thenReturn(ServiceResult.success(contractNegotiation)); - when(transformerRegistry.transform(any(IdResponse.class), eq(JsonObject.class))).thenReturn(Result.success(responseBody)); - - var request = negotiationRequest(); - - baseRequest() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .post(EDR_PATH) - .then() - .statusCode(200) - .body(ID, is(contractNegotiation.getId())); - - } - - @Test - void initEdrNegotiation_shouldReturnBadRequest_whenValidInvalidRequest() { - when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success()); - - var request = NegotiateEdrRequestDto.Builder.newInstance().build(); - when(transformerRegistry.transform(any(JsonObject.class), eq(NegotiateEdrRequestDto.class))).thenReturn(Result.failure("fail")); - - baseRequest() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .post(EDR_PATH) - .then() - .statusCode(400); - - } - - @Test - void initEdrNegotiation_shouldReturnBadRequest_whenValidationFails() { - when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.failure(Violation.violation("failure", "failure path"))); - var request = negotiationRequest(); - - given() - .port(port) - .body(request) - .contentType(MediaType.APPLICATION_JSON) - .post(EDR_PATH) - .then() - .statusCode(400) - .contentType(MediaType.APPLICATION_JSON); - - verifyNoInteractions(transformerRegistry); - } - - @Test - void initEdrNegotiation_shouldReturnError_whenNotFound() { - var transferProcessId = "id"; - - when(edrService.findByTransferProcessId(transferProcessId)).thenReturn(ServiceResult.notFound("")); - - baseRequest() - .contentType(MediaType.APPLICATION_JSON) - .get(EDR_PATH + "/" + transferProcessId) - .then() - .statusCode(404); - } - - @Test - void getEdr_shouldReturnDataAddress_whenFound() { - var transferProcessId = "id"; - var edr = EndpointDataReference.Builder.newInstance().endpoint("test") - .contractId("test-contract-id") - .id(transferProcessId).build(); - var response = Json.createObjectBuilder() - .add(DataAddress.EDC_DATA_ADDRESS_TYPE_PROPERTY, EndpointDataReference.EDR_SIMPLE_TYPE) - .add(EndpointDataReference.ENDPOINT, edr.getEndpoint()) - .add(EndpointDataReference.ID, edr.getId()) - .build(); - - var dataAddress = DataAddress.Builder.newInstance().type("HttpData").build(); - when(edrService.findByTransferProcessId(transferProcessId)).thenReturn(ServiceResult.success(edr)); - when(transformerRegistry.transform(any(EndpointDataReference.class), eq(DataAddress.class))).thenReturn(Result.success(dataAddress)); - when(transformerRegistry.transform(any(DataAddress.class), eq(JsonObject.class))).thenReturn(Result.success(response)); - - baseRequest() - .contentType(MediaType.APPLICATION_JSON) - .get(EDR_PATH + "/" + transferProcessId) - .then() - .statusCode(200) - .body("'edc:endpoint'", is(edr.getEndpoint())) - .body("'edc:id'", is(edr.getId())) - .body("'edc:type'", is(EndpointDataReference.EDR_SIMPLE_TYPE)); - - } - - @Test - void queryEdrs_shouldReturnCachedEntries_whenAssetIdIsProvided() { - var assetId = "assetId"; - var transferProcessId = "transferProcessId"; - var agreementId = "agreementId"; - var providerId = "providerId"; - - - var entry = EndpointDataReferenceEntry.Builder.newInstance() - .transferProcessId(transferProcessId) - .agreementId(agreementId) - .assetId(assetId) - .providerId(providerId) - .state(EndpointDataReferenceEntryStates.NEGOTIATED.code()) - .build(); - - var response = Json.createObjectBuilder() - .add(TYPE, EDR_ENTRY_TYPE) - .add(EDR_ENTRY_ASSET_ID, entry.getAssetId()) - .add(EDR_ENTRY_TRANSFER_PROCESS_ID, entry.getTransferProcessId()) - .add(EDR_ENTRY_AGREEMENT_ID, entry.getAgreementId()) - .add(EDR_ENTRY_PROVIDER_ID, entry.getProviderId()) - .add(EDR_ENTRY_STATE, entry.getEdrState()) - .build(); - - var filter = QuerySpec.Builder.newInstance().filter(fieldFilter(ASSET_ID, assetId)).build(); - - when(edrService.findBy(eq(filter))).thenReturn(ServiceResult.success(List.of(entry))); - when(transformerRegistry.transform(any(EndpointDataReferenceEntry.class), eq(JsonObject.class))).thenReturn(Result.success(response)); - - baseRequest() - .contentType(MediaType.APPLICATION_JSON) - .get(EDR_PATH + format("?=assetId=%s", assetId)) - .then() - .statusCode(200) - .body("[0].'edc:transferProcessId'", is(entry.getTransferProcessId())) - .body("[0].'edc:agreementId'", is(entry.getAgreementId())) - .body("[0].'edc:assetId'", is(entry.getAssetId())) - .body("[0].'edc:providerId'", is(entry.getProviderId())) - .body("[0].'tx:edrState'", is(entry.getEdrState())); - - } - - @Test - void queryEdrs_shouldReturnCachedEntries_whenAgreementIdIsProvided() { - var assetId = "assetId"; - var transferProcessId = "transferProcessId"; - var agreementId = "agreementId"; - var providerId = "providerId"; - - var entry = EndpointDataReferenceEntry.Builder.newInstance() - .transferProcessId(transferProcessId) - .agreementId(agreementId) - .assetId(assetId) - .providerId(providerId) - .build(); - - - var response = Json.createObjectBuilder() - .add(TYPE, EDR_ENTRY_TYPE) - .add(EDR_ENTRY_ASSET_ID, entry.getAssetId()) - .add(EDR_ENTRY_TRANSFER_PROCESS_ID, entry.getTransferProcessId()) - .add(EDR_ENTRY_AGREEMENT_ID, entry.getAgreementId()) - .add(EDR_ENTRY_PROVIDER_ID, entry.getProviderId()) - .build(); - - var filter = QuerySpec.Builder.newInstance() - .filter(fieldFilter(AGREEMENT_ID, agreementId)) - .filter(fieldFilter(PROVIDER_ID, entry.getProviderId())) - .build(); - - when(edrService.findBy(eq(filter))).thenReturn(ServiceResult.success(List.of(entry))); - when(transformerRegistry.transform(any(EndpointDataReferenceEntry.class), eq(JsonObject.class))).thenReturn(Result.success(response)); - - baseRequest() - .contentType(MediaType.APPLICATION_JSON) - .get(EDR_PATH + format("?=agreementId=%s&providerId=%s", entry.getAgreementId(), entry.getProviderId())) - .then() - .statusCode(200) - .body("[0].'edc:transferProcessId'", is(entry.getTransferProcessId())) - .body("[0].'edc:agreementId'", is(entry.getAgreementId())) - .body("[0].'edc:assetId'", is(entry.getAssetId())) - .body("[0].'edc:providerId'", is(entry.getProviderId())); - } - - @Test - void queryEdrs_shouldReturnCachedEntries_whenContractNegotiationIdIsProvided() { - var assetId = "assetId"; - var transferProcessId = "transferProcessId"; - var agreementId = "agreementId"; - var providerId = "providerId"; - var contractNegotiationId = "contractNegotiationId"; - - var entry = EndpointDataReferenceEntry.Builder.newInstance() - .transferProcessId(transferProcessId) - .agreementId(agreementId) - .assetId(assetId) - .providerId(providerId) - .contractNegotiationId(contractNegotiationId) - .build(); - - - var response = Json.createObjectBuilder() - .add(TYPE, EDR_ENTRY_TYPE) - .add(EDR_ENTRY_ASSET_ID, entry.getAssetId()) - .add(EDR_ENTRY_TRANSFER_PROCESS_ID, entry.getTransferProcessId()) - .add(EDR_ENTRY_AGREEMENT_ID, entry.getAgreementId()) - .add(EDR_ENTRY_CONTRACT_NEGOTIATION_ID, entry.getContractNegotiationId()) - .add(EDR_ENTRY_PROVIDER_ID, entry.getProviderId()) - .build(); - - var filter = QuerySpec.Builder.newInstance() - .filter(fieldFilter(CONTRACT_NEGOTIATION_ID, contractNegotiationId)) - .filter(fieldFilter(PROVIDER_ID, entry.getProviderId())) - .build(); - - when(edrService.findBy(eq(filter))).thenReturn(ServiceResult.success(List.of(entry))); - when(transformerRegistry.transform(any(EndpointDataReferenceEntry.class), eq(JsonObject.class))).thenReturn(Result.success(response)); - - baseRequest() - .contentType(MediaType.APPLICATION_JSON) - .get(EDR_PATH + format("?=contractNegotiationId=%s&providerId=%s", entry.getContractNegotiationId(), entry.getProviderId())) - .then() - .log().all(true) - .statusCode(200) - .body("[0].'edc:transferProcessId'", is(entry.getTransferProcessId())) - .body("[0].'edc:agreementId'", is(entry.getAgreementId())) - .body("[0].'edc:contractNegotiationId'", is(entry.getContractNegotiationId())) - .body("[0].'edc:assetId'", is(entry.getAssetId())) - .body("[0].'edc:providerId'", is(entry.getProviderId())); - } - - @Test - void deleteEdr() { - var transferProcessId = "id"; - - when(edrService.deleteByTransferProcessId(transferProcessId)).thenReturn(ServiceResult.success(null)); - - baseRequest() - .contentType(MediaType.APPLICATION_JSON) - .delete(EDR_PATH + "/" + transferProcessId) - .then() - .statusCode(204); - } - - @Test - void deleteEdr_shouldReturnNotFound_whenNotInCache() { - var transferProcessId = "id"; - - when(edrService.deleteByTransferProcessId(transferProcessId)).thenReturn(ServiceResult.notFound("")); - - baseRequest() - .contentType(MediaType.APPLICATION_JSON) - .delete(EDR_PATH + "/" + transferProcessId) - .then() - .statusCode(404); - } - - @Test - void queryEdrs_shouldFail_whenNoQueryParameter() { - baseRequest() - .contentType(MediaType.APPLICATION_JSON) - .get(EDR_PATH) - .then() - .statusCode(400); - } - - @Override - protected Object controller() { - return new EdrController(edrService, transformerRegistry, validatorRegistry, monitor); - } - - @Override - protected Object additionalResource() { - final ObjectMapper objectMapper = JacksonJsonLd.createObjectMapper(); - return new JerseyJsonLdInterceptor(this.jsonLdService, objectMapper, "edr"); - } - - private RequestSpecification baseRequest() { - return given() - .baseUri("http://localhost:" + port) - .basePath("/") - .when(); - } - - private ContractNegotiation getContractNegotiation() { - return ContractNegotiation.Builder.newInstance() - .id("id") - .counterPartyAddress("http://test") - .counterPartyId("provider") - .protocol("protocol") - .build(); - } - - private Criterion fieldFilter(String field, String value) { - return Criterion.Builder.newInstance() - .operandLeft(field) - .operator("=") - .operandRight(value) - .build(); - } -} diff --git a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/TestFunctions.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/TestFunctions.java deleted file mode 100644 index fc622b523..000000000 --- a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/TestFunctions.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr; - -import jakarta.json.Json; -import jakarta.json.JsonObject; -import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription; -import org.eclipse.edc.policy.model.Policy; -import org.eclipse.edc.spi.types.domain.offer.ContractOffer; -import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto; -import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; - -import java.util.UUID; - -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; - -public class TestFunctions { - - public static ContractOfferDescription createOffer(String offerId, String assetId) { - return ContractOfferDescription.Builder.newInstance() - .offerId(offerId) - .assetId(assetId) - .policy(Policy.Builder.newInstance().build()) - .build(); - } - - public static ContractOfferDescription createOffer(Policy policy) { - return ContractOfferDescription.Builder.newInstance() - .offerId(UUID.randomUUID().toString()) - .assetId(UUID.randomUUID().toString()) - .policy(policy) - .build(); - } - - public static ContractOfferDescription createOffer(String offerId) { - return createOffer(offerId, UUID.randomUUID().toString()); - } - - public static ContractOfferDescription createOffer() { - return createOffer(UUID.randomUUID().toString(), UUID.randomUUID().toString()); - } - - public static JsonObject negotiationRequest() { - return Json.createObjectBuilder() - .add(TYPE, NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE) - .add(EDC_NAMESPACE + "connectorId", "test") - .add(EDC_NAMESPACE + "providerId", "test") - .add(EDC_NAMESPACE + "connectorAddress", "test") - .add(EDC_NAMESPACE + "protocol", "dataspace-protocol-http") - .add(EDC_NAMESPACE + "offer", Json.createObjectBuilder() - .add(EDC_NAMESPACE + "offerId", "offerId") - .add(EDC_NAMESPACE + "assetId", "assetId") - .add(EDC_NAMESPACE + "policy", Json.createObjectBuilder().build()) - ) - .build(); - } - - public static NegotiateEdrRequest openRequest() { - return NegotiateEdrRequest.Builder.newInstance() - .connectorAddress("test") - .connectorId("id") - .protocol("test-protocol") - .offer(ContractOffer.Builder.newInstance() - .id("offerId") - .assetId("assetId") - .policy(Policy.Builder.newInstance().build()).build()) - .build(); - } -} diff --git a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformerTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformerTest.java deleted file mode 100644 index d7c0e7db0..000000000 --- a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformerTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr.transform; - -import org.eclipse.edc.transform.spi.TransformerContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.AUTH_CODE; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.AUTH_KEY; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.Builder; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.EDR_SIMPLE_TYPE; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.ENDPOINT; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.ID; -import static org.mockito.Mockito.mock; - -public class EndpointDataReferenceToDataAddressTransformerTest { - - private final TransformerContext context = mock(TransformerContext.class); - private EndpointDataReferenceToDataAddressTransformer transformer; - - @BeforeEach - void setUp() { - transformer = new EndpointDataReferenceToDataAddressTransformer(); - } - - @Test - void transform() { - - var dto = Builder.newInstance() - .id("dataRequestId") - .authCode("authCode") - .authKey("authKey") - .contractId("test-contract-id") - .endpoint("http://endpoint") - .build(); - - var dataAddress = transformer.transform(dto, context); - - assertThat(dataAddress).isNotNull(); - assertThat(dataAddress.getType()).isEqualTo(EDR_SIMPLE_TYPE); - assertThat(dataAddress.getStringProperty(ID)).isEqualTo(dto.getId()); - assertThat(dataAddress.getStringProperty(ENDPOINT)).isEqualTo(dto.getEndpoint()); - assertThat(dataAddress.getStringProperty(AUTH_KEY)).isEqualTo(dto.getAuthKey()); - assertThat(dataAddress.getStringProperty(AUTH_CODE)).isEqualTo(dto.getAuthCode()); - - } -} diff --git a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java deleted file mode 100644 index 5d493cd38..000000000 --- a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java +++ /dev/null @@ -1,158 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr.transform; - -import jakarta.json.Json; -import jakarta.json.JsonArrayBuilder; -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; -import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription; -import org.eclipse.edc.jsonld.TitaniumJsonLd; -import org.eclipse.edc.jsonld.spi.JsonLd; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; -import org.eclipse.edc.transform.spi.ProblemBuilder; -import org.eclipse.edc.transform.spi.TransformerContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.ASSET_ID; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.OFFER_ID; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.POLICY; -import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.CALLBACK_ADDRESSES; -import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.CONTRACT_REQUEST_TYPE; -import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.OFFER; -import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.PROTOCOL; -import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.PROVIDER_ID; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_OBLIGATION_ATTRIBUTE; -import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_PERMISSION_ATTRIBUTE; -import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_POLICY_TYPE_SET; -import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_PROHIBITION_ATTRIBUTE; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -import static org.eclipse.edc.spi.types.domain.callback.CallbackAddress.EVENTS; -import static org.eclipse.edc.spi.types.domain.callback.CallbackAddress.IS_TRANSACTIONAL; -import static org.eclipse.edc.spi.types.domain.callback.CallbackAddress.URI; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_COUNTERPARTY_ID; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class JsonObjectToNegotiateEdrRequestDtoTransformerTest { - - private final JsonLd jsonLd = new TitaniumJsonLd(mock(Monitor.class)); - private final TransformerContext context = mock(TransformerContext.class); - private JsonObjectToNegotiateEdrRequestDtoTransformer transformer; - - @BeforeEach - void setUp() { - transformer = new JsonObjectToNegotiateEdrRequestDtoTransformer(); - } - - @Test - void transform() { - var jsonObject = Json.createObjectBuilder() - .add(TYPE, CONTRACT_REQUEST_TYPE) - .add(EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS, "test-address") - .add(EDR_REQUEST_DTO_COUNTERPARTY_ID, "test-conn-id") - .add(PROTOCOL, "test-protocol") - .add(PROVIDER_ID, "test-provider-id") - .add(CALLBACK_ADDRESSES, createCallbackAddress()) - .add(OFFER, Json.createObjectBuilder() - .add(OFFER_ID, "test-offer-id") - .add(ASSET_ID, "test-asset") - .add(POLICY, createPolicy()) - .build()) - .build(); - - when(context.transform(any(JsonValue.class), eq(ContractOfferDescription.class))).thenReturn(ContractOfferDescription.Builder.newInstance().build()); - - when(context.transform(any(JsonObject.class), eq(CallbackAddress.class))).thenReturn(CallbackAddress.Builder.newInstance() - .uri("http://test.local") - .events(Set.of("foo", "bar")) - .transactional(true) - .build()); - when(context.transform(any(CallbackAddress.class), eq(CallbackAddress.class))).thenReturn(CallbackAddress.Builder.newInstance() - .uri("http://test.local") - .events(Set.of("foo", "bar")) - .transactional(true) - .build()); - var dto = transformer.transform(jsonLd.expand(jsonObject).getContent(), context); - - assertThat(dto).isNotNull(); - assertThat(dto.getCallbackAddresses()).isNotEmpty(); - assertThat(dto.getProtocol()).isEqualTo("test-protocol"); - assertThat(dto.getCounterPartyAddress()).isEqualTo("test-address"); - assertThat(dto.getCounterPartyId()).isEqualTo("test-conn-id"); - assertThat(dto.getProviderId()).isEqualTo("test-provider-id"); - assertThat(dto.getOffer()).isNotNull(); - - } - - @Test - void transform_reportErrors() { - - when(context.problem()).thenReturn(new ProblemBuilder(context)); - - var jsonObject = Json.createObjectBuilder() - .add(TYPE, CONTRACT_REQUEST_TYPE) - .add(EDC_NAMESPACE + "notFound", "test-address") - .build(); - - var dto = transformer.transform(jsonLd.expand(jsonObject).getContent(), context); - - assertThat(dto).isNotNull(); - verify(context, times(1)).reportProblem(anyString()); - } - - private JsonArrayBuilder createCallbackAddress() { - var builder = Json.createArrayBuilder(); - return builder.add(Json.createObjectBuilder() - .add(IS_TRANSACTIONAL, true) - .add(URI, "http://test.local/") - .add(EVENTS, Json.createArrayBuilder().build())); - } - - private JsonObject createPolicy() { - var permissionJson = getJsonObject("permission"); - var prohibitionJson = getJsonObject("prohibition"); - var dutyJson = getJsonObject("duty"); - return Json.createObjectBuilder() - .add(TYPE, ODRL_POLICY_TYPE_SET) - .add(ODRL_PERMISSION_ATTRIBUTE, permissionJson) - .add(ODRL_PROHIBITION_ATTRIBUTE, prohibitionJson) - .add(ODRL_OBLIGATION_ATTRIBUTE, dutyJson) - .build(); - } - - private JsonObject getJsonObject(String type) { - return Json.createObjectBuilder() - .add(TYPE, type) - .build(); - } -} diff --git a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java deleted file mode 100644 index ed7d31abb..000000000 --- a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr.transform; - -import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; -import org.eclipse.edc.transform.spi.TransformerContext; -import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.api.edr.TestFunctions.createOffer; -import static org.mockito.Mockito.mock; - -public class NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest { - - private final NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer transformer = new NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer(); - - private final TransformerContext context = mock(TransformerContext.class); - - @Test - void inputOutputType() { - assertThat(transformer.getInputType()).isNotNull(); - assertThat(transformer.getOutputType()).isNotNull(); - } - - @Test - void verify_transform() { - var callback = CallbackAddress.Builder.newInstance() - .uri("local://test") - .build(); - var dto = NegotiateEdrRequestDto.Builder.newInstance() - .counterPartyId("connectorId") - .connectorAddress("address") - .protocol("protocol") - .providerId("test-provider") - .offer(createOffer("offerId", "assetId")) - .callbackAddresses(List.of(callback)) - .build(); - - var request = transformer.transform(dto, context); - - assertThat(request).isNotNull(); - assertThat(request.getConnectorId()).isEqualTo("connectorId"); - assertThat(request.getConnectorAddress()).isEqualTo("address"); - assertThat(request.getProtocol()).isEqualTo("protocol"); - assertThat(request.getOffer().getId()).isEqualTo("offerId"); - assertThat(request.getOffer().getPolicy()).isNotNull(); - assertThat(request.getCallbackAddresses()).hasSize(1); - } - - @Test - void verify_transfor_withNoProviderId() { - var dto = NegotiateEdrRequestDto.Builder.newInstance() - .counterPartyId("connectorId") - .connectorAddress("address") - .protocol("protocol") - // do not set provider ID - .offer(createOffer("offerId", "assetId")) - .build(); - - var request = transformer.transform(dto, context); - - assertThat(request).isNotNull(); - } - - @Test - void verify_transform_withNoConsumerId() { - var dto = NegotiateEdrRequestDto.Builder.newInstance() - .counterPartyId("connectorId") - .connectorAddress("address") - .protocol("protocol") - // do not set consumer ID - .providerId("urn:connector:test-provider") - .offer(createOffer("offerId", "assetId")) - .build(); - - var request = transformer.transform(dto, context); - assertThat(request).isNotNull(); - } -} diff --git a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/validation/NegotiateEdrRequestDtoValidatorTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/validation/NegotiateEdrRequestDtoValidatorTest.java deleted file mode 100644 index 55f28eb91..000000000 --- a/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/validation/NegotiateEdrRequestDtoValidatorTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.api.edr.validation; - -import jakarta.json.Json; -import jakarta.json.JsonArrayBuilder; -import jakarta.json.JsonObject; -import org.eclipse.edc.validator.spi.ValidationFailure; -import org.eclipse.edc.validator.spi.Validator; -import org.eclipse.edc.validator.spi.Violation; -import org.junit.jupiter.api.Test; - -import static jakarta.json.Json.createArrayBuilder; -import static jakarta.json.Json.createObjectBuilder; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.InstanceOfAssertFactories.list; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.ASSET_ID; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.OFFER_ID; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.POLICY; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; -import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_OFFER; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_PROTOCOL; -import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_PROVIDER_ID; - -public class NegotiateEdrRequestDtoValidatorTest { - - private final Validator validator = NegotiateEdrRequestDtoValidator.instance(); - - @Test - void shouldSuccess_whenObjectIsValid() { - var input = Json.createObjectBuilder() - .add(EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS, value("http://connector-address")) - .add(EDR_REQUEST_DTO_PROTOCOL, value("protocol")) - .add(EDR_REQUEST_DTO_PROVIDER_ID, value("connector-id")) - .add(EDR_REQUEST_DTO_OFFER, createArrayBuilder().add(createObjectBuilder() - .add(OFFER_ID, value("offerId")) - .add(ASSET_ID, value("offerId")) - .add(POLICY, createArrayBuilder().add(createObjectBuilder())) - )) - .build(); - - var result = validator.validate(input); - - assertThat(result).isSucceeded(); - } - - @Test - void shouldFail_whenMandatoryPropertiesAreMissing() { - var input = Json.createObjectBuilder().build(); - - var result = validator.validate(input); - - assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) - .isNotEmpty() - .anySatisfy(violation -> assertThat(violation.path()).isEqualTo(EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS)) - .anySatisfy(violation -> assertThat(violation.path()).isEqualTo(EDR_REQUEST_DTO_PROTOCOL)) - .anySatisfy(violation -> assertThat(violation.path()).isEqualTo(EDR_REQUEST_DTO_OFFER)); - } - - @Test - void shouldFail_whenOfferMandatoryPropertiesAreMissing() { - var input = Json.createObjectBuilder() - .add(EDR_REQUEST_DTO_COUNTERPARTY_ADDRESS, value("http://connector-address")) - .add(EDR_REQUEST_DTO_PROTOCOL, value("protocol")) - .add(EDR_REQUEST_DTO_PROVIDER_ID, value("connector-id")) - .add(EDR_REQUEST_DTO_OFFER, createArrayBuilder().add(createObjectBuilder())) - .build(); - - var result = validator.validate(input); - - assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) - .isNotEmpty() - .anySatisfy(violation -> assertThat(violation.path()).isEqualTo(EDR_REQUEST_DTO_OFFER + "/" + OFFER_ID)) - .anySatisfy(violation -> assertThat(violation.path()).isEqualTo(EDR_REQUEST_DTO_OFFER + "/" + ASSET_ID)) - .anySatisfy(violation -> assertThat(violation.path()).isEqualTo(EDR_REQUEST_DTO_OFFER + "/" + POLICY)); - } - - private JsonArrayBuilder value(String value) { - return createArrayBuilder().add(createObjectBuilder().add(VALUE, value)); - } -} diff --git a/edc-extensions/edr/edr-cache-sql/README.md b/edc-extensions/edr/edr-cache-sql/README.md deleted file mode 100644 index 6e88c4bb0..000000000 --- a/edc-extensions/edr/edr-cache-sql/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# SQL-based `EndpointDataReferenceCache` extension - -This extension provide a persistent implementation of `EndpointDataReferenceCache`. - -It will store in the database this fields: - -- transferProcessId -- agreementId -- assetId -- edrId - -It represents a single EDR negotiation done with the new Control Plane EDR APIs. - -The EDR itself it is stored in the participant vault with a prefixed key `edr--`. - -**_Note that the SQL statements (DDL) are specific to and only tested with PostgreSQL. Using it with other RDBMS may -work but might have unexpected side effects!_** - -## 1. Table schema - -see [schema.sql](docs/schema.sql). - -## 2. Configuration - -| Key | Description | Mandatory | Default | -|:------------------------|:--------------------------------------------------------------------------------------------------|-----------|---------| -| edc.datasource.edr.name | Datasource used by this extension | | edr | -| edc.edr.vault.path | Directory/Path where to store EDRs in the vault for vaults that supports hierarchical structuring | | | diff --git a/edc-extensions/edr/edr-cache-sql/docs/schema.sql b/edc-extensions/edr/edr-cache-sql/docs/schema.sql deleted file mode 100644 index 24a94045f..000000000 --- a/edc-extensions/edr/edr-cache-sql/docs/schema.sql +++ /dev/null @@ -1,56 +0,0 @@ --- --- Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) --- --- 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 --- --- Contributors: --- Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation --- - - -CREATE TABLE IF NOT EXISTS edc_lease -( - leased_by VARCHAR NOT NULL, - leased_at BIGINT, - lease_duration INTEGER DEFAULT 60000 NOT NULL, - lease_id VARCHAR NOT NULL - CONSTRAINT lease_pk - PRIMARY KEY -); - -COMMENT ON COLUMN edc_lease.leased_at IS 'posix timestamp of lease'; - -COMMENT ON COLUMN edc_lease.lease_duration IS 'duration of lease in milliseconds'; - - -CREATE UNIQUE INDEX IF NOT EXISTS lease_lease_id_uindex - ON edc_lease (lease_id); - -CREATE TABLE IF NOT EXISTS edc_edr_cache -( - transfer_process_id VARCHAR NOT NULL PRIMARY KEY, - agreement_id VARCHAR NOT NULL, - asset_id VARCHAR NOT NULL, - edr_id VARCHAR NOT NULL, - contract_negotiation_id VARCHAR, - provider_id VARCHAR, - expiration_timestamp BIGINT, - state INTEGER DEFAULT 0 NOT NULL, - state_count INTEGER DEFAULT 0, - state_timestamp BIGINT, - error_detail VARCHAR, - lease_id VARCHAR CONSTRAINT edc_edr_cache_lease_lease_id_fk REFERENCES edc_lease ON DELETE SET NULL, - created_at BIGINT NOT NULL, - updated_at BIGINT NOT NULL -); - -CREATE INDEX IF NOT EXISTS edc_edr_asset_id_index - ON edc_edr_cache (asset_id); - - -CREATE INDEX IF NOT EXISTS edc_edr_agreement_id_index - ON edc_edr_cache (agreement_id); diff --git a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCache.java b/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCache.java deleted file mode 100644 index 38a115630..000000000 --- a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCache.java +++ /dev/null @@ -1,315 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.store.sql; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.spi.persistence.EdcPersistenceException; -import org.eclipse.edc.spi.query.Criterion; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.result.StoreResult; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.sql.QueryExecutor; -import org.eclipse.edc.sql.ResultSetMapper; -import org.eclipse.edc.sql.lease.SqlLeaseContextBuilder; -import org.eclipse.edc.sql.store.AbstractSqlStore; -import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry; -import org.eclipse.edc.transaction.spi.TransactionContext; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.edr.store.sql.schema.EdrStatements; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.time.Clock; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static java.lang.String.format; -import static java.util.stream.Collectors.toList; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.ASSET_ID; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.PROVIDER_ID; - -public class SqlEndpointDataReferenceCache extends AbstractSqlStore implements EndpointDataReferenceCache { - - public static final String SEPARATOR = "--"; - public static final String VAULT_PREFIX = "edr" + SEPARATOR; - private final EdrStatements statements; - private final String vaultPath; - private final Clock clock; - private final Vault vault; - - private final SqlLeaseContextBuilder leaseContext; - - - public SqlEndpointDataReferenceCache(DataSourceRegistry dataSourceRegistry, String dataSourceName, - TransactionContext transactionContext, EdrStatements statements, - ObjectMapper objectMapper, Vault vault, String vaultPath, Clock clock, - QueryExecutor queryExecutor, String connectorId) { - super(dataSourceRegistry, dataSourceName, transactionContext, objectMapper, queryExecutor); - this.statements = statements; - this.vaultPath = vaultPath; - this.clock = clock; - this.vault = vault; - leaseContext = SqlLeaseContextBuilder.with(transactionContext, connectorId, statements, clock, queryExecutor); - } - - @Override - public @Nullable EndpointDataReference resolveReference(String transferProcessId) { - Objects.requireNonNull(transferProcessId); - return transactionContext.execute(() -> { - try (var connection = getConnection()) { - var edrId = findById(connection, transferProcessId, this::mapToEdrId); - if (edrId != null) { - return referenceFromEntry(edrId); - } - return null; - } catch (Exception exception) { - throw new EdcPersistenceException(exception); - } - }); - } - - @Override - public @Nullable StoreResult findByIdAndLease(String transferProcessId) { - return transactionContext.execute(() -> { - try (var connection = getConnection()) { - var entity = findById(connection, transferProcessId, this::mapResultSet); - if (entity == null) { - return StoreResult.notFound(format("EndpointDataReference %s not found", transferProcessId)); - } - leaseContext.withConnection(connection).acquireLease(entity.getId()); - return StoreResult.success(entity); - } catch (Exception exception) { - throw new EdcPersistenceException(exception); - } - }); - } - - @Override - public @NotNull List referencesForAsset(String assetId, String providerId) { - var querySpec = QuerySpec.Builder.newInstance(); - querySpec.filter(filterFor(ASSET_ID, assetId)); - - if (providerId != null) { - querySpec.filter(filterFor(PROVIDER_ID, providerId)); - } - - return internalQuery(querySpec.build(), this::mapToWrapper) - .filter(wrapper -> filterActive(wrapper.getEntry())) - .map(EndpointDataReferenceEntryWrapper::getEdrId) - .map(this::referenceFromEntry).collect(Collectors.toList()); - } - - @Override - public Stream queryForEntries(QuerySpec spec) { - return internalQuery(spec, this::mapResultSet); - } - - @Override - public void save(EndpointDataReferenceEntry entry, EndpointDataReference edr) { - transactionContext.execute(() -> { - try (var connection = getConnection()) { - var sql = statements.getInsertTemplate(); - queryExecutor.execute(connection, sql, - entry.getTransferProcessId(), - entry.getAssetId(), - entry.getAgreementId(), - edr.getId(), - entry.getProviderId(), - entry.getContractNegotiationId(), - entry.getExpirationTimestamp(), - entry.getState(), - entry.getStateCount(), - entry.getStateTimestamp(), - entry.getErrorDetail(), - entry.getCreatedAt(), - entry.getUpdatedAt()); - vault.storeSecret(vaultPath + VAULT_PREFIX + edr.getId(), toJson(edr)).orElseThrow((failure) -> new EdcPersistenceException(failure.getFailureDetail())); - } catch (Exception exception) { - throw new EdcPersistenceException(exception); - } - }); - } - - @Override - public void update(EndpointDataReferenceEntry entry) { - transactionContext.execute(() -> { - try (var connection = getConnection()) { - leaseContext.withConnection(connection).breakLease(entry.getTransferProcessId()); - var sql = statements.getUpdateTemplate(); - queryExecutor.execute(connection, sql, - entry.getState(), - entry.getStateCount(), - entry.getStateTimestamp(), - entry.getErrorDetail(), - entry.getUpdatedAt(), - entry.getTransferProcessId()); - } catch (SQLException exception) { - throw new EdcPersistenceException(exception); - } - }); - } - - @Override - public StoreResult deleteByTransferProcessId(String id) { - return transactionContext.execute(() -> { - try (var connection = getConnection()) { - var entryWrapper = findById(connection, id, this::mapToWrapper); - if (entryWrapper != null) { - leaseContext.withConnection(connection).acquireLease(id); - queryExecutor.execute(connection, statements.getDeleteByIdTemplate(), id); - leaseContext.withConnection(connection).breakLease(id); - vault.deleteSecret(vaultPath + VAULT_PREFIX + entryWrapper.getEdrId()).orElseThrow((failure) -> new EdcPersistenceException(failure.getFailureDetail())); - return StoreResult.success(entryWrapper.getEntry()); - } else { - return StoreResult.notFound(format("EDR with id %s not found", id)); - } - } catch (SQLException exception) { - throw new EdcPersistenceException(exception); - } - }); - } - - @Override - public @Nullable EndpointDataReferenceEntry findById(String id) { - return transactionContext.execute(() -> { - try (var connection = getConnection()) { - return findById(connection, id, this::mapResultSet); - } catch (Exception exception) { - throw new EdcPersistenceException(exception); - } - }); - } - - @Override - public @NotNull List nextNotLeased(int max, Criterion... criteria) { - return transactionContext.execute(() -> { - var filter = Arrays.stream(criteria).collect(toList()); - var querySpec = QuerySpec.Builder.newInstance().filter(filter).limit(max).build(); - var statement = statements.createQuery(querySpec); - statement.addWhereClause(statements.getNotLeasedFilter()); - statement.addParameter(clock.millis()); - - try ( - var connection = getConnection(); - var stream = queryExecutor.query(getConnection(), true, this::mapResultSet, statement.getQueryAsString(), statement.getParameters()) - ) { - var negotiations = stream.collect(toList()); - negotiations.forEach(cn -> leaseContext.withConnection(connection).acquireLease(cn.getId())); - return negotiations; - } catch (SQLException e) { - throw new EdcPersistenceException(e); - } - }); - } - - @Override - public void save(EndpointDataReferenceEntry entity) { - throw new UnsupportedOperationException("Please use save(EndpointDataReferenceEntry, EndpointDataReference) instead!"); - } - - private T findById(Connection connection, String id, ResultSetMapper resultSetMapper) { - var sql = statements.getFindByTransferProcessIdTemplate(); - return queryExecutor.single(connection, false, resultSetMapper, sql, id); - } - - @NotNull - private Stream internalQuery(QuerySpec spec, ResultSetMapper resultSetMapper) { - return transactionContext.execute(() -> { - try { - var queryStmt = statements.createQuery(spec); - return queryExecutor.query(getConnection(), true, resultSetMapper, queryStmt.getQueryAsString(), queryStmt.getParameters()); - } catch (SQLException exception) { - throw new EdcPersistenceException(exception); - } - }); - - } - - private EndpointDataReferenceEntry mapResultSet(ResultSet resultSet) throws SQLException { - Long expirationTimestamp = resultSet.getLong(statements.getExpirationTimestampColumn()); - if (resultSet.wasNull()) { - expirationTimestamp = null; - } - return EndpointDataReferenceEntry.Builder.newInstance() - .transferProcessId(resultSet.getString(statements.getTransferProcessIdColumn())) - .assetId(resultSet.getString(statements.getAssetIdColumn())) - .agreementId(resultSet.getString(statements.getAgreementIdColumn())) - .contractNegotiationId(resultSet.getString(statements.getContractNegotiationIdColumn())) - .providerId(resultSet.getString(statements.getProviderIdColumn())) - .state(resultSet.getInt(statements.getStateColumn())) - .stateTimestamp(resultSet.getLong(statements.getStateTimestampColumn())) - .stateCount(resultSet.getInt(statements.getStateCountColumn())) - .createdAt(resultSet.getLong(statements.getCreatedAtColumn())) - .updatedAt(resultSet.getLong(statements.getUpdatedAtColumn())) - .errorDetail(resultSet.getString(statements.getErrorDetailColumn())) - .expirationTimestamp(expirationTimestamp) - .build(); - } - - private String mapToEdrId(ResultSet resultSet) throws SQLException { - return resultSet.getString(statements.getEdrId()); - } - - private EndpointDataReferenceEntryWrapper mapToWrapper(ResultSet resultSet) throws SQLException { - return new EndpointDataReferenceEntryWrapper(mapResultSet(resultSet), mapToEdrId(resultSet)); - } - - private EndpointDataReference referenceFromEntry(String edrId) { - var edr = vault.resolveSecret(vaultPath + VAULT_PREFIX + edrId); - if (edr != null) { - return fromJson(edr, EndpointDataReference.class); - } - return null; - } - - private Criterion filterFor(String field, Object value) { - return Criterion.Builder.newInstance() - .operandLeft(field) - .operator("=") - .operandRight(value) - .build(); - } - - private static class EndpointDataReferenceEntryWrapper { - private final EndpointDataReferenceEntry entry; - private final String edrId; - - private EndpointDataReferenceEntryWrapper(EndpointDataReferenceEntry entry, String edrId) { - this.entry = Objects.requireNonNull(entry); - this.edrId = Objects.requireNonNull(edrId); - } - - public EndpointDataReferenceEntry getEntry() { - return entry; - } - - public String getEdrId() { - return edrId; - } - } -} diff --git a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtension.java b/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtension.java deleted file mode 100644 index 769da1ca7..000000000 --- a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtension.java +++ /dev/null @@ -1,83 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.store.sql; - -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provider; -import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.edc.sql.QueryExecutor; -import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry; -import org.eclipse.edc.transaction.spi.TransactionContext; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.store.sql.schema.EdrStatements; -import org.eclipse.tractusx.edc.edr.store.sql.schema.postgres.PostgresEdrStatements; - -import java.time.Clock; - -@Extension(value = SqlEndpointDataReferenceCacheExtension.NAME) -public class SqlEndpointDataReferenceCacheExtension implements ServiceExtension { - - public static final String NAME = "SQL EDR cache store"; - - @Setting(required = true, value = "Datasource name for EDR Cache SQL store", defaultValue = DataSourceRegistry.DEFAULT_DATASOURCE) - public static final String DATASOURCE_SETTING_NAME = "edc.datasource.edr.name"; - - private static final String DEFAULT_EDR_VAULT_PATH = ""; - @Setting(value = "Directory/Path where to store EDRs in the vault for vaults that supports hierarchical structuring.", defaultValue = DEFAULT_EDR_VAULT_PATH) - public static final String EDC_EDR_VAULT_PATH = "edc.edr.vault.path"; - - @Inject - private DataSourceRegistry dataSourceRegistry; - @Inject - private TransactionContext transactionContext; - @Inject(required = false) - private EdrStatements statements; - @Inject - private TypeManager typeManager; - @Inject - private Clock clock; - @Inject - private Vault vault; - - @Inject - private QueryExecutor queryExecutor; - - @Override - public String name() { - return NAME; - } - - @Provider - public EndpointDataReferenceCache edrCache(ServiceExtensionContext context) { - var dataSourceName = context.getConfig().getString(DATASOURCE_SETTING_NAME, DataSourceRegistry.DEFAULT_DATASOURCE); - var vaultDirectory = context.getConfig().getString(EDC_EDR_VAULT_PATH, DEFAULT_EDR_VAULT_PATH); - return new SqlEndpointDataReferenceCache(dataSourceRegistry, dataSourceName, transactionContext, getStatementImpl(), - typeManager.getMapper(), vault, vaultDirectory, clock, queryExecutor, context.getConnectorId()); - } - - private EdrStatements getStatementImpl() { - return statements == null ? new PostgresEdrStatements() : statements; - } -} diff --git a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/BaseSqlEdrStatements.java b/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/BaseSqlEdrStatements.java deleted file mode 100644 index 168a21129..000000000 --- a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/BaseSqlEdrStatements.java +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020,2022 Microsoft Corporation - * - * 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.edc.edr.store.sql.schema; - -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.sql.translation.SqlOperatorTranslator; -import org.eclipse.edc.sql.translation.SqlQueryStatement; - -import static java.lang.String.format; - -public class BaseSqlEdrStatements implements EdrStatements { - - private final SqlOperatorTranslator sqlOperatorTranslator; - - public BaseSqlEdrStatements(SqlOperatorTranslator sqlOperatorTranslator) { - this.sqlOperatorTranslator = sqlOperatorTranslator; - } - - @Override - public String getFindByTransferProcessIdTemplate() { - return format("SELECT * FROM %s WHERE %s = ?", getEdrTable(), getTransferProcessIdColumn()); - } - - @Override - public SqlQueryStatement createQuery(QuerySpec querySpec) { - var select = format("SELECT * FROM %s", getEdrTable()); - return new SqlQueryStatement(select, querySpec, new EdrMapping(this), sqlOperatorTranslator); - } - - @Override - public String getInsertTemplate() { - return format("INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - getEdrTable(), - getTransferProcessIdColumn(), - getAssetIdColumn(), - getAgreementIdColumn(), - getEdrId(), - getProviderIdColumn(), - getContractNegotiationIdColumn(), - getExpirationTimestampColumn(), - getStateColumn(), - getStateCountColumn(), - getStateTimestampColumn(), - getErrorDetailColumn(), - getCreatedAtColumn(), - getUpdatedAtColumn() - ); - } - - @Override - public String getUpdateTemplate() { - return format("UPDATE %s SET %s=?, %s=?, %s=?, %s=?, %s=? WHERE %s = ?;", - getEdrTable(), getStateColumn(), getStateCountColumn(), getStateTimestampColumn(), - getErrorDetailColumn(), getUpdatedAtColumn(), getTransferProcessIdColumn()); - } - - @Override - public String getDeleteByIdTemplate() { - return format("DELETE FROM %s WHERE %s = ?", - getEdrTable(), - getTransferProcessIdColumn()); - } - - @Override - public String getDeleteLeaseTemplate() { - return format("DELETE FROM %s WHERE %s=?", getLeaseTableName(), getLeaseIdColumn()); - } - - @Override - public String getInsertLeaseTemplate() { - return format("INSERT INTO %s (%s, %s, %s, %s) VALUES (?, ?, ?, ?);", - getLeaseTableName(), getLeaseIdColumn(), getLeasedByColumn(), getLeasedAtColumn(), getLeaseDurationColumn()); - } - - @Override - public String getUpdateLeaseTemplate() { - return format("UPDATE %s SET %s=? WHERE %s = ?;", getEdrTable(), getLeaseIdColumn(), getTransferProcessIdColumn()); - } - - @Override - public String getFindLeaseByEntityTemplate() { - return format("SELECT * FROM %s WHERE %s = (SELECT lease_id FROM %s WHERE %s=? )", - getLeaseTableName(), getLeaseIdColumn(), getEdrTable(), getTransferProcessIdColumn()); - } -} diff --git a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrMapping.java b/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrMapping.java deleted file mode 100644 index fbeb27f55..000000000 --- a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrMapping.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.store.sql.schema; - -import org.eclipse.edc.sql.translation.TranslationMapping; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; - -/** - * Maps fields of a {@link EndpointDataReferenceEntry} onto the - * corresponding SQL schema (= column names) - */ -public class EdrMapping extends TranslationMapping { - public EdrMapping(EdrStatements statements) { - add("assetId", statements.getAssetIdColumn()); - add("agreementId", statements.getAgreementIdColumn()); - add("providerId", statements.getProviderIdColumn()); - add("contractNegotiationId", statements.getContractNegotiationIdColumn()); - add("state", statements.getStateColumn()); - } -} diff --git a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrStatements.java b/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrStatements.java deleted file mode 100644 index 9e49a9e1a..000000000 --- a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrStatements.java +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.store.sql.schema; - -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.sql.lease.LeaseStatements; -import org.eclipse.edc.sql.translation.SqlQueryStatement; - -/** - * Sql Statements for DataPlane Store - */ -public interface EdrStatements extends LeaseStatements { - - default String getEdrTable() { - return "edc_edr_cache"; - } - - default String getTransferProcessIdColumn() { - return "transfer_process_id"; - } - - default String getAgreementIdColumn() { - return "agreement_id"; - } - - default String getProviderIdColumn() { - return "provider_id"; - } - - default String getContractNegotiationIdColumn() { - return "contract_negotiation_id"; - } - - default String getAssetIdColumn() { - return "asset_id"; - } - - default String getEdrId() { - return "edr_id"; - } - - default String getCreatedAtColumn() { - return "created_at"; - } - - default String getUpdatedAtColumn() { - return "updated_at"; - } - - default String getStateColumn() { - return "state"; - } - - default String getExpirationTimestampColumn() { - return "expiration_timestamp"; - } - - default String getStateCountColumn() { - return "state_count"; - } - - default String getStateTimestampColumn() { - return "state_timestamp"; - } - - default String getErrorDetailColumn() { - return "error_detail"; - } - - String getFindByTransferProcessIdTemplate(); - - SqlQueryStatement createQuery(QuerySpec querySpec); - - String getInsertTemplate(); - - String getUpdateTemplate(); - - String getDeleteByIdTemplate(); - -} - diff --git a/edc-extensions/edr/edr-cache-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/edr/edr-cache-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension deleted file mode 100644 index 6ce8fce04..000000000 --- a/edc-extensions/edr/edr-cache-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ /dev/null @@ -1,20 +0,0 @@ -################################################################################# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# -# 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 -################################################################################# - -org.eclipse.tractusx.edc.edr.store.sql.SqlEndpointDataReferenceCacheExtension diff --git a/edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTest.java b/edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTest.java deleted file mode 100644 index afbe9025b..000000000 --- a/edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.store.sql; - -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.edc.sql.QueryExecutor; -import org.eclipse.edc.sql.lease.testfixtures.LeaseUtil; -import org.eclipse.edc.sql.testfixtures.PostgresqlStoreSetupExtension; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCacheTestBase; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.store.sql.schema.EdrStatements; -import org.eclipse.tractusx.edc.edr.store.sql.schema.postgres.PostgresEdrStatements; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.sql.SQLException; -import java.time.Clock; -import java.time.Duration; - -import static java.util.UUID.randomUUID; -import static org.eclipse.tractusx.edc.edr.spi.TestFunctions.edr; -import static org.eclipse.tractusx.edc.edr.spi.TestFunctions.edrEntry; -import static org.eclipse.tractusx.edc.edr.store.sql.SqlEndpointDataReferenceCache.SEPARATOR; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@PostgresqlDbIntegrationTest -@ExtendWith(PostgresqlStoreSetupExtension.class) -public class SqlEndpointDataReferenceCacheTest extends EndpointDataReferenceCacheTestBase { - - EdrStatements statements = new PostgresEdrStatements(); - SqlEndpointDataReferenceCache cache; - - Clock clock = Clock.systemUTC(); - - Vault vault = mock(Vault.class); - - TypeManager typeManager = new TypeManager(); - - LeaseUtil leaseUtil; - - @BeforeEach - void setUp(PostgresqlStoreSetupExtension extension, QueryExecutor queryExecutor) throws IOException { - - when(vault.deleteSecret(any())).thenReturn(Result.success()); - when(vault.storeSecret(any(), any())).thenReturn(Result.success()); - when(vault.resolveSecret(any())).then(a -> edrJson(a.getArgument(0))); - - cache = new SqlEndpointDataReferenceCache(extension.getDataSourceRegistry(), extension.getDatasourceName(), extension.getTransactionContext(), statements, typeManager.getMapper(), vault, "", clock, queryExecutor, CONNECTOR_NAME); - var schema = Files.readString(Paths.get("./docs/schema.sql")); - extension.runQuery(schema); - leaseUtil = new LeaseUtil(extension.getTransactionContext(), extension::getConnection, statements, clock); - - } - - @AfterEach - void tearDown(PostgresqlStoreSetupExtension extension) throws SQLException { - extension.runQuery("DROP TABLE " + statements.getEdrTable() + " CASCADE"); - } - - @Test - void verify_unoffensive_secretKey() { - var tpId = "tp1"; - var assetId = "asset1"; - var edrId = "edr1"; - - var edr = edr(edrId); - var entry = edrEntry(assetId, randomUUID().toString(), tpId); - - getStore().save(entry, edr); - - verify(vault).storeSecret(argThat(s -> s.startsWith("edr--")), anyString()); - } - - @Test - void verify_custom_vaultPath(PostgresqlStoreSetupExtension extension, QueryExecutor queryExecutor) { - - var path = "testPath/"; - cache = new SqlEndpointDataReferenceCache(extension.getDataSourceRegistry(), extension.getDatasourceName(), extension.getTransactionContext(), statements, typeManager.getMapper(), vault, path, clock, queryExecutor, CONNECTOR_NAME); - - var tpId = "tp1"; - var assetId = "asset1"; - var edrId = "edr1"; - - var edr = edr(edrId); - var entry = edrEntry(assetId, randomUUID().toString(), tpId); - - cache.save(entry, edr); - - verify(vault).storeSecret(argThat(s -> s.startsWith(path + "edr--")), anyString()); - } - - @Override - protected EndpointDataReferenceCache getStore() { - return cache; - } - - @Override - protected void lockEntity(String negotiationId, String owner, Duration duration) { - leaseUtil.leaseEntity(negotiationId, owner, duration); - } - - @Override - protected boolean isLockedBy(String negotiationId, String owner) { - return leaseUtil.isLeased(negotiationId, owner); - } - - - private String edrJson(String id) { - return typeManager.writeValueAsString(edr(id.split(SEPARATOR)[1])); - } - -} diff --git a/edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTransactionalTest.java b/edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTransactionalTest.java deleted file mode 100644 index 4e98b06ea..000000000 --- a/edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTransactionalTest.java +++ /dev/null @@ -1,157 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.store.sql; - -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; -import org.eclipse.edc.spi.persistence.EdcPersistenceException; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.result.StoreResult; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.sql.QueryExecutor; -import org.eclipse.edc.sql.testfixtures.PostgresqlStoreSetupExtension; -import org.eclipse.tractusx.edc.edr.store.sql.schema.EdrStatements; -import org.eclipse.tractusx.edc.edr.store.sql.schema.postgres.PostgresEdrStatements; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.sql.SQLException; -import java.time.Clock; - -import static java.util.UUID.randomUUID; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCacheTestBase.CONNECTOR_NAME; -import static org.eclipse.tractusx.edc.edr.spi.TestFunctions.edr; -import static org.eclipse.tractusx.edc.edr.spi.TestFunctions.edrEntry; -import static org.eclipse.tractusx.edc.edr.store.sql.SqlEndpointDataReferenceCache.VAULT_PREFIX; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@PostgresqlDbIntegrationTest -@ExtendWith(PostgresqlStoreSetupExtension.class) -public class SqlEndpointDataReferenceCacheTransactionalTest { - - EdrStatements statements = new PostgresEdrStatements(); - SqlEndpointDataReferenceCache cache; - - Clock clock = Clock.systemUTC(); - - Vault vault = mock(Vault.class); - - TypeManager typeManager = new TypeManager(); - - @BeforeEach - void setUp(PostgresqlStoreSetupExtension extension, QueryExecutor queryExecutor) throws IOException { - - when(vault.deleteSecret(any())).thenReturn(Result.success()); - when(vault.storeSecret(any(), any())).thenReturn(Result.success()); - - cache = new SqlEndpointDataReferenceCache(extension.getDataSourceRegistry(), extension.getDatasourceName(), extension.getTransactionContext(), statements, typeManager.getMapper(), vault, "", clock, queryExecutor, CONNECTOR_NAME); - var schema = Files.readString(Paths.get("./docs/schema.sql")); - extension.runQuery(schema); - - } - - @Test - void save_shouldFail_whenVaultError() { - - var tpId = "tp1"; - var assetId = "asset1"; - var edrId = "edr1"; - - var edr = edr(edrId); - var entry = edrEntry(assetId, randomUUID().toString(), tpId); - - when(vault.storeSecret(any(), any())).thenReturn(Result.failure("fail")); - when(vault.resolveSecret(edr.getId())).thenReturn(typeManager.writeValueAsString(edr)); - - assertThatThrownBy(() -> cache.save(entry, edr)).isInstanceOf(EdcPersistenceException.class); - - assertThat(cache.resolveReference(tpId)) - .isNull(); - - } - - @Test - void save() { - - var tpId = "tp1"; - var assetId = "asset1"; - var edrId = "edr1"; - - var edr = edr(edrId); - var entry = edrEntry(assetId, randomUUID().toString(), tpId); - - when(vault.storeSecret(any(), any())).thenReturn(Result.success()); - when(vault.resolveSecret(VAULT_PREFIX + edr.getId())).thenReturn(typeManager.writeValueAsString(edr)); - - cache.save(entry, edr); - - assertThat(cache.resolveReference(tpId)) - .isNotNull() - .extracting(EndpointDataReference::getId) - .isEqualTo(edrId); - - var edrs = cache.referencesForAsset(assetId, null); - assertThat(edrs.size()).isEqualTo(1); - assertThat(edrs.get((0)).getId()).isEqualTo(edrId); - - verify(vault).storeSecret(eq(VAULT_PREFIX + edr.getId()), any()); - verify(vault, times(2)).resolveSecret(eq(VAULT_PREFIX + edr.getId())); - - } - - @Test - void deleteByTransferProcessId_shouldDelete_WhenFound() { - - var entry = edrEntry("assetId", "agreementId", "tpId"); - var edr = edr("edrId"); - cache.save(entry, edr); - - assertThat(cache.deleteByTransferProcessId(entry.getTransferProcessId())) - .extracting(StoreResult::getContent) - .isEqualTo(entry); - - assertThat(cache.resolveReference(entry.getTransferProcessId())).isNull(); - assertThat(cache.referencesForAsset(entry.getAssetId(), null)).hasSize(0); - assertThat(cache.queryForEntries(QuerySpec.max())).hasSize(0); - - verify(vault).storeSecret(eq(VAULT_PREFIX + edr.getId()), any()); - verify(vault).deleteSecret(eq(VAULT_PREFIX + edr.getId())); - } - - @AfterEach - void tearDown(PostgresqlStoreSetupExtension extension) throws SQLException { - extension.runQuery("DROP TABLE " + statements.getEdrTable() + " CASCADE"); - } - -} diff --git a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallback.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallback.java index 74bd92aea..b9e2afda0 100644 --- a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallback.java +++ b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallback.java @@ -19,10 +19,10 @@ package org.eclipse.tractusx.edc.callback; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationFinalized; -import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; -import org.eclipse.edc.connector.spi.transferprocess.TransferProcessService; -import org.eclipse.edc.connector.transfer.spi.types.TransferRequest; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationFinalized; +import org.eclipse.edc.connector.controlplane.services.spi.callback.CallbackEventRemoteMessage; +import org.eclipse.edc.connector.controlplane.services.spi.transferprocess.TransferProcessService; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest; import org.eclipse.edc.spi.event.Event; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.result.Result; @@ -36,6 +36,7 @@ public class ContractNegotiationCallback implements InProcessCallback { public static final DataAddress DATA_DESTINATION = DataAddress.Builder.newInstance().type("HttpProxy").build(); + private static final String TRANSFER_TYPE = "HttpData-PULL"; private final TransferProcessService transferProcessService; private final Monitor monitor; @@ -62,6 +63,7 @@ private Result initiateTransfer(ContractNegotiationFinalized negotiationFi .counterPartyAddress(negotiationFinalized.getCounterPartyAddress()) .protocol(negotiationFinalized.getProtocol()) .dataDestination(DATA_DESTINATION) + .transferType(TRANSFER_TYPE) .callbackAddresses(negotiationFinalized.getCallbackAddresses()) .build(); diff --git a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackMessageDispatcher.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackMessageDispatcher.java index d540d9fcf..67287114b 100644 --- a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackMessageDispatcher.java +++ b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackMessageDispatcher.java @@ -19,7 +19,7 @@ package org.eclipse.tractusx.edc.callback; -import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; +import org.eclipse.edc.connector.controlplane.services.spi.callback.CallbackEventRemoteMessage; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.event.Event; import org.eclipse.edc.spi.message.RemoteMessageDispatcher; diff --git a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryImpl.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryImpl.java index c03e660c3..c91b742a2 100644 --- a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryImpl.java +++ b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryImpl.java @@ -19,7 +19,7 @@ package org.eclipse.tractusx.edc.callback; -import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; +import org.eclipse.edc.connector.controlplane.services.spi.callback.CallbackEventRemoteMessage; import org.eclipse.edc.spi.event.Event; import org.eclipse.edc.spi.result.Result; import org.eclipse.tractusx.edc.spi.callback.InProcessCallback; diff --git a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtension.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtension.java index ac3a22b59..a13220d36 100644 --- a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtension.java +++ b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtension.java @@ -19,10 +19,9 @@ package org.eclipse.tractusx.edc.callback; -import org.eclipse.edc.connector.spi.callback.CallbackProtocolResolverRegistry; -import org.eclipse.edc.connector.spi.contractagreement.ContractAgreementService; -import org.eclipse.edc.connector.spi.transferprocess.TransferProcessService; -import org.eclipse.edc.connector.transfer.spi.store.TransferProcessStore; +import org.eclipse.edc.connector.controlplane.services.spi.callback.CallbackProtocolResolverRegistry; +import org.eclipse.edc.connector.controlplane.services.spi.contractagreement.ContractAgreementService; +import org.eclipse.edc.connector.controlplane.services.spi.transferprocess.TransferProcessService; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.spi.message.RemoteMessageDispatcherRegistry; @@ -31,7 +30,6 @@ import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.transaction.spi.TransactionContext; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; import org.eclipse.tractusx.edc.spi.callback.InProcessCallbackRegistry; import static org.eclipse.tractusx.edc.callback.InProcessCallbackMessageDispatcher.CALLBACK_EVENT_LOCAL; @@ -50,12 +48,6 @@ public class LocalCallbackExtension implements ServiceExtension { @Inject private TransferProcessService transferProcessService; - @Inject - private TransferProcessStore transferProcessStore; - - @Inject - private EndpointDataReferenceCache edrCache; - @Inject private InProcessCallbackRegistry callbackRegistry; @@ -65,9 +57,6 @@ public class LocalCallbackExtension implements ServiceExtension { @Inject private TransactionContext transactionContext; - @Inject - private EndpointDataReferenceCache endpointDataReferenceCache; - @Inject private ContractAgreementService agreementService; @@ -83,7 +72,6 @@ public String name() { public void initialize(ServiceExtensionContext context) { callbackRegistry.registerHandler(new ContractNegotiationCallback(transferProcessService, monitor)); - callbackRegistry.registerHandler(new TransferProcessLocalCallback(edrCache, transferProcessStore, agreementService, transformerRegistry, transactionContext, monitor)); resolverRegistry.registerResolver(this::resolveProtocol); registry.register(new InProcessCallbackMessageDispatcher(callbackRegistry)); diff --git a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallback.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallback.java deleted file mode 100644 index 6e5a4a0ac..000000000 --- a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallback.java +++ /dev/null @@ -1,198 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.callback; - -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; -import org.eclipse.edc.connector.spi.contractagreement.ContractAgreementService; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessStarted; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessTerminated; -import org.eclipse.edc.connector.transfer.spi.store.TransferProcessStore; -import org.eclipse.edc.spi.event.Event; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.query.Criterion; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.transaction.spi.TransactionContext; -import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates; -import org.eclipse.tractusx.edc.spi.callback.InProcessCallback; - -import java.text.ParseException; -import java.time.ZoneOffset; -import java.util.Optional; - -import static java.lang.String.format; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.REFRESHING; - -public class TransferProcessLocalCallback implements InProcessCallback { - - private final EndpointDataReferenceCache edrCache; - private final TransferProcessStore transferProcessStore; - private final TypeTransformerRegistry transformerRegistry; - private final TransactionContext transactionContext; - private final Monitor monitor; - private final ContractAgreementService agreementService; - - public TransferProcessLocalCallback(EndpointDataReferenceCache edrCache, TransferProcessStore transferProcessStore, - ContractAgreementService agreementService, TypeTransformerRegistry transformerRegistry, - TransactionContext transactionContext, Monitor monitor) { - this.edrCache = edrCache; - this.transferProcessStore = transferProcessStore; - this.agreementService = agreementService; - this.transformerRegistry = transformerRegistry; - this.transactionContext = transactionContext; - this.monitor = monitor; - } - - @Override - public Result invoke(CallbackEventRemoteMessage message) { - if (message.getEventEnvelope().getPayload() instanceof TransferProcessStarted transferProcessStarted && transferProcessStarted.getDataAddress() != null) { - return transformerRegistry.transform(transferProcessStarted.getDataAddress(), EndpointDataReference.class) - .compose(this::storeEdr) - .mapTo(); - } else if (message.getEventEnvelope().getPayload() instanceof TransferProcessTerminated terminated && terminated.getReason() != null) { - return handleTransferProcessTermination(terminated); - } else { - return Result.success(); - } - } - - private Result handleTransferProcessTermination(TransferProcessTerminated terminated) { - return transactionContext.execute(() -> { - var transferProcess = transferProcessStore.findById(terminated.getTransferProcessId()); - if (transferProcess != null) { - stopEdrNegotiation(transferProcess.getDataRequest().getAssetId(), transferProcess.getDataRequest().getContractId(), terminated.getReason()); - return Result.success(); - } else { - return Result.failure(format("Failed to find a transfer process with ID %s", terminated.getTransferProcessId())); - } - }); - - } - - private Result storeEdr(EndpointDataReference edr) { - return transactionContext.execute(() -> { - var transferProcess = Optional.ofNullable(transferProcessStore.findById(edr.getId())) - .orElseGet(() -> transferProcessStore.findForCorrelationId(edr.getId())); - - if (transferProcess != null) { - String contractNegotiationId; - var contractNegotiation = agreementService.findNegotiation(transferProcess.getContractId()); - if (contractNegotiation != null) { - contractNegotiationId = contractNegotiation.getId(); - } else { - var msg = format("Contract negotiation for agreement with id: %s is missing.", transferProcess.getContractId()); - monitor.warning(msg); - return Result.failure(msg); - } - var expirationTime = extractExpirationTime(edr); - - if (expirationTime.failed()) { - return expirationTime.mapTo(); - } - var cacheEntry = EndpointDataReferenceEntry.Builder.newInstance() - .transferProcessId(transferProcess.getId()) - .assetId(transferProcess.getDataRequest().getAssetId()) - .agreementId(transferProcess.getDataRequest().getContractId()) - .providerId(contractNegotiation.getCounterPartyId()) - .state(EndpointDataReferenceEntryStates.NEGOTIATED.code()) - .expirationTimestamp(expirationTime.getContent()) - .contractNegotiationId(contractNegotiationId) - .build(); - - cleanOldEdr(transferProcess.getDataRequest().getAssetId(), transferProcess.getDataRequest().getContractId()); - edrCache.save(cacheEntry, edr); - - return Result.success(); - } else { - return Result.failure(format("Failed to find a transfer process with correlation ID %s", edr.getId())); - } - }); - - } - - private void stopEdrNegotiation(String assetId, String agreementId, String errorDetail) { - var querySpec = QuerySpec.Builder.newInstance() - .filter(fieldFilter("agreementId", agreementId)) - .filter(fieldFilter("assetId", assetId)) - .filter(fieldFilter("state", REFRESHING.code())) - .build(); - - edrCache.queryForEntries(querySpec).forEach((entry -> { - monitor.debug(format("Transitioning EDR to Error Refreshing for transfer process %s", entry.getTransferProcessId())); - entry.setErrorDetail(errorDetail); - entry.transitionError(); - edrCache.update(entry); - })); - } - - private void cleanOldEdr(String assetId, String agreementId) { - var querySpec = QuerySpec.Builder.newInstance() - .filter(fieldFilter("agreementId", agreementId)) - .filter(fieldFilter("assetId", assetId)) - .build(); - - edrCache.queryForEntries(querySpec).forEach((entry -> { - monitor.debug(format("Expiring EDR for transfer process %s", entry.getTransferProcessId())); - entry.transitionToExpired(); - edrCache.update(entry); - - var transferProcess = transferProcessStore.findById(entry.getTransferProcessId()); - - if (transferProcess != null && transferProcess.canBeTerminated()) { - transferProcess.transitionTerminating(); - transferProcessStore.save(transferProcess); - } else { - monitor.info(format("Cannot terminate transfer process with id: %s", entry.getTransferProcessId())); - } - - })); - } - - private Result extractExpirationTime(EndpointDataReference edr) { - try { - if (edr.getAuthCode() != null) { - var jwt = SignedJWT.parse(edr.getAuthCode()); - var expirationTime = jwt.getJWTClaimsSet().getExpirationTime(); - if (expirationTime != null) { - return Result.success(expirationTime - .toInstant() - .atOffset(ZoneOffset.UTC) - .toInstant().toEpochMilli()); - } - } - } catch (ParseException e) { - return Result.failure(format("Failed to parts JWT token for edr %s", edr.getId())); - } - return Result.success(0L); - } - - private Criterion fieldFilter(String field, Object value) { - return Criterion.Builder.newInstance() - .operandLeft(field) - .operator("=") - .operandRight(value) - .build(); - } -} diff --git a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallbackTest.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallbackTest.java index c38d27bcb..9247bda85 100644 --- a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallbackTest.java +++ b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallbackTest.java @@ -19,22 +19,22 @@ package org.eclipse.tractusx.edc.callback; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationAccepted; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationAgreed; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationEvent; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationFinalized; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationInitiated; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationOffered; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationRequested; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationTerminated; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationVerified; -import org.eclipse.edc.connector.spi.transferprocess.TransferProcessService; -import org.eclipse.edc.connector.transfer.spi.types.TransferProcess; -import org.eclipse.edc.connector.transfer.spi.types.TransferRequest; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationAccepted; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationAgreed; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationEvent; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationFinalized; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationInitiated; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationOffered; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationRequested; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationTerminated; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationVerified; +import org.eclipse.edc.connector.controlplane.contract.spi.types.agreement.ContractAgreement; +import org.eclipse.edc.connector.controlplane.services.spi.transferprocess.TransferProcessService; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.result.ServiceResult; -import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtensionTest.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtensionTest.java index 8fb31d81c..1c764c360 100644 --- a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtensionTest.java +++ b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtensionTest.java @@ -19,8 +19,8 @@ package org.eclipse.tractusx.edc.callback; -import org.eclipse.edc.connector.spi.callback.CallbackProtocolResolver; -import org.eclipse.edc.connector.spi.callback.CallbackProtocolResolverRegistry; +import org.eclipse.edc.connector.controlplane.services.spi.callback.CallbackProtocolResolver; +import org.eclipse.edc.connector.controlplane.services.spi.callback.CallbackProtocolResolverRegistry; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.message.RemoteMessageDispatcherRegistry; import org.eclipse.edc.spi.system.ServiceExtensionContext; @@ -66,10 +66,10 @@ void shouldInitializeTheExtension(ServiceExtensionContext context, LocalCallback assertThat(resolver.resolve("test")).isNull(); var callbackArgumentCaptor = ArgumentCaptor.forClass(InProcessCallback.class); - verify(inProcessCallbackRegistry, times(2)).registerHandler(callbackArgumentCaptor.capture()); + verify(inProcessCallbackRegistry, times(1)).registerHandler(callbackArgumentCaptor.capture()); assertThat(callbackArgumentCaptor.getAllValues()) .flatExtracting(Object::getClass) - .containsExactly(ContractNegotiationCallback.class, TransferProcessLocalCallback.class); + .containsExactly(ContractNegotiationCallback.class); } } diff --git a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TestFunctions.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TestFunctions.java index 4a46e31b0..7fa7a36f8 100644 --- a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TestFunctions.java +++ b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TestFunctions.java @@ -27,17 +27,16 @@ import com.nimbusds.jose.jwk.gen.RSAKeyGenerator; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationFinalized; -import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessStarted; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessTerminated; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationFinalized; +import org.eclipse.edc.connector.controlplane.contract.spi.types.agreement.ContractAgreement; +import org.eclipse.edc.connector.controlplane.services.spi.callback.CallbackEventRemoteMessage; +import org.eclipse.edc.connector.controlplane.transfer.spi.event.TransferProcessStarted; +import org.eclipse.edc.connector.controlplane.transfer.spi.event.TransferProcessTerminated; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.event.Event; import org.eclipse.edc.spi.event.EventEnvelope; import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import java.time.Instant; import java.util.Date; @@ -116,16 +115,6 @@ public static TransferProcessTerminated getTransferTerminatedEvent(String transf .build(); } - public static EndpointDataReference getEdr() { - return EndpointDataReference.Builder.newInstance() - .id("dataRequestId") - .contractId("test-contract-id") - .authCode(createToken()) - .authKey("authKey") - .endpoint("http://endpoint") - .build(); - } - public static CallbackEventRemoteMessage remoteMessage(T event) { var callback = CallbackAddress.Builder.newInstance() .events(Set.of("test")) diff --git a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallbackTest.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallbackTest.java deleted file mode 100644 index 290ce3528..000000000 --- a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallbackTest.java +++ /dev/null @@ -1,272 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.callback; - -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; -import org.eclipse.edc.connector.spi.contractagreement.ContractAgreementService; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessCompleted; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessDeprovisioned; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessEvent; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessProvisioned; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessRequested; -import org.eclipse.edc.connector.transfer.spi.store.TransferProcessStore; -import org.eclipse.edc.connector.transfer.spi.types.DataRequest; -import org.eclipse.edc.connector.transfer.spi.types.TransferProcess; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.transaction.spi.NoopTransactionContext; -import org.eclipse.edc.transaction.spi.TransactionContext; -import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.ArgumentsProvider; -import org.junit.jupiter.params.provider.ArgumentsSource; -import org.mockito.ArgumentCaptor; - -import java.util.List; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.EDR_SIMPLE_TYPE; -import static org.eclipse.tractusx.edc.callback.TestFunctions.getEdr; -import static org.eclipse.tractusx.edc.callback.TestFunctions.getTransferProcessStartedEvent; -import static org.eclipse.tractusx.edc.callback.TestFunctions.getTransferTerminatedEvent; -import static org.eclipse.tractusx.edc.callback.TestFunctions.remoteMessage; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.REFRESHING; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - - -public class TransferProcessLocalCallbackTest { - - TransferProcessStore transferProcessStore = mock(); - EndpointDataReferenceCache edrCache = mock(); - - TransactionContext transactionContext = new NoopTransactionContext(); - - TransferProcessLocalCallback callback; - - ContractAgreementService agreementService = mock(); - - TypeTransformerRegistry transformerRegistry = mock(TypeTransformerRegistry.class); - - - @BeforeEach - void setup() { - callback = new TransferProcessLocalCallback(edrCache, transferProcessStore, agreementService, transformerRegistry, transactionContext, mock(Monitor.class)); - } - - @Test - void invoke_shouldStoreTheEdrInCache_whenDataAddressIsPresent() { - - var transferProcessId = "transferProcessId"; - var assetId = "assetId"; - var contractId = "contractId"; - - - var edr = getEdr(); - - - var dataRequest = DataRequest.Builder.newInstance().id(edr.getId()) - .destinationType("HttpProxy") - .assetId(assetId) - .contractId(contractId) - .build(); - - var transferProcess = TransferProcess.Builder.newInstance() - .id(transferProcessId) - .dataRequest(dataRequest) - .build(); - - var edrEntry = EndpointDataReferenceEntry.Builder.newInstance() - .agreementId(contractId) - .transferProcessId(transferProcessId) - .assetId(assetId) - .build(); - - var negotiation = ContractNegotiation.Builder.newInstance() - .id(contractId) - .counterPartyId("providerId") - .counterPartyAddress("http://test") - .protocol("protocol") - .build(); - - when(transformerRegistry.transform(any(DataAddress.class), eq(EndpointDataReference.class))).thenReturn(Result.success(edr)); - when(transferProcessStore.findForCorrelationId(edr.getId())).thenReturn(transferProcess); - when(transferProcessStore.findById(transferProcessId)).thenReturn(transferProcess); - when(agreementService.findNegotiation(contractId)).thenReturn(negotiation); - when(edrCache.queryForEntries(any())).thenReturn(Stream.of(edrEntry)); - - - var event = getTransferProcessStartedEvent(DataAddress.Builder.newInstance().type(EDR_SIMPLE_TYPE).build()); - - var cacheEntryCaptor = ArgumentCaptor.forClass(EndpointDataReferenceEntry.class); - var edrCaptor = ArgumentCaptor.forClass(EndpointDataReference.class); - var message = remoteMessage(event); - - var result = callback.invoke(message); - assertThat(result.succeeded()).isTrue(); - - verify(edrCache).save(cacheEntryCaptor.capture(), edrCaptor.capture()); - verify(edrCache).update(argThat(entry -> entry.getState() == EndpointDataReferenceEntryStates.EXPIRED.code())); - - assertThat(edrCaptor.getValue()).usingRecursiveComparison().isEqualTo(edr); - - } - - @Test - void invoke_shouldNotFail_whenDataAddressIsAbsent() { - - var event = getTransferProcessStartedEvent(); - var message = remoteMessage(event); - - var result = callback.invoke(message); - assertThat(result.succeeded()).isTrue(); - - verifyNoInteractions(edrCache); - verifyNoInteractions(transferProcessStore); - } - - @Test - void invoke_shouldNotFail_whenTransferProcessNotFound() { - - var transferProcessId = "transferProcessId"; - - var edr = getEdr(); - - when(transformerRegistry.transform(any(DataAddress.class), eq(EndpointDataReference.class))).thenReturn(Result.success(edr)); - - when(transferProcessStore.findForCorrelationId(edr.getId())).thenReturn(null); - - when(transferProcessStore.findById(transferProcessId)).thenReturn(null); - - var event = getTransferProcessStartedEvent(DataAddress.Builder.newInstance().type(EDR_SIMPLE_TYPE).build()); - var message = remoteMessage(event); - - var result = callback.invoke(message); - assertThat(result.succeeded()).isFalse(); - - verifyNoInteractions(edrCache); - } - - @Test - void invoke_shouldFail_withInvalidDataAddress() { - - var event = getTransferProcessStartedEvent(DataAddress.Builder.newInstance().type("HttpProxy").build()); - - when(transformerRegistry.transform(any(DataAddress.class), eq(EndpointDataReference.class))) - .thenReturn(Result.failure("failure")); - - var message = remoteMessage(event); - - var result = callback.invoke(message); - assertThat(result.failed()).isTrue(); - - verifyNoInteractions(edrCache); - verifyNoInteractions(transferProcessStore); - } - - @Test - void invoke_shouldStopEdrNegotiation_whenTerminatedMessageReceived() { - - var transferProcessId = "transferProcessId"; - var assetId = "assetId"; - var contractId = "contractId"; - var edr = getEdr(); - - var dataRequest = DataRequest.Builder.newInstance().id(edr.getId()) - .destinationType("HttpProxy") - .assetId(assetId) - .contractId(contractId) - .build(); - - var transferProcess = TransferProcess.Builder.newInstance() - .id(transferProcessId) - .dataRequest(dataRequest) - .build(); - - var edrEntry = EndpointDataReferenceEntry.Builder.newInstance() - .agreementId(contractId) - .transferProcessId(transferProcessId) - .assetId(assetId) - .state(REFRESHING.code()) - .build(); - - when(transferProcessStore.findById(transferProcessId)).thenReturn(transferProcess); - when(edrCache.queryForEntries(any())).thenReturn(Stream.of(edrEntry)); - - var event = getTransferTerminatedEvent(transferProcessId, "Failure"); - var message = remoteMessage(event); - - var result = callback.invoke(message); - assertThat(result.succeeded()).isTrue(); - - verify(edrCache).update(argThat(entry -> entry.getState() == EndpointDataReferenceEntryStates.ERROR.code())); - - } - - @ParameterizedTest - @ArgumentsSource(EventInstances.class) - void invoke_shouldIgnoreOtherEvents(TransferProcessEvent event) { - var message = remoteMessage(event); - var result = callback.invoke(message); - - assertThat(result.succeeded()).isTrue(); - - verifyNoInteractions(edrCache); - } - - private static class EventInstances implements ArgumentsProvider { - - @Override - public Stream provideArguments(ExtensionContext context) { - return Stream.of( - baseBuilder(TransferProcessRequested.Builder.newInstance()).build(), - baseBuilder(TransferProcessProvisioned.Builder.newInstance()).build(), - baseBuilder(TransferProcessCompleted.Builder.newInstance()).build(), - baseBuilder(TransferProcessDeprovisioned.Builder.newInstance()).build() - ).map(Arguments::of); - - } - - private > B baseBuilder(B builder) { - var callbacks = List.of(CallbackAddress.Builder.newInstance().uri("http://local").events(Set.of("test")).build()); - return builder - .transferProcessId(UUID.randomUUID().toString()) - .callbackAddresses(callbacks); - } - } -} diff --git a/edc-extensions/iatp/tx-iatp-sts-dim/build.gradle.kts b/edc-extensions/iatp/tx-iatp-sts-dim/build.gradle.kts new file mode 100644 index 000000000..d225a5f05 --- /dev/null +++ b/edc-extensions/iatp/tx-iatp-sts-dim/build.gradle.kts @@ -0,0 +1,33 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 { + `java-library` + `maven-publish` +} + +dependencies { + implementation(libs.edc.spi.identitytrust) + implementation(libs.edc.auth.oauth2.client) + implementation(project(":spi:core-spi")) + implementation(project(":core:core-utils")) + + testImplementation(libs.edc.junit) + testImplementation(testFixtures(libs.edc.lib.http)) +} diff --git a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCacheCoreExtension.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimOauthClientExtension.java similarity index 53% rename from core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCacheCoreExtension.java rename to edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimOauthClientExtension.java index 3f273bea8..a21184a80 100644 --- a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCacheCoreExtension.java +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimOauthClientExtension.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,43 +17,43 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.edr.core; +package org.eclipse.tractusx.edc.iam.iatp.sts.dim; +import org.eclipse.edc.iam.oauth2.spi.client.Oauth2Client; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provider; -import org.eclipse.edc.spi.query.CriterionOperatorRegistry; +import org.eclipse.edc.spi.security.Vault; import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.edr.core.defaults.EdrCacheEntryPropertyLookup; -import org.eclipse.tractusx.edc.edr.core.defaults.InMemoryEndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.iam.iatp.sts.dim.oauth.DimOauth2Client; +import org.eclipse.tractusx.edc.iam.iatp.sts.dim.oauth.DimOauthClientImpl; /** - * Registers default services for the EDR cache. + * Extension that provides an implementation if {@link DimOauth2Client} using {@link Oauth2Client} + * and the {@link StsRemoteClientConfiguration} configuration for fetching an OAuth token */ -@Extension(value = EdrCacheCoreExtension.NAME) -public class EdrCacheCoreExtension implements ServiceExtension { +@Extension(DimOauthClientExtension.NAME) +public class DimOauthClientExtension implements ServiceExtension { - static final String NAME = "EDR Cache Core"; + protected static final String NAME = "DIM OAuth client extension"; @Inject - private CriterionOperatorRegistry operatorRegistry; + private StsRemoteClientConfiguration clientConfiguration; - @Override - public String name() { - return NAME; - } + @Inject + private Oauth2Client oauth2Client; + @Inject + private Vault vault; @Override - public void initialize(ServiceExtensionContext context) { - operatorRegistry.registerPropertyLookup(new EdrCacheEntryPropertyLookup()); + public String name() { + return NAME; } - @Provider(isDefault = true) - public EndpointDataReferenceCache edrCache() { - return new InMemoryEndpointDataReferenceCache(operatorRegistry); + @Provider + public DimOauth2Client oauth2Client() { + return new DimOauthClientImpl(oauth2Client, vault, clientConfiguration); } -} +} \ No newline at end of file diff --git a/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureTokenService.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureTokenService.java new file mode 100644 index 000000000..60c56af78 --- /dev/null +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureTokenService.java @@ -0,0 +1,227 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.iam.iatp.sts.dim; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.MediaType; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.eclipse.edc.http.spi.EdcHttpClient; +import org.eclipse.edc.iam.identitytrust.spi.SecureTokenService; +import org.eclipse.edc.spi.iam.TokenRepresentation; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.tractusx.edc.iam.iatp.sts.dim.oauth.DimOauth2Client; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; + +import static java.lang.String.format; +import static org.eclipse.edc.http.spi.FallbackFactories.retryWhenStatusIsNotIn; +import static org.eclipse.edc.iam.identitytrust.spi.SelfIssuedTokenConstants.PRESENTATION_TOKEN_CLAIM; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SUBJECT; + +/** + * Implementation of {@link SecureTokenService} that talks with DIM wallet. It supports two APIs for fetching the + * SI Token: + *
    + *
  • grantAccess: request the SI token to DIM by providing the credential types required
  • + *
  • signToken: request the SI token to DIM by providing the extracted `token` from the received SI token
  • + *
+ */ +public class DimSecureTokenService implements SecureTokenService { + + public static final MediaType TYPE_JSON = MediaType.parse("application/json"); + public static final String READ_SCOPE = "read"; + public static final String GRANT_ACCESS = "grantAccess"; + public static final String SCOPE = "scope"; + public static final String CREDENTIAL_TYPES = "credentialTypes"; + public static final String SIGN_TOKEN = "signToken"; + + private final EdcHttpClient httpClient; + private final String dimUrl; + private final DimOauth2Client dimOauth2Client; + private final ObjectMapper mapper; + private final Monitor monitor; + + private final Map grantAccessMapper = Map.of( + ISSUER, "consumerDid", + AUDIENCE, "providerDid"); + + private final Map signTokenMapper = Map.of( + AUDIENCE, "audience", + ISSUER, "issuer", + SUBJECT, "subject", + PRESENTATION_TOKEN_CLAIM, PRESENTATION_TOKEN_CLAIM); + + public DimSecureTokenService(EdcHttpClient httpClient, String dimUrl, DimOauth2Client dimOauth2Client, ObjectMapper mapper, Monitor monitor) { + this.httpClient = httpClient; + this.dimUrl = dimUrl; + this.dimOauth2Client = dimOauth2Client; + this.mapper = mapper; + this.monitor = monitor; + } + + @Override + public Result createToken(Map claims, @Nullable String bearerAccessScope) { + return Optional.ofNullable(bearerAccessScope) + .map(scope -> grantAccessRequest(claims, scope)) + .orElseGet(() -> signTokenRequest(claims)); + } + + private Result grantAccessRequest(Map claims, @Nullable String bearerAccessScope) { + return grantAccessPayload(claims, bearerAccessScope) + .compose(this::postRequest) + .map(builder -> builder.url(dimUrl).build()) + .compose(request -> executeRequest(request, GRANT_ACCESS)); + } + + private Result signTokenRequest(Map claims) { + return signTokenPayload(claims) + .compose(this::postRequest) + .map(builder -> builder.url(dimUrl).build()) + .compose(request -> executeRequest(request, SIGN_TOKEN)); + } + + private Result> grantAccessPayload(Map claims, String bearerAccessScope) { + return mapClaims(claims, grantAccessMapper) + .compose(payload -> extractScopes(bearerAccessScope) + .onSuccess(scopes -> payload.put(CREDENTIAL_TYPES, scopes)) + .compose(i -> Result.success(payload))) + .map(payload -> { + payload.put(SCOPE, READ_SCOPE); + return Map.of(GRANT_ACCESS, payload); + }); + } + + private Result> extractScopes(String bearerAccessScope) { + var scopes = new HashSet(); + var split = bearerAccessScope.split(" "); + + var result = Arrays.stream(split) + .map(scope -> extractCredential(scope, scopes::add)) + .reduce(Result::merge) + .orElseGet(Result::success); + + if (result.succeeded()) { + return Result.success(scopes); + } else { + return Result.failure(result.getFailureDetail()); + } + } + + private Result extractCredential(String scope, Consumer consumer) { + var tokens = scope.split(":"); + if (tokens.length != 3) { + return Result.failure("Scope string %s has invalid format".formatted(scope)); + } + consumer.accept(tokens[1]); + return Result.success(); + } + + @NotNull + private Result> signTokenPayload(Map claims) { + return mapClaims(claims, signTokenMapper) + .map(payload -> Map.of(SIGN_TOKEN, payload)); + } + + private Result executeRequest(Request request, String context) { + return httpClient.execute(request, List.of(retryWhenStatusIsNotIn(200, 201)), this::handleResponse) + .recover(failure -> Result.failure("[%s] %s".formatted(context, failure.getFailureDetail()))); + } + + private Result handleResponse(Response response) { + try { + var body = Objects.requireNonNull(response.body()).string(); + var parsedBody = mapper.readValue(body, new TypeReference>() { + }); + return Optional.ofNullable(parsedBody.get("jwt")) + .map(token -> TokenRepresentation.Builder.newInstance().token(token).build()) + .map(Result::success) + .orElseGet(() -> Result.failure("Failed to get jwt field")); + } catch (IOException e) { + monitor.severe("Failed to parse response from DIM"); + return Result.failure(e.getMessage()); + } + } + + private Result postRequest(Map body) { + try { + var requestBody = RequestBody.create(mapper.writeValueAsString(body), TYPE_JSON); + return baseRequestWithToken() + .map(builder -> builder.post(requestBody)); + } catch (JsonProcessingException e) { + return Result.failure(e.getMessage()); + } + + } + + private Result baseRequestWithToken() { + return dimOauth2Client.obtainRequestToken() + .map(this::baseRequestWithToken); + } + + private Request.Builder baseRequestWithToken(TokenRepresentation tokenRepresentation) { + return new Request.Builder() + .addHeader("Authorization", format("Bearer %s", tokenRepresentation.getToken())); + } + + private Result> mapClaims(Map claims, Map mappings) { + var payload = new HashMap(); + var result = mappings.entrySet().stream() + .map((entry -> mapClaim(claims, entry))) + .peek(inner -> inner.onSuccess(mapped -> payload.put(mapped.getKey(), mapped.getValue()))) + .map(Result::mapTo) + .reduce(Result::merge) + .orElseGet(() -> Result.success(null)); + + if (result.failed()) { + return Result.failure(result.getFailureDetail()); + } else { + return Result.success(payload); + } + + } + + private Result> mapClaim(Map claims, Map.Entry mapping) { + var value = claims.get(mapping.getKey()); + if (value != null) { + return Result.success(Map.entry(mapping.getValue(), value)); + } else { + return Result.failure("Key %s not found in the input claims".formatted(mapping.getKey())); + } + } + +} diff --git a/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureTokenServiceExtension.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureTokenServiceExtension.java new file mode 100644 index 000000000..5b01ff4e6 --- /dev/null +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureTokenServiceExtension.java @@ -0,0 +1,78 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.iam.iatp.sts.dim; + +import org.eclipse.edc.http.spi.EdcHttpClient; +import org.eclipse.edc.iam.identitytrust.spi.SecureTokenService; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.tractusx.edc.core.utils.PathUtils; +import org.eclipse.tractusx.edc.iam.iatp.sts.dim.oauth.DimOauth2Client; + +import static java.util.Optional.ofNullable; +import static org.eclipse.tractusx.edc.core.utils.RequiredConfigWarnings.warningNotPresent; + +@Extension(DimSecureTokenServiceExtension.NAME) +public class DimSecureTokenServiceExtension implements ServiceExtension { + + @Setting(value = "STS Dim endpoint") + public static final String DIM_URL = "edc.iam.sts.dim.url"; + protected static final String NAME = "DIM Secure token service extension"; + + @Inject + private StsRemoteClientConfiguration stsRemoteClientConfiguration; + + @Inject + private DimOauth2Client dimOauth2Client; + + @Inject + private EdcHttpClient httpClient; + + @Inject + private Monitor monitor; + + @Inject + private TypeManager typeManager; + + + @Override + public String name() { + return NAME; + } + + @Provider + public SecureTokenService secureTokenService(ServiceExtensionContext context) { + var dimUrl = ofNullable(context.getConfig().getString(DIM_URL, null)) + .map(PathUtils::removeTrailingSlash) + .orElse(null); + + if (dimUrl == null) { + warningNotPresent(context.getMonitor().withPrefix("STS Client for DIM"), DIM_URL); + } + + return new DimSecureTokenService(httpClient, dimUrl, dimOauth2Client, typeManager.getMapper(), monitor); + } +} \ No newline at end of file diff --git a/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimStsConfigurationExtension.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimStsConfigurationExtension.java new file mode 100644 index 000000000..4f5674955 --- /dev/null +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimStsConfigurationExtension.java @@ -0,0 +1,77 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.iam.iatp.sts.dim; + +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.tractusx.edc.core.utils.PathUtils; + +import static java.util.Optional.ofNullable; +import static org.eclipse.tractusx.edc.core.utils.RequiredConfigWarnings.warningNotPresent; + +/** + * Configuration Extension for the STS OAuth2 client + */ +@Extension(DimStsConfigurationExtension.NAME) +public class DimStsConfigurationExtension implements ServiceExtension { + + @Setting(value = "STS OAuth2 endpoint for requesting a token") + public static final String TOKEN_URL = "edc.iam.sts.oauth.token.url"; + + @Setting(value = "STS OAuth2 client id") + public static final String CLIENT_ID = "edc.iam.sts.oauth.client.id"; + + @Setting(value = "Vault alias of STS OAuth2 client secret") + public static final String CLIENT_SECRET_ALIAS = "edc.iam.sts.oauth.client.secret.alias"; + + protected static final String NAME = "DIM STS client configuration extension"; + + + @Override + public String name() { + return NAME; + } + + @Provider + public StsRemoteClientConfiguration clientConfiguration(ServiceExtensionContext context) { + + var tokenUrl = ofNullable(context.getConfig().getString(TOKEN_URL, null)) + .map(PathUtils::removeTrailingSlash).orElse(null); + var clientId = context.getConfig().getString(CLIENT_ID, null); + var clientSecretAlias = context.getConfig().getString(CLIENT_SECRET_ALIAS, null); + + var monitor = context.getMonitor().withPrefix("STS Client for DIM"); + if (tokenUrl == null) { + warningNotPresent(monitor, TOKEN_URL); + } + if (clientId == null) { + warningNotPresent(monitor, CLIENT_ID); + } + if (clientSecretAlias == null) { + warningNotPresent(monitor, CLIENT_SECRET_ALIAS); + } + + return new StsRemoteClientConfiguration(tokenUrl, clientId, clientSecretAlias); + } + +} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/authorization/AuthorizationHandlerRegistry.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/StsRemoteClientConfiguration.java similarity index 60% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/authorization/AuthorizationHandlerRegistry.java rename to edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/StsRemoteClientConfiguration.java index b402430c4..35cd8c913 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/spi/provider/gateway/authorization/AuthorizationHandlerRegistry.java +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/StsRemoteClientConfiguration.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,24 +17,17 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.authorization; +package org.eclipse.tractusx.edc.iam.iatp.sts.dim; -import org.jetbrains.annotations.Nullable; +import org.eclipse.edc.spi.security.Vault; /** - * Manages {@link AuthorizationHandler}s. + * Configuration of the OAuth2 client for the OAuth2 Client credentials flow + * + * @param tokenUrl The token endpoint + * @param clientId The identifier of the client + * @param clientSecretAlias The client secret alias to be used with the {@link Vault} for fetching the secret */ -public interface AuthorizationHandlerRegistry { - - /** - * Returns a handler for the alias or null if not found. - */ - @Nullable - AuthorizationHandler getHandler(String alias); - - /** - * Registers a handler for the given alias. - */ - void register(String alias, AuthorizationHandler handler); +public record StsRemoteClientConfiguration(String tokenUrl, String clientId, String clientSecretAlias) { } diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2Client.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/oauth/DimOauth2Client.java similarity index 80% rename from edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2Client.java rename to edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/oauth/DimOauth2Client.java index 20a8451f7..e3aff4b97 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2Client.java +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/oauth/DimOauth2Client.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,16 +17,22 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.miw.oauth2; +package org.eclipse.tractusx.edc.iam.iatp.sts.dim.oauth; import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; import org.eclipse.edc.spi.iam.TokenRepresentation; import org.eclipse.edc.spi.result.Result; /** - * OAuth2 client for fetching an access token to be added when using the MIW APIs + * OAuth2 client for fetching an access token to be added when using the DIM APIs */ @ExtensionPoint -public interface MiwOauth2Client { +public interface DimOauth2Client { + + /** + * Request a token from the Auth server + * + * @return The {@link TokenRepresentation} + */ Result obtainRequestToken(); } diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2ClientImpl.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/oauth/DimOauthClientImpl.java similarity index 54% rename from edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2ClientImpl.java rename to edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/oauth/DimOauthClientImpl.java index 1d18bbbe0..d0b3f2fcc 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2ClientImpl.java +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/oauth/DimOauthClientImpl.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,47 +17,50 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.miw.oauth2; +package org.eclipse.tractusx.edc.iam.iatp.sts.dim.oauth; import org.eclipse.edc.iam.oauth2.spi.client.Oauth2Client; import org.eclipse.edc.iam.oauth2.spi.client.Oauth2CredentialsRequest; import org.eclipse.edc.iam.oauth2.spi.client.SharedSecretOauth2CredentialsRequest; import org.eclipse.edc.spi.iam.TokenRepresentation; import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.tractusx.edc.iam.iatp.sts.dim.StsRemoteClientConfiguration; import org.jetbrains.annotations.NotNull; -public class MiwOauth2ClientImpl implements MiwOauth2Client { +public class DimOauthClientImpl implements DimOauth2Client { private static final String GRANT_TYPE = "client_credentials"; + private final StsRemoteClientConfiguration configuration; private final Oauth2Client oauth2Client; - private final MiwOauth2ClientConfiguration configuration; + private final Vault vault; - public MiwOauth2ClientImpl(Oauth2Client oauth2Client, MiwOauth2ClientConfiguration configuration) { - this.oauth2Client = oauth2Client; + public DimOauthClientImpl(Oauth2Client oauth2Client, Vault vault, StsRemoteClientConfiguration configuration) { this.configuration = configuration; + this.oauth2Client = oauth2Client; + this.vault = vault; } @Override public Result obtainRequestToken() { - return oauth2Client.requestToken(createRequest()); - } + return createRequest().compose(oauth2Client::requestToken); - public MiwOauth2ClientConfiguration getConfiguration() { - return configuration; } - + @NotNull - private Oauth2CredentialsRequest createRequest() { - var builder = SharedSecretOauth2CredentialsRequest.Builder.newInstance() - .url(configuration.getTokenUrl()) - .clientId(configuration.getClientId()) - .clientSecret(configuration.getClientSecret()) - .grantType(GRANT_TYPE); - - if (configuration.getScope() != null) { - builder.scope(configuration.getScope()); + private Result createRequest() { + var secret = vault.resolveSecret(configuration.clientSecretAlias()); + if (secret != null) { + var builder = SharedSecretOauth2CredentialsRequest.Builder.newInstance() + .url(configuration.tokenUrl()) + .clientId(configuration.clientId()) + .clientSecret(secret) + .grantType(GRANT_TYPE); + + return Result.success(builder.build()); + } else { + return Result.failure("Failed to fetch client secret from the vault with alias: %s".formatted(configuration.clientSecretAlias())); } - return builder.build(); } } diff --git a/edc-extensions/iatp/tx-iatp-sts-dim/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..dfaffa2a0 --- /dev/null +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,23 @@ +################################################################################# +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# +# 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 +################################################################################# + +org.eclipse.tractusx.edc.iam.iatp.sts.dim.DimOauthClientExtension +org.eclipse.tractusx.edc.iam.iatp.sts.dim.DimSecureTokenServiceExtension +org.eclipse.tractusx.edc.iam.iatp.sts.dim.DimStsConfigurationExtension + diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/AuthorizationHandlerRegistryImplTest.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimOauthClientExtensionTest.java similarity index 57% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/AuthorizationHandlerRegistryImplTest.java rename to edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimOauthClientExtensionTest.java index 59bf7b0fd..0c366f52d 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/AuthorizationHandlerRegistryImplTest.java +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimOauthClientExtensionTest.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,21 +17,29 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.dataplane.proxy.provider.core.gateway.auth; +package org.eclipse.tractusx.edc.iam.iatp.sts.dim; -import org.eclipse.tractusx.edc.dataplane.proxy.spi.provider.gateway.authorization.AuthorizationHandler; +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.tractusx.edc.iam.iatp.sts.dim.oauth.DimOauthClientImpl; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -class AuthorizationHandlerRegistryImplTest { +@ExtendWith(DependencyInjectionExtension.class) +public class DimOauthClientExtensionTest { - @Test - void verify_registration() { - var registry = new AuthorizationHandlerRegistryImpl(); - registry.register("alias", mock(AuthorizationHandler.class)); + @BeforeEach + void setup(ServiceExtensionContext context) { + + } - assertThat(registry.getHandler("alias")).isNotNull(); + @Test + void initialize(DimOauthClientExtension extension) { + assertThat(extension.oauth2Client()).isInstanceOf(DimOauthClientImpl.class); } + + } diff --git a/edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtensionTest.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureServiceExtensionTest.java similarity index 55% rename from edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtensionTest.java rename to edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureServiceExtensionTest.java index 621190751..f4c5a9b62 100644 --- a/edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtensionTest.java +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureServiceExtensionTest.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,43 +17,40 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.edr.store.sql; +package org.eclipse.tractusx.edc.iam.iatp.sts.dim; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.spi.system.configuration.Config; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry.DEFAULT_DATASOURCE; -import static org.eclipse.tractusx.edc.edr.store.sql.SqlEndpointDataReferenceCacheExtension.DATASOURCE_SETTING_NAME; -import static org.mockito.ArgumentMatchers.any; +import static org.eclipse.tractusx.edc.iam.iatp.sts.dim.DimSecureTokenServiceExtension.DIM_URL; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(DependencyInjectionExtension.class) -public class SqlEndpointDataReferenceCacheExtensionTest { - - @BeforeEach - void setUp(ServiceExtensionContext context) { - context.registerService(TypeManager.class, new TypeManager()); - context.registerService(DataSourceRegistry.class, mock()); - } +public class DimSecureServiceExtensionTest { @Test - void shouldInitializeTheStore(ServiceExtensionContext context, SqlEndpointDataReferenceCacheExtension extension) { + void initialize(ServiceExtensionContext context, DimSecureTokenServiceExtension extension) { var config = mock(Config.class); when(context.getConfig()).thenReturn(config); - when(config.getString(any(), any())).thenReturn(DEFAULT_DATASOURCE); - - var cache = extension.edrCache(context); + when(config.getString(DIM_URL, null)).thenReturn("url"); + assertThat(extension.secureTokenService(context)).isInstanceOf(DimSecureTokenService.class); + } - assertThat(cache).isInstanceOf(SqlEndpointDataReferenceCache.class); - verify(config).getString(DATASOURCE_SETTING_NAME, DEFAULT_DATASOURCE); + @Test + void initialize_shouldNotThrow_whenUrlIsMissing(ServiceExtensionContext context, DimSecureTokenServiceExtension extension) { + var monitor = context.getMonitor(); + var prefixeMonitor = mock(Monitor.class); + when(monitor.withPrefix(anyString())).thenReturn(prefixeMonitor); + extension.secureTokenService(context); + verify(prefixeMonitor).severe("Mandatory config value missing: 'edc.iam.sts.dim.url'. This runtime will not be fully operational! Starting with v0.7.x this will be a runtime error."); } + } diff --git a/edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureTokenServiceTest.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureTokenServiceTest.java new file mode 100644 index 000000000..5861d00eb --- /dev/null +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimSecureTokenServiceTest.java @@ -0,0 +1,298 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.iam.iatp.sts.dim; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.Buffer; +import org.eclipse.edc.spi.iam.TokenRepresentation; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.tractusx.edc.iam.iatp.sts.dim.oauth.DimOauth2Client; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.invocation.InvocationOnMock; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.http.client.testfixtures.HttpTestUtils.testHttpClient; +import static org.eclipse.edc.iam.identitytrust.spi.SelfIssuedTokenConstants.PRESENTATION_TOKEN_CLAIM; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SUBJECT; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DimSecureTokenServiceTest { + + static final String DIM_URL = "http://localhost:8080/iatp"; + private final Monitor monitor = mock(Monitor.class); + private final DimOauth2Client oauth2Client = mock(DimOauth2Client.class); + private final ObjectMapper mapper = new ObjectMapper(); + private final Interceptor interceptor = mock(Interceptor.class); + private DimSecureTokenService client; + + @BeforeEach + void setup() { + client = new DimSecureTokenService(testHttpClient(interceptor), DIM_URL, oauth2Client, mapper, monitor); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Test + void createToken_grantAccess() throws IOException { + var response = Map.of("jwt", "responseToken"); + Consumer requestAcceptor = (request) -> { + + var expectedBody = Map.of( + "consumerDid", "issuer", + "scope", "read", + "credentialTypes", List.of("TestCredential"), + "providerDid", "audience"); + var body = getBody(request, new TypeReference>() { + }); + + assertThat(body).extractingByKey("grantAccess").satisfies(payload -> { + assertThat(((Map) payload)).containsAllEntriesOf(expectedBody); + }); + + assertThat(request.url().url().toString()).isEqualTo(DIM_URL); + + }; + + when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("token").build())); + when(interceptor.intercept(isA(Interceptor.Chain.class))) + .thenAnswer(invocation -> createResponse(200, invocation, requestAcceptor, response)); + + var input = Map.of(ISSUER, "issuer", AUDIENCE, "audience"); + var result = client.createToken(input, "namespace:TestCredential:read"); + + + assertThat(result).isSucceeded() + .extracting(TokenRepresentation::getToken) + .isEqualTo("responseToken"); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Test + void createToken_signToken() throws IOException { + var response = Map.of("jwt", "responseToken"); + Consumer requestAcceptor = (request) -> { + var expectedBody = Map.of( + "issuer", "issuer", + "audience", "audience", + "subject", "issuer", + "token", "accessToken"); + var body = getBody(request, new TypeReference>() { + }); + + assertThat(body).extractingByKey("signToken").satisfies(payload -> { + assertThat(((Map) payload)).containsAllEntriesOf(expectedBody); + }); + + assertThat(request.url().url().toString()).isEqualTo(DIM_URL); + + }; + + when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("token").build())); + when(interceptor.intercept(isA(Interceptor.Chain.class))) + .thenAnswer(invocation -> createResponse(200, invocation, requestAcceptor, response)); + + var input = Map.of(ISSUER, "issuer", SUBJECT, "issuer", AUDIENCE, "audience", PRESENTATION_TOKEN_CLAIM, "accessToken"); + var result = client.createToken(input, null); + + assertThat(result).isSucceeded() + .extracting(TokenRepresentation::getToken) + .isEqualTo("responseToken"); + } + + @Test + void createToken_grantFails_withDimFailure() throws IOException { + + when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("token").build())); + when(interceptor.intercept(isA(Interceptor.Chain.class))) + .thenAnswer(invocation -> createResponse(500, invocation)); + + var input = Map.of(ISSUER, "issuer", AUDIENCE, "audience"); + var result = client.createToken(input, "namespace:TestCredential:read"); + + assertThat(result).isFailed() + .satisfies(failure -> assertThat(failure.getFailureDetail()).contains("grantAccess")); + + } + + @Test + void createToken_grantFails_whenClaimsMissing() throws IOException { + + when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("token").build())); + when(interceptor.intercept(isA(Interceptor.Chain.class))) + .thenAnswer(invocation -> createResponse(500, invocation)); + + var input = Map.of("foo", "bar"); + var result = client.createToken(input, "namespace:TestCredential:read"); + + assertThat(result).isFailed() + .satisfies(failure -> assertThat(failure.getFailureDetail()) + .contains("Key iss not found") + .contains("Key aud not found")); + + } + + @Test + void createToken_grantFails_whenBadResponsePayload() throws IOException { + + when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("token").build())); + when(interceptor.intercept(isA(Interceptor.Chain.class))) + .thenAnswer(invocation -> createResponse(200, invocation, Map.of())); + + var input = Map.of(ISSUER, "issuer", AUDIENCE, "audience"); + var result = client.createToken(input, "namespace:TestCredential:read"); + + assertThat(result).isFailed() + .satisfies(failure -> assertThat(failure.getFailureDetail()) + .contains("[grantAccess] Failed to get jwt field")); + + } + + @Test + void createToken_grantFails_whenInvalidScope() throws IOException { + + when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("token").build())); + when(interceptor.intercept(isA(Interceptor.Chain.class))) + .thenAnswer(invocation -> createResponse(500, invocation)); + + var input = Map.of(ISSUER, "issuer", AUDIENCE, "audience"); + var result = client.createToken(input, "invalidScope"); + + assertThat(result).isFailed() + .satisfies(failure -> assertThat(failure.getFailureDetail()) + .contains("Scope string invalidScope has invalid format")); + + } + + @Test + void createToken_signFails_withDimFailure() throws IOException { + + when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("token").build())); + when(interceptor.intercept(isA(Interceptor.Chain.class))) + .thenAnswer(invocation -> createResponse(500, invocation)); + + var input = Map.of(ISSUER, "issuer", SUBJECT, "issuer", AUDIENCE, "audience", PRESENTATION_TOKEN_CLAIM, "token"); + var result = client.createToken(input, null); + + assertThat(result).isFailed() + .satisfies(failure -> assertThat(failure.getFailureDetail()).contains("signToken")); + + } + + @Test + void createToken_signFails_whenBadResponsePayload() throws IOException { + + when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("token").build())); + when(interceptor.intercept(isA(Interceptor.Chain.class))) + .thenAnswer(invocation -> createResponse(200, invocation, Map.of())); + + var input = Map.of(ISSUER, "issuer", SUBJECT, "issuer", AUDIENCE, "audience", PRESENTATION_TOKEN_CLAIM, "token"); + var result = client.createToken(input, null); + + assertThat(result).isFailed() + .satisfies(failure -> assertThat(failure.getFailureDetail()) + .contains("[signToken] Failed to get jwt field")); + + } + + @Test + void createToken_signFails_whenClaimsMissing() throws IOException { + + when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("token").build())); + when(interceptor.intercept(isA(Interceptor.Chain.class))) + .thenAnswer(invocation -> createResponse(500, invocation)); + + var input = Map.of("foo", "bar"); + var result = client.createToken(input, null); + + assertThat(result).isFailed() + .satisfies(failure -> assertThat(failure.getFailureDetail()) + .contains("Key iss not found") + .contains("Key sub not found") + .contains("Key token not found") + .contains("Key aud not found")); + + } + + private Response createResponse(int code, InvocationOnMock invocation) { + return createResponse(code, invocation, (req) -> { + }, null); + } + + private Response createResponse(int code, InvocationOnMock invocation, Object body) { + return createResponse(code, invocation, (req) -> { + }, body); + } + + + private Response createResponse(int code, InvocationOnMock invocation, Consumer consumer, Object body) { + var bodyString = Optional.ofNullable(body).map(this::toJson).orElse(""); + var request = getRequest(invocation); + consumer.accept(request); + return new Response.Builder() + .protocol(Protocol.HTTP_1_1) + .request(request) + .code(code) + .message("") + .body(ResponseBody.create(bodyString, MediaType.parse("application/json"))) + .build(); + } + + private String toJson(Object body) { + try { + return mapper.writeValueAsString(body); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + private T getBody(Request request, TypeReference typeReference) { + try (var buffer = new Buffer()) { + Objects.requireNonNull(request.body()).writeTo(buffer); + return mapper.readValue(buffer.inputStream(), typeReference); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private Request getRequest(InvocationOnMock invocation) { + return invocation.getArgument(0, Interceptor.Chain.class).request(); + } +} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwApiClientExtensionTest.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimStsConfigurationExtensionTest.java similarity index 50% rename from edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwApiClientExtensionTest.java rename to edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimStsConfigurationExtensionTest.java index 9c2cbf9da..62ac06d85 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwApiClientExtensionTest.java +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/DimStsConfigurationExtensionTest.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,44 +17,38 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.miw; +package org.eclipse.tractusx.edc.iam.iatp.sts.dim; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClient; -import org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl; -import org.eclipse.tractusx.edc.iam.ssi.miw.config.SsiMiwConfiguration; -import org.junit.jupiter.api.BeforeEach; +import org.eclipse.edc.spi.system.configuration.Config; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.tractusx.edc.iam.iatp.sts.dim.DimStsConfigurationExtension.CLIENT_ID; +import static org.eclipse.tractusx.edc.iam.iatp.sts.dim.DimStsConfigurationExtension.CLIENT_SECRET_ALIAS; +import static org.eclipse.tractusx.edc.iam.iatp.sts.dim.DimStsConfigurationExtension.TOKEN_URL; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(DependencyInjectionExtension.class) -public class SsiMiwApiClientExtensionTest { - - private final SsiMiwConfiguration cfg = mock(SsiMiwConfiguration.class); - - @BeforeEach - void setup(ServiceExtensionContext context) { - context.registerService(SsiMiwConfiguration.class, cfg); - context.registerService(MiwApiClient.class, mock(MiwApiClient.class)); - context.registerService(TypeManager.class, new TypeManager()); - } +public class DimStsConfigurationExtensionTest { @Test - void initialize(ServiceExtensionContext context, SsiMiwApiClientExtension extension) { - when(cfg.getUrl()).thenReturn("http://localhost"); - when(cfg.getAuthorityId()).thenReturn("id"); - - assertThat(extension.apiClient(context)).isInstanceOf(MiwApiClientImpl.class); + void initialize(ServiceExtensionContext context, DimStsConfigurationExtension extension) { + var config = mock(Config.class); + when(context.getConfig()).thenReturn(config); + when(config.getString(TOKEN_URL, null)).thenReturn("url"); + when(config.getString(CLIENT_ID, null)).thenReturn("clientId"); + when(config.getString(CLIENT_SECRET_ALIAS, null)).thenReturn("clientSecretAlias"); + + assertThat(extension.clientConfiguration(context)).satisfies(stsConfig -> { + assertThat(stsConfig.clientId()).isEqualTo("clientId"); + assertThat(stsConfig.clientSecretAlias()).isEqualTo("clientSecretAlias"); + assertThat(stsConfig.tokenUrl()).isEqualTo("url"); + }); + } - verify(cfg).getUrl(); - verify(cfg).getAuthorityId(); - } } diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2ClientImplTest.java b/edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/oauth/DimOauthClientImplTest.java similarity index 65% rename from edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2ClientImplTest.java rename to edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/oauth/DimOauthClientImplTest.java index 4444edcea..9e48d50fa 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2ClientImplTest.java +++ b/edc-extensions/iatp/tx-iatp-sts-dim/src/test/java/org/eclipse/tractusx/edc/iam/iatp/sts/dim/oauth/DimOauthClientImplTest.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,12 +17,14 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.miw.oauth2; +package org.eclipse.tractusx.edc.iam.iatp.sts.dim.oauth; import org.eclipse.edc.iam.oauth2.spi.client.Oauth2Client; import org.eclipse.edc.iam.oauth2.spi.client.SharedSecretOauth2CredentialsRequest; import org.eclipse.edc.spi.iam.TokenRepresentation; import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.tractusx.edc.iam.iatp.sts.dim.StsRemoteClientConfiguration; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -32,22 +34,19 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class MiwOauth2ClientImplTest { - - Oauth2Client oauth2Client = mock(Oauth2Client.class); +public class DimOauthClientImplTest { + + private final Oauth2Client oauth2Client = mock(Oauth2Client.class); + + private final Vault vault = mock(Vault.class); @Test void obtainRequestToken() { - var config = MiwOauth2ClientConfiguration.Builder.newInstance() - .tokenUrl("http://localhost:8081/token") - .clientId("client_id") - .scope("scope") - .clientSecret("client_secret") - .build(); - + var config = new StsRemoteClientConfiguration("http://localhost:8081/token", "clientId", "client_secret_alias"); var tokenRepresentation = TokenRepresentation.Builder.newInstance().token("token").build(); + when(vault.resolveSecret("client_secret_alias")).thenReturn("client_secret"); when(oauth2Client.requestToken(any())).thenReturn(Result.success(tokenRepresentation)); - var client = new MiwOauth2ClientImpl(oauth2Client, config); + var client = new DimOauthClientImpl(oauth2Client, vault, config); var response = client.obtainRequestToken(); assertThat(response).isNotNull().extracting(Result::getContent).isEqualTo(tokenRepresentation); @@ -57,24 +56,18 @@ void obtainRequestToken() { var request = captor.getValue(); - assertThat(request.getClientId()).isEqualTo(config.getClientId()); - assertThat(request.getClientSecret()).isEqualTo(config.getClientSecret()); - assertThat(request.getScope()).isEqualTo(config.getScope()); - assertThat(request.getUrl()).isEqualTo(config.getTokenUrl()); + assertThat(request.getClientId()).isEqualTo(config.clientId()); + assertThat(request.getClientSecret()).isEqualTo("client_secret"); + assertThat(request.getUrl()).isEqualTo(config.tokenUrl()); } @Test void obtainRequestToken_failed() { - var config = MiwOauth2ClientConfiguration.Builder.newInstance() - .tokenUrl("http://localhost:8081/token") - .clientId("client_id") - .scope("scope") - .clientSecret("client_secret") - .build(); + var config = new StsRemoteClientConfiguration("http://localhost:8081/token", "clientId", "client_secret"); when(oauth2Client.requestToken(any())).thenReturn(Result.failure("failure")); - var client = new MiwOauth2ClientImpl(oauth2Client, config); + var client = new DimOauthClientImpl(oauth2Client, vault, config); var response = client.obtainRequestToken(); assertThat(response).isNotNull().matches(Result::failed); diff --git a/edc-extensions/iatp/tx-iatp/build.gradle.kts b/edc-extensions/iatp/tx-iatp/build.gradle.kts index af54a6311..ebeda88ae 100644 --- a/edc-extensions/iatp/tx-iatp/build.gradle.kts +++ b/edc-extensions/iatp/tx-iatp/build.gradle.kts @@ -25,8 +25,12 @@ plugins { dependencies { implementation(libs.edc.spi.core) implementation(libs.edc.spi.policyengine) - implementation(libs.edc.identity.core.trust) + implementation(libs.edc.spi.identitytrust) + implementation(libs.edc.spi.contract) + implementation(libs.edc.spi.transfer) + implementation(libs.edc.spi.catalog) implementation(project(":spi:core-spi")) + implementation(project(":core:core-utils")) testImplementation(libs.edc.junit) } diff --git a/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/IatpDefaultScopeExtension.java b/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/IatpDefaultScopeExtension.java index 3f5c8cec4..5f48e5226 100644 --- a/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/IatpDefaultScopeExtension.java +++ b/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/IatpDefaultScopeExtension.java @@ -33,9 +33,6 @@ import java.util.stream.Collectors; import static java.lang.String.format; -import static org.eclipse.edc.iam.identitytrust.core.IatpScopeExtractorExtension.CATALOG_REQUEST_SCOPE; -import static org.eclipse.edc.iam.identitytrust.core.IatpScopeExtractorExtension.NEGOTIATION_REQUEST_SCOPE; -import static org.eclipse.edc.iam.identitytrust.core.IatpScopeExtractorExtension.TRANSFER_PROCESS_REQUEST_SCOPE; import static org.eclipse.tractusx.edc.iam.iatp.IatpDefaultScopeExtension.NAME; import static org.eclipse.tractusx.edc.iam.iatp.TxIatpConstants.DEFAULT_SCOPES; @@ -54,9 +51,10 @@ public class IatpDefaultScopeExtension implements ServiceExtension { @Setting(context = TX_IATP_DEFAULT_SCOPE_PREFIX_CONFIG_ALIAS, value = "The alias of the scope e.g. read", required = true) public static final String OPERATION = "operation"; - + public static final String CATALOG_REQUEST_SCOPE = "request.catalog"; + public static final String NEGOTIATION_REQUEST_SCOPE = "request.contract.negotiation"; + public static final String TRANSFER_PROCESS_REQUEST_SCOPE = "request.transfer.process"; static final String NAME = "Tractusx default scope extension"; - @Inject private PolicyEngine policyEngine; diff --git a/edc-extensions/ssi/ssi-identity-extractor/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/SsiIdentityExtractorExtension.java b/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/IatpIdentityExtension.java similarity index 72% rename from edc-extensions/ssi/ssi-identity-extractor/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/SsiIdentityExtractorExtension.java rename to edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/IatpIdentityExtension.java index a4e309a81..7f8213771 100644 --- a/edc-extensions/ssi/ssi-identity-extractor/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/SsiIdentityExtractorExtension.java +++ b/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/IatpIdentityExtension.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,29 +17,33 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.identity.extractor; +package org.eclipse.tractusx.edc.iam.iatp; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.spi.agent.ParticipantAgentService; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.tractusx.edc.iam.iatp.identity.IatpIdentityExtractor; -@Extension(SsiIdentityExtractorExtension.EXTENSION_NAME) -public class SsiIdentityExtractorExtension implements ServiceExtension { +import static org.eclipse.tractusx.edc.iam.iatp.IatpDefaultScopeExtension.NAME; - public static final String EXTENSION_NAME = "SSI Identity extractor"; +@Extension(NAME) +public class IatpIdentityExtension implements ServiceExtension { + + static final String NAME = "Tractusx IATP identity extension"; @Inject private ParticipantAgentService participantAgentService; @Override public String name() { - return EXTENSION_NAME; + return NAME; } @Override public void initialize(ServiceExtensionContext context) { - participantAgentService.register(new CredentialIdentityExtractor()); + participantAgentService.register(new IatpIdentityExtractor()); } + } diff --git a/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/IatpScopeExtractorExtension.java b/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/IatpScopeExtractorExtension.java index 2cd451e1a..4963aa4b9 100644 --- a/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/IatpScopeExtractorExtension.java +++ b/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/IatpScopeExtractorExtension.java @@ -19,9 +19,10 @@ package org.eclipse.tractusx.edc.iam.iatp; -import org.eclipse.edc.identitytrust.scope.ScopeExtractorRegistry; +import org.eclipse.edc.iam.identitytrust.spi.scope.ScopeExtractorRegistry; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.tractusx.edc.iam.iatp.scope.CredentialScopeExtractor; @@ -36,6 +37,9 @@ public class IatpScopeExtractorExtension implements ServiceExtension { @Inject private ScopeExtractorRegistry scopeExtractorRegistry; + @Inject + private Monitor monitor; + @Override public String name() { return NAME; @@ -43,6 +47,6 @@ public String name() { @Override public void initialize(ServiceExtensionContext context) { - scopeExtractorRegistry.registerScopeExtractor(new CredentialScopeExtractor()); + scopeExtractorRegistry.registerScopeExtractor(new CredentialScopeExtractor(monitor)); } } diff --git a/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/identity/IatpIdentityExtractor.java b/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/identity/IatpIdentityExtractor.java new file mode 100644 index 000000000..59754f3c1 --- /dev/null +++ b/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/identity/IatpIdentityExtractor.java @@ -0,0 +1,89 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.iam.iatp.identity; + +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.agent.ParticipantAgentServiceExtension; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.tractusx.edc.core.utils.credentials.CredentialTypePredicate; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.eclipse.edc.spi.agent.ParticipantAgent.PARTICIPANT_IDENTITY; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_CREDENTIAL_NS; + +/** + * Implementation of {@link ParticipantAgentServiceExtension} which extracts the identity of a participant + * from the MembershipCredential + */ +public class IatpIdentityExtractor implements ParticipantAgentServiceExtension { + + private static final String VC_CLAIM = "vc"; + private static final String IDENTITY_CREDENTIAL = "MembershipCredential"; + private static final String IDENTITY_PROPERTY = "holderIdentifier"; + + private final CredentialTypePredicate typePredicate = new CredentialTypePredicate(CX_CREDENTIAL_NS, IDENTITY_CREDENTIAL); + + @Override + public @NotNull Map attributesFor(ClaimToken claimToken) { + var credentials = getCredentialList(claimToken) + .orElseThrow(failure -> new EdcException("Failed to fetch credentials from the claim token: %s".formatted(failure.getFailureDetail()))); + + return credentials.stream() + .filter(typePredicate) + .findFirst() + .flatMap(this::getIdentifier) + .map(identity -> Map.of(PARTICIPANT_IDENTITY, identity)) + .orElseThrow(() -> new EdcException("Failed to fetch %s property from %s credential".formatted(IDENTITY_PROPERTY, IDENTITY_CREDENTIAL))); + + + } + + private Optional getIdentifier(VerifiableCredential verifiableCredential) { + return verifiableCredential.getCredentialSubject().stream() + .flatMap(credentialSubject -> credentialSubject.getClaims().entrySet().stream()) + .filter(entry -> entry.getKey().endsWith(IDENTITY_PROPERTY)) + .map(Map.Entry::getValue) + .map(String.class::cast) + .findFirst(); + } + + @SuppressWarnings("unchecked") + private Result> getCredentialList(ClaimToken claimToken) { + var vcListClaim = claimToken.getClaims().get(VC_CLAIM); + + if (vcListClaim == null) { + return Result.failure("ClaimToken did not contain a '%s' claim.".formatted(VC_CLAIM)); + } + if (!(vcListClaim instanceof List)) { + return Result.failure("ClaimToken contains a '%s' claim, but the type is incorrect. Expected %s, got %s.".formatted(VC_CLAIM, List.class.getName(), vcListClaim.getClass().getName())); + } + var vcList = (List) vcListClaim; + if (vcList.isEmpty()) { + return Result.failure("ClaimToken contains a '%s' claim but it did not contain any VerifiableCredentials.".formatted(VC_CLAIM)); + } + return Result.success(vcList); + } +} diff --git a/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/scope/CredentialScopeExtractor.java b/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/scope/CredentialScopeExtractor.java index a1f0ec91d..8721e4616 100644 --- a/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/scope/CredentialScopeExtractor.java +++ b/edc-extensions/iatp/tx-iatp/src/main/java/org/eclipse/tractusx/edc/iam/iatp/scope/CredentialScopeExtractor.java @@ -19,43 +19,94 @@ package org.eclipse.tractusx.edc.iam.iatp.scope; -import org.eclipse.edc.identitytrust.scope.ScopeExtractor; +import org.eclipse.edc.connector.controlplane.catalog.spi.CatalogRequestMessage; +import org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractRequestMessage; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.protocol.TransferRequestMessage; +import org.eclipse.edc.iam.identitytrust.spi.scope.ScopeExtractor; import org.eclipse.edc.policy.engine.spi.PolicyContext; import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.spi.iam.RequestContext; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.types.domain.message.RemoteMessage; +import java.util.Optional; import java.util.Set; -import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_CREDENTIAL_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_POLICY_NS; import static org.eclipse.tractusx.edc.iam.iatp.TxIatpConstants.CREDENTIAL_TYPE_NAMESPACE; /** * Extract credentials from the policy constraints *

* Extract and map from the Credential DSL the required credential type - * The left operand should be bound to the namespace {@link org.eclipse.tractusx.edc.edr.spi.CoreConstants#TX_CREDENTIAL_NAMESPACE} + * The left operand should be bound to the namespace {@link org.eclipse.tractusx.edc.edr.spi.CoreConstants#CX_CREDENTIAL_NS} */ public class CredentialScopeExtractor implements ScopeExtractor { - public static final String FRAMEWORK_CREDENTIAL_PREFIX = "FrameworkAgreement."; + public static final String FRAMEWORK_CREDENTIAL_PREFIX = "FrameworkAgreement"; public static final String SCOPE_FORMAT = "%s:%s:read"; public static final String CREDENTIAL_FORMAT = "%sCredential"; - public CredentialScopeExtractor() { + private static final Set> SUPPORTED_MESSAGES = Set.of(CatalogRequestMessage.class, ContractRequestMessage.class, TransferRequestMessage.class); + + private final Monitor monitor; + + public CredentialScopeExtractor(Monitor monitor) { + this.monitor = monitor; } @Override public Set extractScopes(Object leftValue, Operator operator, Object rightValue, PolicyContext context) { Set scopes = Set.of(); - if (leftValue instanceof String leftOperand && leftOperand.startsWith(TX_CREDENTIAL_NAMESPACE)) { - var credential = leftOperand.replace(TX_CREDENTIAL_NAMESPACE, ""); - if (credential.startsWith(FRAMEWORK_CREDENTIAL_PREFIX)) { - credential = credential.replace(FRAMEWORK_CREDENTIAL_PREFIX, ""); + + var requestContext = context.getContextData(RequestContext.class); + + if (requestContext != null) { + if (leftValue instanceof String leftOperand && leftOperand.startsWith(CX_POLICY_NS) && isMessageSupported(requestContext)) { + leftOperand = leftOperand.replace(CX_POLICY_NS, ""); + var credentialType = extractCredentialType(leftOperand, rightValue); + scopes = Set.of(SCOPE_FORMAT.formatted(CREDENTIAL_TYPE_NAMESPACE, CREDENTIAL_FORMAT.formatted(capitalize(credentialType)))); + } - scopes = Set.of(SCOPE_FORMAT.formatted(CREDENTIAL_TYPE_NAMESPACE, CREDENTIAL_FORMAT.formatted(capitalize(credential)))); + } else { + monitor.warning("RequestContext not found in the PolicyContext: scope cannot be extracted from the policy. Defaulting to empty scopes"); } return scopes; } + private boolean isMessageSupported(RequestContext ctx) { + return Optional.ofNullable(ctx.getMessage()) + .map(RemoteMessage::getClass) + .map(SUPPORTED_MESSAGES::contains) + .orElse(false); + } + + /** + * Possible values for credential: + *

    + *
  • FrameworkAgreement -> subtype is encoded in rightValue, return subtype from rightOperand
  • + *
  • FrameworkAgreement.[subtype] -> return subtype
  • + *
  • Dismantler -> return "Dismantler"
  • + *
  • Dismantler.[expr] -> return "Dismantler"
  • + *
  • Membership -> return "Membership"
  • + *
+ */ + private String extractCredentialType(String leftOperand, Object rightValue) { + if (leftOperand.equals(FRAMEWORK_CREDENTIAL_PREFIX)) { //this is the "new" notation, where the subtype is encoded in the right operand + var rightOperand = rightValue.toString(); + var ix = rightOperand.indexOf(":"); + return ix > 0 ? rightOperand.substring(0, ix) : rightOperand; + } + // for FrameworkAgreement.xyz we need the "xyz" part + if (leftOperand.startsWith(FRAMEWORK_CREDENTIAL_PREFIX + ".")) { + leftOperand = leftOperand.replace(FRAMEWORK_CREDENTIAL_PREFIX + ".", ""); + } else { //for all others, e.g. Dismantler.activityType, we only need the "Dismantler" part + var ix = leftOperand.indexOf("."); + leftOperand = ix > 0 ? leftOperand.substring(0, ix) : leftOperand; + } + return leftOperand; + } + private String capitalize(String input) { return input.substring(0, 1).toUpperCase() + input.substring(1).toLowerCase(); } diff --git a/edc-extensions/iatp/tx-iatp/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/iatp/tx-iatp/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 8441c54b3..0d9cdbce5 100644 --- a/edc-extensions/iatp/tx-iatp/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/iatp/tx-iatp/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -18,5 +18,6 @@ ################################################################################# org.eclipse.tractusx.edc.iam.iatp.IatpDefaultScopeExtension +org.eclipse.tractusx.edc.iam.iatp.IatpIdentityExtension org.eclipse.tractusx.edc.iam.iatp.IatpScopeExtractorExtension diff --git a/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/IatpDefaultScopeExtensionTest.java b/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/IatpDefaultScopeExtensionTest.java index 7c7714424..70cb93136 100644 --- a/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/IatpDefaultScopeExtensionTest.java +++ b/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/IatpDefaultScopeExtensionTest.java @@ -34,9 +34,9 @@ import java.util.Set; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.eclipse.edc.iam.identitytrust.core.IatpScopeExtractorExtension.CATALOG_REQUEST_SCOPE; -import static org.eclipse.edc.iam.identitytrust.core.IatpScopeExtractorExtension.NEGOTIATION_REQUEST_SCOPE; -import static org.eclipse.edc.iam.identitytrust.core.IatpScopeExtractorExtension.TRANSFER_PROCESS_REQUEST_SCOPE; +import static org.eclipse.tractusx.edc.iam.iatp.IatpDefaultScopeExtension.CATALOG_REQUEST_SCOPE; +import static org.eclipse.tractusx.edc.iam.iatp.IatpDefaultScopeExtension.NEGOTIATION_REQUEST_SCOPE; +import static org.eclipse.tractusx.edc.iam.iatp.IatpDefaultScopeExtension.TRANSFER_PROCESS_REQUEST_SCOPE; import static org.eclipse.tractusx.edc.iam.iatp.IatpDefaultScopeExtension.TX_IATP_DEFAULT_SCOPE_PREFIX; import static org.eclipse.tractusx.edc.iam.iatp.TxIatpConstants.DEFAULT_SCOPES; import static org.mockito.ArgumentMatchers.argThat; diff --git a/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/SsiIdentityExtractorExtensionTest.java b/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/IatpIdentityExtensionTest.java similarity index 76% rename from edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/SsiIdentityExtractorExtensionTest.java rename to edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/IatpIdentityExtensionTest.java index de3799312..71e45ab12 100644 --- a/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/SsiIdentityExtractorExtensionTest.java +++ b/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/IatpIdentityExtensionTest.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,11 +17,12 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.identity.extractor; +package org.eclipse.tractusx.edc.iam.iatp; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.agent.ParticipantAgentService; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.tractusx.edc.iam.iatp.identity.IatpIdentityExtractor; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -31,9 +32,9 @@ import static org.mockito.Mockito.verify; @ExtendWith(DependencyInjectionExtension.class) -public class SsiIdentityExtractorExtensionTest { +public class IatpIdentityExtensionTest { - ParticipantAgentService participantAgentService = mock(ParticipantAgentService.class); + private final ParticipantAgentService participantAgentService = mock(); @BeforeEach void setup(ServiceExtensionContext context) { @@ -41,8 +42,9 @@ void setup(ServiceExtensionContext context) { } @Test - void initialize(ServiceExtensionContext context, SsiIdentityExtractorExtension extension) { + void initialize(ServiceExtensionContext context, IatpIdentityExtension extension) { extension.initialize(context); - verify(participantAgentService).register(isA(CredentialIdentityExtractor.class)); + verify(participantAgentService).register(isA(IatpIdentityExtractor.class)); } + } diff --git a/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/IatpScopeExtractorExtensionTest.java b/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/IatpScopeExtractorExtensionTest.java index 0cd4b6c7d..d75d366ef 100644 --- a/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/IatpScopeExtractorExtensionTest.java +++ b/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/IatpScopeExtractorExtensionTest.java @@ -19,7 +19,7 @@ package org.eclipse.tractusx.edc.iam.iatp; -import org.eclipse.edc.identitytrust.scope.ScopeExtractorRegistry; +import org.eclipse.edc.iam.identitytrust.spi.scope.ScopeExtractorRegistry; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.tractusx.edc.iam.iatp.scope.CredentialScopeExtractor; diff --git a/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/identity/IatpIdentityExtractorTest.java b/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/identity/IatpIdentityExtractorTest.java new file mode 100644 index 000000000..5e2727dea --- /dev/null +++ b/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/identity/IatpIdentityExtractorTest.java @@ -0,0 +1,110 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.iam.iatp.identity; + +import org.eclipse.edc.iam.verifiablecredentials.spi.model.CredentialSubject; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.Issuer; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.eclipse.edc.spi.agent.ParticipantAgent.PARTICIPANT_IDENTITY; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_CREDENTIAL_NS; + +class IatpIdentityExtractorTest { + + private static final String IDENTITY = "identity"; + private final IatpIdentityExtractor extractor = new IatpIdentityExtractor(); + + private static VerifiableCredential vc(String type, Map claims) { + return VerifiableCredential.Builder.newInstance().type(type) + .issuanceDate(Instant.now()) + .issuer(new Issuer("issuer", Map.of())) + .credentialSubject(CredentialSubject.Builder.newInstance().claims(claims).build()) + .build(); + } + + @ParameterizedTest + @ArgumentsSource(VerifiableCredentialArgumentProvider.class) + void attributesFor(VerifiableCredential credential) { + var attributes = extractor.attributesFor(ClaimToken.Builder.newInstance().claim("vc", List.of(credential)).build()); + assertThat(attributes).containsEntry(PARTICIPANT_IDENTITY, IDENTITY); + } + + @Test + void attributesFor_Fails_WhenCredentialNotFound() { + assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().claim("vc", List.of(vc("FooCredential", Map.of("foo", "bar")))).build())) + .isInstanceOf(EdcException.class) + .hasMessageContaining("Failed to fetch"); + } + + @Test + void attributesFor_Fails_whenNoVcClaims() { + assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().build())) + .isInstanceOf(EdcException.class) + .hasMessageContaining("Failed to fetch credentials from the claim token: ClaimToken did not contain a 'vc' claim"); + } + + @Test + void attributesFor_Fails_whenNullVcClaims() { + + assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().claim("vc", null).build())) + .isInstanceOf(EdcException.class) + .hasMessageContaining("Failed to fetch credentials from the claim token: ClaimToken did not contain a 'vc' claim"); + } + + @Test + void attributesFor_Fails_WhenVcClaimIsNotList() { + assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().claim("vc", "wrong").build())) + .isInstanceOf(EdcException.class) + .hasMessageContaining("Failed to fetch credentials from the claim token: ClaimToken contains a 'vc' claim, but the type is incorrect. Expected java.util.List, got java.lang.String."); + } + + @Test + void attributesFor_Fails_WhenVcClaimsIsEmptyList() { + assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().claim("vc", List.of()).build())) + .isInstanceOf(EdcException.class) + .hasMessageContaining("Failed to fetch credentials from the claim token: ClaimToken contains a 'vc' claim but it did not contain any VerifiableCredentials."); + } + + private static class VerifiableCredentialArgumentProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + Arguments.of(vc("MembershipCredential", Map.of("holderIdentifier", IDENTITY))), + Arguments.of(vc(CX_CREDENTIAL_NS + "MembershipCredential", Map.of("holderIdentifier", IDENTITY))), + Arguments.of(vc(CX_CREDENTIAL_NS + "MembershipCredential", Map.of(CX_CREDENTIAL_NS + "holderIdentifier", IDENTITY)))); + } + + } +} diff --git a/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/scope/CredentialScopeExtractorTest.java b/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/scope/CredentialScopeExtractorTest.java index f20f52c0f..a89878479 100644 --- a/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/scope/CredentialScopeExtractorTest.java +++ b/edc-extensions/iatp/tx-iatp/src/test/java/org/eclipse/tractusx/edc/iam/iatp/scope/CredentialScopeExtractorTest.java @@ -19,33 +19,69 @@ package org.eclipse.tractusx.edc.iam.iatp.scope; +import org.eclipse.edc.connector.controlplane.catalog.spi.CatalogRequestMessage; +import org.eclipse.edc.connector.controlplane.contract.spi.types.agreement.ContractAgreementMessage; +import org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractNegotiationTerminationMessage; +import org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractRequestMessage; +import org.eclipse.edc.connector.controlplane.contract.spi.types.offer.ContractOffer; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.protocol.TransferRequestMessage; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.protocol.TransferTerminationMessage; import org.eclipse.edc.policy.engine.spi.PolicyContextImpl; +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.spi.iam.RequestContext; import org.eclipse.edc.spi.iam.TokenParameters; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.types.domain.message.RemoteMessage; import org.eclipse.tractusx.edc.edr.spi.CoreConstants; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.tractusx.edc.iam.iatp.TxIatpConstants.CREDENTIAL_TYPE_NAMESPACE; import static org.eclipse.tractusx.edc.iam.iatp.scope.CredentialScopeExtractor.FRAMEWORK_CREDENTIAL_PREFIX; +import static org.mockito.Mockito.mock; public class CredentialScopeExtractorTest { + private final Monitor monitor = mock(); private CredentialScopeExtractor extractor; @BeforeEach void setup() { - extractor = new CredentialScopeExtractor(); + extractor = new CredentialScopeExtractor(monitor); } - @Test - void verify_extractScopes() { + @DisplayName("Scope extractor with supported messages") + @ParameterizedTest(name = "{1}") + @ArgumentsSource(SupportedMessages.class) + void verify_extractScopes(RemoteMessage message) { var builder = TokenParameters.Builder.newInstance(); - var ctx = PolicyContextImpl.Builder.newInstance().additional(TokenParameters.Builder.class, builder).build(); - var scopes = extractor.extractScopes(CoreConstants.TX_CREDENTIAL_NAMESPACE + FRAMEWORK_CREDENTIAL_PREFIX + "pfc", null, null, ctx); + var requestContext = RequestContext.Builder.newInstance().message(message).direction(RequestContext.Direction.Egress).build(); + var ctx = PolicyContextImpl.Builder.newInstance().additional(TokenParameters.Builder.class, builder).additional(RequestContext.class, requestContext).build(); + var scopes = extractor.extractScopes(CoreConstants.CX_POLICY_NS + FRAMEWORK_CREDENTIAL_PREFIX + ".pfc", null, null, ctx); assertThat(scopes).contains(CREDENTIAL_TYPE_NAMESPACE + ":PfcCredential:read"); } + + @DisplayName("Scope extractor with not supported messages") + @ParameterizedTest(name = "{1}") + @ArgumentsSource(NotSupportedMessages.class) + void verify_extractScopes_isEmpty_whenNotSupportedMessages(RemoteMessage message) { + var builder = TokenParameters.Builder.newInstance(); + var requestContext = RequestContext.Builder.newInstance().message(message).direction(RequestContext.Direction.Egress).build(); + var ctx = PolicyContextImpl.Builder.newInstance().additional(TokenParameters.Builder.class, builder).additional(RequestContext.class, requestContext).build(); + var scopes = extractor.extractScopes(CoreConstants.CX_POLICY_NS + FRAMEWORK_CREDENTIAL_PREFIX + ".pfc", null, null, ctx); + assertThat(scopes).isEmpty(); + } + @Test void verify_extractScope_Empty() { var builder = TokenParameters.Builder.newInstance(); @@ -53,4 +89,27 @@ void verify_extractScope_Empty() { var scopes = extractor.extractScopes("wrong", null, null, ctx); assertThat(scopes).isEmpty(); } + + private static class SupportedMessages implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) { + var offer = ContractOffer.Builder.newInstance().id("id").assetId("assetId").policy(Policy.Builder.newInstance().build()).build(); + return Stream.of( + Arguments.of(CatalogRequestMessage.Builder.newInstance().build()), + Arguments.of(ContractRequestMessage.Builder.newInstance().contractOffer(offer).callbackAddress("cb").build()), + Arguments.of(TransferRequestMessage.Builder.newInstance().callbackAddress("cb").build()) + ); + } + } + + private static class NotSupportedMessages implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) { + return Stream.of( + Arguments.of(ContractNegotiationTerminationMessage.Builder.newInstance().build()), + Arguments.of(ContractAgreementMessage.Builder.newInstance().counterPartyAddress("cb").contractAgreement(mock()).build()), + Arguments.of(TransferTerminationMessage.Builder.newInstance().counterPartyAddress("pd").build()) + ); + } + } } diff --git a/edc-extensions/postgresql-migration/README.md b/edc-extensions/migrations/control-plane-migration/README.md similarity index 100% rename from edc-extensions/postgresql-migration/README.md rename to edc-extensions/migrations/control-plane-migration/README.md diff --git a/edc-extensions/edr/edr-cache-sql/build.gradle.kts b/edc-extensions/migrations/control-plane-migration/build.gradle.kts similarity index 60% rename from edc-extensions/edr/edr-cache-sql/build.gradle.kts rename to edc-extensions/migrations/control-plane-migration/build.gradle.kts index 8d1fde1e8..2ff1aa7fa 100644 --- a/edc-extensions/edr/edr-cache-sql/build.gradle.kts +++ b/edc-extensions/migrations/control-plane-migration/build.gradle.kts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -18,25 +18,24 @@ ********************************************************************************/ plugins { - `java-library` `maven-publish` + `java-library` } dependencies { - implementation(project(":spi:edr-spi")) - + implementation(project(":edc-extensions:migrations:postgresql-migration-lib")) implementation(libs.edc.spi.core) - implementation(libs.edc.core.sql) - implementation(libs.edc.spi.transactionspi) + implementation(libs.edc.junit) implementation(libs.edc.spi.transaction.datasource) - implementation(libs.edc.sql.lease) - - testImplementation(libs.edc.transaction.local) - - testImplementation(testFixtures(project(":spi:edr-spi"))) - testImplementation(testFixtures(libs.edc.core.sql)) - testImplementation(testFixtures(libs.edc.sql.lease)) + implementation(libs.edc.sql.assetindex) + implementation(libs.edc.sql.core) + runtimeOnly(libs.postgres) - testImplementation(testFixtures(libs.edc.junit)) + implementation(libs.flyway.core) + // starting from flyway 10, they've moved to a more modular structure, + // so we need to add PG support explicitly + // https://documentation.red-gate.com/flyway/release-notes-and-older-versions/release-notes-for-flyway-engine + runtimeOnly(libs.flyway.database.postgres) + testImplementation(testFixtures(libs.edc.sql.core)) } diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java b/edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java similarity index 100% rename from edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java rename to edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/BusinessGroupPostgresMigrationExtension.java b/edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/BusinessGroupPostgresMigrationExtension.java similarity index 100% rename from edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/BusinessGroupPostgresMigrationExtension.java rename to edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/BusinessGroupPostgresMigrationExtension.java diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java b/edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java similarity index 100% rename from edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java rename to edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java b/edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java similarity index 100% rename from edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java rename to edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java diff --git a/edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/EdrIndexPostgresqlMigrationExtension.java b/edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/EdrIndexPostgresqlMigrationExtension.java new file mode 100644 index 000000000..1e16364ae --- /dev/null +++ b/edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/EdrIndexPostgresqlMigrationExtension.java @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.postgresql.migration; + +public class EdrIndexPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "edr"; + + @Override + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } + + @Override + protected String getMigrationSubsystem() { + return NAME_SUBSYSTEM + "-index"; + } +} diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyMonitorPostgresqlMigrationExtension.java b/edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyMonitorPostgresqlMigrationExtension.java similarity index 100% rename from edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyMonitorPostgresqlMigrationExtension.java rename to edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyMonitorPostgresqlMigrationExtension.java diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java b/edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java similarity index 100% rename from edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java rename to edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java b/edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java similarity index 100% rename from edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java rename to edc-extensions/migrations/control-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java diff --git a/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/migrations/control-plane-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 95% rename from edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/migrations/control-plane-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index d89bb3f09..20a17d939 100644 --- a/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/migrations/control-plane-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -25,5 +25,5 @@ org.eclipse.tractusx.edc.postgresql.migration.ContractNegotiationPostgresqlMigra org.eclipse.tractusx.edc.postgresql.migration.PolicyPostgresqlMigrationExtension org.eclipse.tractusx.edc.postgresql.migration.PolicyMonitorPostgresqlMigrationExtension org.eclipse.tractusx.edc.postgresql.migration.TransferProcessPostgresqlMigrationExtension -org.eclipse.tractusx.edc.postgresql.migration.EdrPostgresqlMigrationExtension org.eclipse.tractusx.edc.postgresql.migration.BusinessGroupPostgresMigrationExtension +org.eclipse.tractusx.edc.postgresql.migration.EdrIndexPostgresqlMigrationExtension \ No newline at end of file diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_4__Alter_Asset_Property_add_IsPrivateFlag.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_4__Alter_Asset_Property_add_IsPrivateFlag.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_4__Alter_Asset_Property_add_IsPrivateFlag.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_4__Alter_Asset_Property_add_IsPrivateFlag.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_5__Alter_Asset_Internalize_Properties.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_5__Alter_Asset_Internalize_Properties.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_5__Alter_Asset_Internalize_Properties.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_5__Alter_Asset_Internalize_Properties.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_6__Transform_Property_Array_To_Map.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_6__Transform_Property_Array_To_Map.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_6__Transform_Property_Array_To_Map.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_6__Transform_Property_Array_To_Map.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/bpn/V0_0_1__Init_BusinessGroup_Schema.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/bpn/V0_0_1__Init_BusinessGroup_Schema.sql similarity index 91% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/bpn/V0_0_1__Init_BusinessGroup_Schema.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/bpn/V0_0_1__Init_BusinessGroup_Schema.sql index aa7b7a08f..44daef527 100644 --- a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/bpn/V0_0_1__Init_BusinessGroup_Schema.sql +++ b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/bpn/V0_0_1__Init_BusinessGroup_Schema.sql @@ -12,7 +12,7 @@ -- -CREATE TABLE edc_business_partner_group +CREATE TABLE IF NOT EXISTS edc_business_partner_group ( bpn VARCHAR NOT NULL CONSTRAINT edc_business_partner_group_pk diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_2__Alter_ContractDefinition_Access_Contract_Policy_Id_Schema.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_2__Alter_ContractDefinition_Access_Contract_Policy_Id_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_2__Alter_ContractDefinition_Access_Contract_Policy_Id_Schema.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_2__Alter_ContractDefinition_Access_Contract_Policy_Id_Schema.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_4__Snapshot_20221201_Update.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_4__Snapshot_20221201_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_4__Snapshot_20221201_Update.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_4__Snapshot_20221201_Update.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_5__Alter_ContractDefinition_Remove_validity.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_5__Alter_ContractDefinition_Remove_validity.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_5__Alter_ContractDefinition_Remove_validity.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_5__Alter_ContractDefinition_Remove_validity.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_6__Alter_ContractDefinition_Rename_selector_expression.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_6__Alter_ContractDefinition_Rename_selector_expression.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_6__Alter_ContractDefinition_Rename_selector_expression.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_6__Alter_ContractDefinition_Rename_selector_expression.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_7__Alter_ContractDefinition_Add_Private_Properties.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_7__Alter_ContractDefinition_Add_Private_Properties.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_7__Alter_ContractDefinition_Add_Private_Properties.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_7__Alter_ContractDefinition_Add_Private_Properties.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_8__Alter_ContractDefinition_Switch_Assets_Selector_Json.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_8__Alter_ContractDefinition_Switch_Assets_Selector_Json.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_8__Alter_ContractDefinition_Switch_Assets_Selector_Json.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_8__Alter_ContractDefinition_Switch_Assets_Selector_Json.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_5__Alter_ContractNegotiation_Add_Callbacks.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_5__Alter_ContractNegotiation_Add_Callbacks.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_5__Alter_ContractNegotiation_Add_Callbacks.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_5__Alter_ContractNegotiation_Add_Callbacks.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_6__Alter_ContractNegotiation_Change_Type.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_6__Alter_ContractNegotiation_Change_Type.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_6__Alter_ContractNegotiation_Change_Type.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_6__Alter_ContractNegotiation_Change_Type.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_7__Alter_ContractNegotiation_AddPendingField.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_7__Alter_ContractNegotiation_AddPendingField.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_7__Alter_ContractNegotiation_AddPendingField.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_7__Alter_ContractNegotiation_AddPendingField.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_8__Alter_ContractNegotiation_AddProtocolMessagesField.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_8__Alter_ContractNegotiation_AddProtocolMessagesField.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_8__Alter_ContractNegotiation_AddProtocolMessagesField.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_8__Alter_ContractNegotiation_AddProtocolMessagesField.sql diff --git a/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr-index/V0_0_1__Init_EdrIndex_Database_Schema.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr-index/V0_0_1__Init_EdrIndex_Database_Schema.sql new file mode 100644 index 000000000..868430225 --- /dev/null +++ b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr-index/V0_0_1__Init_EdrIndex_Database_Schema.sql @@ -0,0 +1,31 @@ +-- +-- Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +-- +-- 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 +-- +-- Contributors: +-- Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation +-- + +-- +-- table: edc_edr_entry +-- + +CREATE TABLE IF NOT EXISTS edc_edr_entry +( + transfer_process_id VARCHAR NOT NULL PRIMARY KEY, + agreement_id VARCHAR NOT NULL, + asset_id VARCHAR NOT NULL, + provider_id VARCHAR NOT NULL, + contract_negotiation_id VARCHAR, + created_at BIGINT NOT NULL +); + + +CREATE INDEX IF NOT EXISTS asset_id_index ON edc_edr_entry (asset_id); + + diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy-monitor/V0_0_1__Init_PolicyMonitor_Database_Schema.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy-monitor/V0_0_1__Init_PolicyMonitor_Database_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy-monitor/V0_0_1__Init_PolicyMonitor_Database_Schema.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy-monitor/V0_0_1__Init_PolicyMonitor_Database_Schema.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_4__Add_Private_Properties.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_4__Add_Private_Properties.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_4__Add_Private_Properties.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_4__Add_Private_Properties.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_10__Alter_TransferProcess_Rename_transfer_process_column.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_10__Alter_TransferProcess_Rename_transfer_process_column.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_10__Alter_TransferProcess_Rename_transfer_process_column.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_10__Alter_TransferProcess_Rename_transfer_process_column.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_11__Alter_TransferProcess_AddPendingField.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_11__Alter_TransferProcess_AddPendingField.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_11__Alter_TransferProcess_AddPendingField.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_11__Alter_TransferProcess_AddPendingField.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_12__Alter_TransferProcess_AddTransferType.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_12__Alter_TransferProcess_AddTransferType.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_12__Alter_TransferProcess_AddTransferType.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_12__Alter_TransferProcess_AddTransferType.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_13__Alter_TransferProcess_AddProtocolMessages.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_13__Alter_TransferProcess_AddProtocolMessages.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_13__Alter_TransferProcess_AddProtocolMessages.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_13__Alter_TransferProcess_AddProtocolMessages.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_2__Add_ProviderId_Column.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_14__Alter_TransferProcess_AddDataPlaneId.sql similarity index 67% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_2__Add_ProviderId_Column.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_14__Alter_TransferProcess_AddDataPlaneId.sql index ee20b3990..c76c88529 100644 --- a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_2__Add_ProviderId_Column.sql +++ b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_14__Alter_TransferProcess_AddDataPlaneId.sql @@ -1,5 +1,5 @@ -- --- Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +-- Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -- -- This program and the accompanying materials are made available under the -- terms of the Apache License, Version 2.0 which is available at @@ -11,9 +11,5 @@ -- Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation -- --- Statements are designed for and tested with Postgres only! - - -- add column -ALTER TABLE edc_edr_cache ADD COLUMN provider_id VARCHAR; - +ALTER TABLE edc_transfer_process ADD COLUMN data_plane_id VARCHAR; diff --git a/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_15__Alter_TransferProcess_InlineDataAddress.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_15__Alter_TransferProcess_InlineDataAddress.sql new file mode 100644 index 000000000..469bd0149 --- /dev/null +++ b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_15__Alter_TransferProcess_InlineDataAddress.sql @@ -0,0 +1,28 @@ +-- +-- Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +-- +-- 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 +-- +-- Contributors: +-- Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation +-- + +-- drop datarequest +DROP TABLE IF EXISTS edc_data_request; +-- add columns +ALTER TABLE edc_transfer_process + ADD COLUMN correlation_id VARCHAR; +ALTER TABLE edc_transfer_process + ADD COLUMN counter_party_address VARCHAR; +ALTER TABLE edc_transfer_process + ADD COLUMN protocol VARCHAR; +ALTER TABLE edc_transfer_process + ADD COLUMN asset_id VARCHAR; +ALTER TABLE edc_transfer_process + ADD COLUMN contract_id VARCHAR; +ALTER TABLE edc_transfer_process + ADD COLUMN data_destination JSON; diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_6__Snapshot_20230109_Update.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_6__Snapshot_20230109_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_6__Snapshot_20230109_Update.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_6__Snapshot_20230109_Update.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_7__Default_Value_For_Properties.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_7__Default_Value_For_Properties.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_7__Default_Value_For_Properties.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_7__Default_Value_For_Properties.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_8__Alter_TransferProcess_Add_Callbacks.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_8__Alter_TransferProcess_Add_Callbacks.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_8__Alter_TransferProcess_Add_Callbacks.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_8__Alter_TransferProcess_Add_Callbacks.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_9__Alter_TransferProcess_Remove_Transfertype.sql b/edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_9__Alter_TransferProcess_Remove_Transfertype.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_9__Alter_TransferProcess_Remove_Transfertype.sql rename to edc-extensions/migrations/control-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_9__Alter_TransferProcess_Remove_Transfertype.sql diff --git a/edc-extensions/postgresql-migration/src/test/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtensionTest.java b/edc-extensions/migrations/control-plane-migration/src/test/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtensionTest.java similarity index 89% rename from edc-extensions/postgresql-migration/src/test/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtensionTest.java rename to edc-extensions/migrations/control-plane-migration/src/test/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtensionTest.java index 29b3990dd..2d18a999e 100644 --- a/edc-extensions/postgresql-migration/src/test/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtensionTest.java +++ b/edc-extensions/migrations/control-plane-migration/src/test/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtensionTest.java @@ -19,11 +19,11 @@ package org.eclipse.tractusx.edc.postgresql.migration; -import org.eclipse.edc.connector.store.sql.assetindex.SqlAssetIndex; -import org.eclipse.edc.connector.store.sql.assetindex.schema.postgres.PostgresDialectStatements; -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; +import org.eclipse.edc.connector.controlplane.asset.spi.domain.Asset; +import org.eclipse.edc.connector.controlplane.store.sql.assetindex.SqlAssetIndex; +import org.eclipse.edc.connector.controlplane.store.sql.assetindex.schema.postgres.PostgresDialectStatements; +import org.eclipse.edc.junit.annotations.PostgresqlIntegrationTest; import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.edc.spi.types.domain.asset.Asset; import org.eclipse.edc.sql.QueryExecutor; import org.eclipse.edc.sql.testfixtures.PostgresqlStoreSetupExtension; import org.flywaydb.core.api.MigrationVersion; @@ -35,7 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat; -@PostgresqlDbIntegrationTest +@PostgresqlIntegrationTest @ExtendWith(PostgresqlStoreSetupExtension.class) class AssetPostgresqlMigrationExtensionTest { @@ -48,7 +48,8 @@ void setUp(PostgresqlStoreSetupExtension extension, QueryExecutor queryExecutor) queryExecutor); } - @Test // bugfix https://github.com/eclipse-tractusx/tractusx-edc/issues/1003 + // bugfix https://github.com/eclipse-tractusx/tractusx-edc/issues/1003 + @Test void version006shouldTransformPropertiesListToMap(PostgresqlStoreSetupExtension extension) { var dataSource = extension.getDataSourceRegistry().resolve(extension.getDatasourceName()); FlywayManager.migrate(dataSource, "asset", "public", MigrationVersion.fromVersion("0.0.5")); diff --git a/edc-extensions/migrations/data-plane-migration/README.md b/edc-extensions/migrations/data-plane-migration/README.md new file mode 100644 index 000000000..e4ce1d0de --- /dev/null +++ b/edc-extensions/migrations/data-plane-migration/README.md @@ -0,0 +1,14 @@ +# Postgresql SQL Migration Extension + +This extension applies SQL migrations to these stores: + +* dataplane +* accesstokendata + +## Configuration + +| Key | Description | Mandatory | Default | +|:--------------------------------------------------------------------------|:-------------------------------------------------|-----------|----------| +| org.eclipse.tractusx.edc.postgresql.migration.dataplane.enabled | Enable migration for data flow tables | | true | +| org.eclipse.tractusx.edc.postgresql.migration.accesstokendata.enabled | Enable migration for access token data tables | | true | +| org.eclipse.tractusx.edc.postgresql.migration.schema | The DB schema to be used during migration | | "public" | diff --git a/edc-extensions/migrations/data-plane-migration/build.gradle.kts b/edc-extensions/migrations/data-plane-migration/build.gradle.kts new file mode 100644 index 000000000..2ff1aa7fa --- /dev/null +++ b/edc-extensions/migrations/data-plane-migration/build.gradle.kts @@ -0,0 +1,41 @@ +/******************************************************************************** + * 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 { + `maven-publish` + `java-library` +} + +dependencies { + implementation(project(":edc-extensions:migrations:postgresql-migration-lib")) + implementation(libs.edc.spi.core) + implementation(libs.edc.junit) + implementation(libs.edc.spi.transaction.datasource) + implementation(libs.edc.sql.assetindex) + implementation(libs.edc.sql.core) + runtimeOnly(libs.postgres) + + implementation(libs.flyway.core) + // starting from flyway 10, they've moved to a more modular structure, + // so we need to add PG support explicitly + // https://documentation.red-gate.com/flyway/release-notes-and-older-versions/release-notes-for-flyway-engine + runtimeOnly(libs.flyway.database.postgres) + + testImplementation(testFixtures(libs.edc.sql.core)) +} diff --git a/edc-extensions/migrations/data-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AccessTokenDataPostgresqlMigrationExtension.java b/edc-extensions/migrations/data-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AccessTokenDataPostgresqlMigrationExtension.java new file mode 100644 index 000000000..d1ab69723 --- /dev/null +++ b/edc-extensions/migrations/data-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AccessTokenDataPostgresqlMigrationExtension.java @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.postgresql.migration; + +public class AccessTokenDataPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "accesstokendata"; + + @Override + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } +} diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/EdrPostgresqlMigrationExtension.java b/edc-extensions/migrations/data-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/DataPlanePostgresqlMigrationExtension.java similarity index 80% rename from edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/EdrPostgresqlMigrationExtension.java rename to edc-extensions/migrations/data-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/DataPlanePostgresqlMigrationExtension.java index cbf82303d..bcbea63f9 100644 --- a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/EdrPostgresqlMigrationExtension.java +++ b/edc-extensions/migrations/data-plane-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/DataPlanePostgresqlMigrationExtension.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -19,10 +19,12 @@ package org.eclipse.tractusx.edc.postgresql.migration; -public class EdrPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { - private static final String NAME_SUBSYSTEM = "edr"; +public class DataPlanePostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "dataplane"; + @Override protected String getSubsystemName() { return NAME_SUBSYSTEM; } + } diff --git a/edc-extensions/migrations/data-plane-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/migrations/data-plane-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..6212ad5dd --- /dev/null +++ b/edc-extensions/migrations/data-plane-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,21 @@ +################################################################################# +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft +# +# 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 +################################################################################# + +org.eclipse.tractusx.edc.postgresql.migration.DataPlanePostgresqlMigrationExtension +org.eclipse.tractusx.edc.postgresql.migration.AccessTokenDataPostgresqlMigrationExtension diff --git a/edc-extensions/migrations/data-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/accesstokendata/V0_0_1__Init_AccessTokenData_Database_Schema.sql b/edc-extensions/migrations/data-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/accesstokendata/V0_0_1__Init_AccessTokenData_Database_Schema.sql new file mode 100644 index 000000000..6796e9afc --- /dev/null +++ b/edc-extensions/migrations/data-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/accesstokendata/V0_0_1__Init_AccessTokenData_Database_Schema.sql @@ -0,0 +1,28 @@ +-- +-- Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +-- +-- 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 +-- +-- Contributors: +-- Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation +-- + +-- +-- table: edc_accesstokendata +-- + +CREATE TABLE IF NOT EXISTS edc_accesstokendata +( + id VARCHAR NOT NULL PRIMARY KEY, + claim_token JSON NOT NULL, + data_address JSON NOT NULL, + additional_properties JSON DEFAULT '{}' +); + +COMMENT ON COLUMN edc_accesstokendata.claim_token IS 'ClaimToken serialized as JSON map'; +COMMENT ON COLUMN edc_accesstokendata.data_address IS 'DataAddress serialized as JSON map'; +COMMENT ON COLUMN edc_accesstokendata.additional_properties IS 'Optional Additional properties serialized as JSON map'; diff --git a/edc-extensions/migrations/data-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/dataplane/V0_0_1__Init_Dataplane_Database_Schema.sql b/edc-extensions/migrations/data-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/dataplane/V0_0_1__Init_Dataplane_Database_Schema.sql new file mode 100644 index 000000000..c83548bd5 --- /dev/null +++ b/edc-extensions/migrations/data-plane-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/dataplane/V0_0_1__Init_Dataplane_Database_Schema.sql @@ -0,0 +1,55 @@ +-- +-- Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +-- +-- 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 +-- +-- Contributors: +-- Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation +-- + +-- +-- tables: edc_data_plane, edc_lease +-- + +CREATE TABLE IF NOT EXISTS edc_lease +( + leased_by VARCHAR NOT NULL, + leased_at BIGINT, + lease_duration INTEGER NOT NULL, + lease_id VARCHAR NOT NULL + CONSTRAINT lease_pk + PRIMARY KEY +); + +COMMENT ON COLUMN edc_lease.leased_at IS 'posix timestamp of lease'; +COMMENT ON COLUMN edc_lease.lease_duration IS 'duration of lease in milliseconds'; + +CREATE TABLE IF NOT EXISTS edc_data_plane +( + process_id VARCHAR NOT NULL PRIMARY KEY, + state INTEGER NOT NULL , + created_at BIGINT NOT NULL , + updated_at BIGINT NOT NULL , + state_count INTEGER DEFAULT 0 NOT NULL, + state_time_stamp BIGINT, + trace_context JSON, + error_detail VARCHAR, + callback_address VARCHAR, + lease_id VARCHAR + CONSTRAINT data_plane_lease_lease_id_fk + REFERENCES edc_lease + ON DELETE SET NULL, + source JSON, + destination JSON, + properties JSON, + flow_type VARCHAR +); + +COMMENT ON COLUMN edc_data_plane.trace_context IS 'Java Map serialized as JSON'; +COMMENT ON COLUMN edc_data_plane.source IS 'DataAddress serialized as JSON'; +COMMENT ON COLUMN edc_data_plane.destination IS 'DataAddress serialized as JSON'; +COMMENT ON COLUMN edc_data_plane.properties IS 'Java Map serialized as JSON'; diff --git a/edc-extensions/postgresql-migration/build.gradle.kts b/edc-extensions/migrations/postgresql-migration-lib/build.gradle.kts similarity index 82% rename from edc-extensions/postgresql-migration/build.gradle.kts rename to edc-extensions/migrations/postgresql-migration-lib/build.gradle.kts index 22f26a7c2..ed6ad7469 100644 --- a/edc-extensions/postgresql-migration/build.gradle.kts +++ b/edc-extensions/migrations/postgresql-migration-lib/build.gradle.kts @@ -31,6 +31,10 @@ dependencies { runtimeOnly(libs.postgres) implementation(libs.flyway.core) + // starting from flyway 10, they've moved to a more modular structure, + // so we need to add PG support explicitly + // https://documentation.red-gate.com/flyway/release-notes-and-older-versions/release-notes-for-flyway-engine + runtimeOnly(libs.flyway.database.postgres) testImplementation(testFixtures(libs.edc.sql.core)) } diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java b/edc-extensions/migrations/postgresql-migration-lib/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java similarity index 95% rename from edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java rename to edc-extensions/migrations/postgresql-migration-lib/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java index 6c894b4d2..20ca7979f 100644 --- a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java +++ b/edc-extensions/migrations/postgresql-migration-lib/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java @@ -78,7 +78,7 @@ public void initialize(final ServiceExtensionContext context) { var dataSource = new ConnectionFactoryDataSource(driverManagerConnectionFactory, jdbcUrl, jdbcProperties); var defaultSchema = config.getString(MIGRATION_SCHEMA, DEFAULT_MIGRATION_SCHEMA); - var migrateResult = FlywayManager.migrate(dataSource, subSystemName, defaultSchema, LATEST); + var migrateResult = FlywayManager.migrate(dataSource, getMigrationSubsystem(), defaultSchema, LATEST); if (!migrateResult.success) { throw new EdcPersistenceException( @@ -90,4 +90,8 @@ public void initialize(final ServiceExtensionContext context) { protected abstract String getSubsystemName(); + protected String getMigrationSubsystem() { + return getSubsystemName(); + } + } diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/FlywayManager.java b/edc-extensions/migrations/postgresql-migration-lib/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/FlywayManager.java similarity index 100% rename from edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/FlywayManager.java rename to edc-extensions/migrations/postgresql-migration-lib/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/FlywayManager.java diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_1__Init_Edr_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_1__Init_Edr_Database_Schema.sql deleted file mode 100644 index 0e166045b..000000000 --- a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_1__Init_Edr_Database_Schema.sql +++ /dev/null @@ -1,34 +0,0 @@ --- --- Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) --- --- 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 --- --- Contributors: --- Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation --- - --- Statements are designed for and tested with Postgres only! - - -CREATE TABLE IF NOT EXISTS edc_edr_cache -( - transfer_process_id VARCHAR NOT NULL PRIMARY KEY, - agreement_id VARCHAR NOT NULL, - asset_id VARCHAR NOT NULL, - edr_id VARCHAR NOT NULL, - created_at BIGINT NOT NULL, - updated_at BIGINT NOT NULL -); - - -CREATE INDEX IF NOT EXISTS edc_edr_asset_id_index - ON edc_edr_cache (asset_id); - - -CREATE INDEX IF NOT EXISTS edc_edr_agreement_id_index - ON edc_edr_cache (agreement_id); - diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_3__Add_StatefulEntity_Columns.sql b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_3__Add_StatefulEntity_Columns.sql deleted file mode 100644 index a3442e00b..000000000 --- a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_3__Add_StatefulEntity_Columns.sql +++ /dev/null @@ -1,24 +0,0 @@ --- --- Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) --- --- 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 --- --- Contributors: --- Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation --- - --- Statements are designed for and tested with Postgres only! - - --- add column -ALTER TABLE edc_edr_cache ADD COLUMN expiration_timestamp BIGINT; -ALTER TABLE edc_edr_cache ADD COLUMN state INTEGER DEFAULT 50 NOT NULL; -ALTER TABLE edc_edr_cache ADD COLUMN state_count INTEGER DEFAULT 0; -ALTER TABLE edc_edr_cache ADD COLUMN state_timestamp BIGINT; -ALTER TABLE edc_edr_cache ADD COLUMN error_detail VARCHAR; -ALTER TABLE edc_edr_cache ADD COLUMN lease_id VARCHAR CONSTRAINT edc_edr_cache_lease_lease_id_fk REFERENCES edc_lease ON DELETE SET NULL; - diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_4__Add_ContractNegotiationId_Column.sql b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_4__Add_ContractNegotiationId_Column.sql deleted file mode 100644 index 291f325c3..000000000 --- a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_4__Add_ContractNegotiationId_Column.sql +++ /dev/null @@ -1,19 +0,0 @@ --- --- Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) --- --- 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 --- --- Contributors: --- Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation --- - --- Statements are designed for and tested with Postgres only! - - --- add column -ALTER TABLE edc_edr_cache ADD COLUMN contract_negotiation_id VARCHAR; - diff --git a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResource.java b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResource.java index 23ead4361..f6d3a6751 100644 --- a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResource.java +++ b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResource.java @@ -23,7 +23,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionedContentResource; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ProvisionedContentResource; @JsonDeserialize(builder = AdditionalHeadersProvisionedResource.Builder.class) class AdditionalHeadersProvisionedResource extends ProvisionedContentResource { diff --git a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisioner.java b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisioner.java index 1bf3928dc..80e2ab166 100644 --- a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisioner.java +++ b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisioner.java @@ -20,12 +20,12 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; +import org.eclipse.edc.connector.controlplane.transfer.spi.provision.Provisioner; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.DeprovisionedResource; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ProvisionResponse; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ProvisionedResource; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ResourceDefinition; import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; -import org.eclipse.edc.connector.transfer.spi.provision.Provisioner; -import org.eclipse.edc.connector.transfer.spi.types.DeprovisionedResource; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionResponse; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionedResource; -import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.response.StatusResult; @@ -54,15 +54,14 @@ public CompletableFuture> provision(AdditionalHe .addAdditionalHeader("Edc-Bpn", resourceDefinition.getBpn()) .build(); - var provisioned = - AdditionalHeadersProvisionedResource.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .resourceDefinitionId(resourceDefinition.getId()) - .transferProcessId(resourceDefinition.getTransferProcessId()) - .dataAddress(address) - .resourceName(UUID.randomUUID().toString()) - .hasToken(false) - .build(); + var provisioned = AdditionalHeadersProvisionedResource.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .resourceDefinitionId(resourceDefinition.getId()) + .transferProcessId(resourceDefinition.getTransferProcessId()) + .dataAddress(address) + .resourceName(UUID.randomUUID().toString()) + .hasToken(false) + .build(); var response = ProvisionResponse.Builder.newInstance().resource(provisioned).build(); var result = StatusResult.success(response); diff --git a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinition.java b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinition.java index 6ce362c0e..3b2536fd6 100644 --- a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinition.java +++ b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinition.java @@ -24,7 +24,7 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ResourceDefinition; import org.eclipse.edc.spi.types.domain.DataAddress; @JsonDeserialize(builder = AdditionalHeadersResourceDefinition.Builder.class) diff --git a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGenerator.java b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGenerator.java index b13f09fed..5baa3e5e9 100644 --- a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGenerator.java +++ b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGenerator.java @@ -20,13 +20,13 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; -import org.eclipse.edc.connector.spi.contractagreement.ContractAgreementService; -import org.eclipse.edc.connector.transfer.spi.provision.ProviderResourceDefinitionGenerator; -import org.eclipse.edc.connector.transfer.spi.types.DataRequest; -import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; +import org.eclipse.edc.connector.controlplane.contract.spi.types.agreement.ContractAgreement; +import org.eclipse.edc.connector.controlplane.services.spi.contractagreement.ContractAgreementService; +import org.eclipse.edc.connector.controlplane.transfer.spi.provision.ProviderResourceDefinitionGenerator; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ResourceDefinition; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; import org.jetbrains.annotations.Nullable; import java.util.Optional; @@ -41,14 +41,8 @@ class AdditionalHeadersResourceDefinitionGenerator implements ProviderResourceDe } @Override - public boolean canGenerate(DataRequest dataRequest, DataAddress dataAddress, Policy policy) { - return "HttpData".equals(dataAddress.getType()); - } - - @Override - public @Nullable ResourceDefinition generate( - DataRequest dataRequest, DataAddress dataAddress, Policy policy) { - var bpn = Optional.of(dataRequest.getContractId()) + public @Nullable ResourceDefinition generate(TransferProcess transferProcess, DataAddress dataAddress, Policy policy) { + var bpn = Optional.of(transferProcess.getContractId()) .map(contractAgreementService::findById) .map(ContractAgreement::getConsumerId) .orElse(null); @@ -56,8 +50,13 @@ public boolean canGenerate(DataRequest dataRequest, DataAddress dataAddress, Pol return AdditionalHeadersResourceDefinition.Builder.newInstance() .id(UUID.randomUUID().toString()) .dataAddress(dataAddress) - .contractId(dataRequest.getContractId()) + .contractId(transferProcess.getContractId()) .bpn(bpn) .build(); } + + @Override + public boolean canGenerate(TransferProcess transferProcess, DataAddress dataAddress, Policy policy) { + return "HttpData".equals(dataAddress.getType()); + } } diff --git a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtension.java b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtension.java index b1322de9a..321d6b655 100644 --- a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtension.java +++ b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtension.java @@ -20,9 +20,9 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; -import org.eclipse.edc.connector.spi.contractagreement.ContractAgreementService; -import org.eclipse.edc.connector.transfer.spi.provision.ProvisionManager; -import org.eclipse.edc.connector.transfer.spi.provision.ResourceManifestGenerator; +import org.eclipse.edc.connector.controlplane.services.spi.contractagreement.ContractAgreementService; +import org.eclipse.edc.connector.controlplane.transfer.spi.provision.ProvisionManager; +import org.eclipse.edc.connector.controlplane.transfer.spi.provision.ResourceManifestGenerator; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; diff --git a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionerTest.java b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionerTest.java index f15c72f6d..1a3a986c2 100644 --- a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionerTest.java +++ b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionerTest.java @@ -20,11 +20,11 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ProvisionResponse; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ProvisionedDataAddressResource; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ProvisionedResource; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ResourceDefinition; import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionResponse; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionedDataAddressResource; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionedResource; -import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.response.StatusResult; import org.junit.jupiter.api.Test; @@ -66,7 +66,6 @@ void shouldAddAdditionalHeaders() { .build(); var result = provisioner.provision(resourceDefinition, Policy.Builder.newInstance().build()); - assertThat(result) .succeedsWithin(5, SECONDS) .matches(StatusResult::succeeded) diff --git a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGeneratorTest.java b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGeneratorTest.java index f56480b36..0dff0d818 100644 --- a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGeneratorTest.java +++ b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGeneratorTest.java @@ -20,13 +20,13 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; +import org.eclipse.edc.connector.controlplane.contract.spi.types.agreement.ContractAgreement; +import org.eclipse.edc.connector.controlplane.services.spi.contractagreement.ContractAgreementService; +import org.eclipse.edc.connector.controlplane.transfer.spi.provision.ProviderResourceDefinitionGenerator; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess; import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; -import org.eclipse.edc.connector.spi.contractagreement.ContractAgreementService; -import org.eclipse.edc.connector.transfer.spi.provision.ProviderResourceDefinitionGenerator; -import org.eclipse.edc.connector.transfer.spi.types.DataRequest; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; import org.junit.jupiter.api.Test; import java.util.UUID; @@ -43,17 +43,23 @@ class AdditionalHeadersResourceDefinitionGeneratorTest { private final ContractAgreementService contractAgreementService = mock(); private final ProviderResourceDefinitionGenerator generator = new AdditionalHeadersResourceDefinitionGenerator(contractAgreementService); + private static ContractAgreement contractAgreementWithBpn(String bpn) { + return ContractAgreement.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .consumerId(bpn) + .providerId("providerId") + .assetId("assetId") + .policy(Policy.Builder.newInstance().build()) + .build(); + } + @Test void canGenerate_shouldReturnFalseForNotHttpDataAddresses() { var dataAddress = DataAddress.Builder.newInstance().type("any").build(); - var dataRequest = - DataRequest.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .dataDestination(dataAddress) - .build(); var build = Policy.Builder.newInstance().build(); + var transferProcess = TransferProcess.Builder.newInstance().build(); - var result = generator.canGenerate(dataRequest, dataAddress, build); + var result = generator.canGenerate(transferProcess, dataAddress, build); assertThat(result).isFalse(); } @@ -61,14 +67,10 @@ void canGenerate_shouldReturnFalseForNotHttpDataAddresses() { @Test void canGenerate_shouldReturnTrueForHttpDataAddresses() { var dataAddress = DataAddress.Builder.newInstance().type("HttpData").build(); - var dataRequest = - DataRequest.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .dataDestination(dataAddress) - .build(); var build = Policy.Builder.newInstance().build(); + var transferProcess = TransferProcess.Builder.newInstance().build(); - var result = generator.canGenerate(dataRequest, dataAddress, build); + var result = generator.canGenerate(transferProcess, dataAddress, build); assertThat(result).isTrue(); } @@ -76,16 +78,14 @@ void canGenerate_shouldReturnTrueForHttpDataAddresses() { @Test void shouldCreateResourceDefinitionWithDataAddress() { var dataAddress = HttpDataAddress.Builder.newInstance().baseUrl("http://any").build(); - var dataRequest = - DataRequest.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .dataDestination(dataAddress) - .contractId("contractId") - .build(); var build = Policy.Builder.newInstance().build(); when(contractAgreementService.findById(any())).thenReturn(contractAgreementWithBpn("bpn")); + var transferProcess = TransferProcess.Builder.newInstance() + .dataDestination(dataAddress) + .contractId("contractId") + .build(); - var result = generator.generate(dataRequest, dataAddress, build); + var result = generator.generate(transferProcess, dataAddress, build); assertThat(result) .asInstanceOf(type(AdditionalHeadersResourceDefinition.class)) @@ -99,14 +99,4 @@ void shouldCreateResourceDefinitionWithDataAddress() { }); verify(contractAgreementService).findById("contractId"); } - - private static ContractAgreement contractAgreementWithBpn(String bpn) { - return ContractAgreement.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .consumerId(bpn) - .providerId("providerId") - .assetId("assetId") - .policy(Policy.Builder.newInstance().build()) - .build(); - } } diff --git a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtensionTest.java b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtensionTest.java index 3e86729e0..b05713cee 100644 --- a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtensionTest.java +++ b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtensionTest.java @@ -20,8 +20,8 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; -import org.eclipse.edc.connector.transfer.spi.provision.ProvisionManager; -import org.eclipse.edc.connector.transfer.spi.provision.ResourceManifestGenerator; +import org.eclipse.edc.connector.controlplane.transfer.spi.provision.ProvisionManager; +import org.eclipse.edc.connector.controlplane.transfer.spi.provision.ResourceManifestGenerator; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.junit.jupiter.api.BeforeEach; diff --git a/edc-extensions/ssi/ssi-identity-core/README.md b/edc-extensions/ssi/ssi-identity-core/README.md deleted file mode 100644 index 59cc63bfe..000000000 --- a/edc-extensions/ssi/ssi-identity-core/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# SSI Core Identity Service Module - -This module contains an implementation of the EDC identity service for SSI. -The SsiIdentityService contains a `SsiTokenValidationService` for validating the `JWT` token, -that uses an implementation of `SsiCredentialClient` for validating the JWT token and then check custom rules registered in the `SsiValidationRuleRegistry` - -For obtaining the `JWT` token, the identity service also delegate to the `SsiCredentialClient` . - -The default implementation according to the first milestone [here](https://github.com/eclipse-tractusx/ssi-docu/tree/main/docs/architecture/cx-3-2) -will rely on an MIW and the implementations in available in the module `:edc-extensions:ssi:ssi-miw-credential-client`. - -The implementation also provide a rule registry `SsiValidationRuleRegistry` where custom rule can be registered for validating the `ClaimToken` extracted from the `JWT` token. - -Custom rule could be like: - -- Audience validation -- VP/VC validation -- Expiration -- ..etc - -## Configuration - -| Key | Required | Example | Description | -|-----------------------------------------|----------|----------------|---------------------------------------| -| tx.ssi.endpoint.audience | X | | Endpoint URL for audience check (DSP) | diff --git a/edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityService.java b/edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityService.java deleted file mode 100644 index fd3c5fde9..000000000 --- a/edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityService.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.identity; - -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.IdentityService; -import org.eclipse.edc.spi.iam.TokenParameters; -import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.iam.VerificationContext; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.token.spi.TokenValidationRulesRegistry; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiCredentialClient; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiTokenValidationService; - -import static org.eclipse.tractusx.edc.iam.ssi.spi.SsiConstants.SSI_TOKEN_CONTEXT; - -public class SsiIdentityService implements IdentityService { - - private final SsiTokenValidationService tokenValidationService; - - private final TokenValidationRulesRegistry rulesRegistry; - - private final SsiCredentialClient client; - - public SsiIdentityService(SsiTokenValidationService tokenValidationService, TokenValidationRulesRegistry rulesRegistry, - SsiCredentialClient client) { - this.tokenValidationService = tokenValidationService; - this.rulesRegistry = rulesRegistry; - this.client = client; - } - - @Override - public Result obtainClientCredentials(TokenParameters parameters) { - return client.obtainClientCredentials(parameters); - } - - @Override - public Result verifyJwtToken(TokenRepresentation tokenRepresentation, VerificationContext verificationContext) { - return tokenValidationService.validate(tokenRepresentation, rulesRegistry.getRules(SSI_TOKEN_CONTEXT)); - } -} diff --git a/edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityServiceExtension.java b/edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityServiceExtension.java deleted file mode 100644 index 762e0ac33..000000000 --- a/edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityServiceExtension.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.identity; - -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provides; -import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.iam.AudienceResolver; -import org.eclipse.edc.spi.iam.IdentityService; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.domain.message.RemoteMessage; -import org.eclipse.edc.token.spi.TokenValidationRulesRegistry; -import org.eclipse.tractusx.edc.iam.ssi.identity.rule.SsiAudienceValidationRule; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiCredentialClient; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiTokenValidationService; - -import static org.eclipse.tractusx.edc.iam.ssi.spi.SsiConstants.SSI_TOKEN_CONTEXT; - -@Provides({ IdentityService.class, SsiTokenValidationService.class, AudienceResolver.class }) -@Extension(SsiIdentityServiceExtension.EXTENSION_NAME) -public class SsiIdentityServiceExtension implements ServiceExtension { - - public static final String EXTENSION_NAME = "SSI Identity Service"; - - @Setting(value = "SSI Endpoint audience of this connector") - public static final String ENDPOINT_AUDIENCE = "tx.ssi.endpoint.audience"; - - @Inject - private SsiCredentialClient credentialClient; - - @Inject - private TokenValidationRulesRegistry rulesRegistry; - - @Override - public String name() { - return EXTENSION_NAME; - } - - @Override - public void initialize(ServiceExtensionContext context) { - - var tokenValidationService = new SsiTokenValidationServiceImpl(credentialClient); - configureRules(context, rulesRegistry); - - var identityService = new SsiIdentityService(tokenValidationService, rulesRegistry, credentialClient); - - context.registerService(IdentityService.class, identityService); - context.registerService(SsiTokenValidationService.class, tokenValidationService); - context.registerService(AudienceResolver.class, RemoteMessage::getCounterPartyAddress); - } - - private void configureRules(ServiceExtensionContext context, TokenValidationRulesRegistry registry) { - var endpointAudience = context.getConfig().getString(ENDPOINT_AUDIENCE); - registry.addRule(SSI_TOKEN_CONTEXT, new SsiAudienceValidationRule(endpointAudience)); - } -} diff --git a/edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiTokenValidationServiceImpl.java b/edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiTokenValidationServiceImpl.java deleted file mode 100644 index 1c1304dd2..000000000 --- a/edc-extensions/ssi/ssi-identity-core/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiTokenValidationServiceImpl.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.identity; - -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.token.spi.TokenValidationRule; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiCredentialClient; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiTokenValidationService; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -public class SsiTokenValidationServiceImpl implements SsiTokenValidationService { - - private final SsiCredentialClient credentialClient; - - public SsiTokenValidationServiceImpl(SsiCredentialClient credentialClient) { - this.credentialClient = credentialClient; - } - - @Override - public Result validate(TokenRepresentation tokenRepresentation, List rules) { - return credentialClient.validate(tokenRepresentation) - .compose(claimToken -> checkRules(claimToken, tokenRepresentation.getAdditional(), rules)); - } - - private Result checkRules(ClaimToken claimToken, @Nullable Map additional, List rules) { - var errors = rules.stream() - .map(r -> r.checkRule(claimToken, additional)) - .filter(Result::failed) - .map(Result::getFailureMessages) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - - if (!errors.isEmpty()) { - return Result.failure(errors); - } - return Result.success(claimToken); - } -} diff --git a/edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityServiceTest.java b/edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityServiceTest.java deleted file mode 100644 index b6d7fc050..000000000 --- a/edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiIdentityServiceTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.identity; - -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.PublicKeyResolver; -import org.eclipse.edc.spi.iam.TokenParameters; -import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.token.spi.TokenValidationRulesRegistry; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiCredentialClient; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiTokenValidationService; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class SsiIdentityServiceTest { - - SsiCredentialClient credentialClient = mock(SsiCredentialClient.class); - SsiTokenValidationService tokenValidationService = mock(SsiTokenValidationService.class); - TokenValidationRulesRegistry rulesRegistry = mock(TokenValidationRulesRegistry.class); - PublicKeyResolver publicKeyResolver = mock(PublicKeyResolver.class); - - SsiIdentityService identityService; - - @BeforeEach - void setup() { - identityService = new SsiIdentityService(tokenValidationService, rulesRegistry, credentialClient); - } - - @Test - void verifyJwtToken_success() { - var token = TokenRepresentation.Builder.newInstance().token("test").build(); - var claim = ClaimToken.Builder.newInstance().build(); - - when(tokenValidationService.validate(eq(token), eq(List.of()))).thenReturn(Result.success(claim)); - - var result = identityService.verifyJwtToken(token, mock()); - - assertThat(result).isNotNull().extracting(Result::getContent).isEqualTo(claim); - } - - @Test - void verifyJwtToken_failed() { - var token = TokenRepresentation.Builder.newInstance().token("test").build(); - - when(tokenValidationService.validate(eq(token), eq(List.of()))).thenReturn(Result.failure("fail")); - - var result = identityService.verifyJwtToken(token, mock()); - - assertThat(result).isNotNull().matches(Result::failed); - } - - - @Test - void obtainClientCredentials_success() { - var tokenParameters = TokenParameters.Builder.newInstance().claims(AUDIENCE, "audience").build(); - var tokenRepresentation = TokenRepresentation.Builder.newInstance().token("test").build(); - - when(credentialClient.obtainClientCredentials(tokenParameters)).thenReturn(Result.success(tokenRepresentation)); - - var result = identityService.obtainClientCredentials(tokenParameters); - - assertThat(result).isNotNull().extracting(Result::getContent).isEqualTo(tokenRepresentation); - } - - @Test - void obtainClientCredentials_fail() { - var tokenParameters = TokenParameters.Builder.newInstance().claims(AUDIENCE, "audience").build(); - - when(credentialClient.obtainClientCredentials(tokenParameters)).thenReturn(Result.failure("fail")); - - var result = identityService.obtainClientCredentials(tokenParameters); - - assertThat(result).isNotNull().matches(Result::failed); - } -} diff --git a/edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiTokenValidationServiceImplTest.java b/edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiTokenValidationServiceImplTest.java deleted file mode 100644 index ba42dff2c..000000000 --- a/edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/SsiTokenValidationServiceImplTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.identity; - -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.token.spi.TokenValidationRule; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiCredentialClient; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiTokenValidationService; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - -public class SsiTokenValidationServiceImplTest { - - SsiCredentialClient credentialClient = mock(SsiCredentialClient.class); - - SsiTokenValidationService validationService; - - @BeforeEach - void setup() { - validationService = new SsiTokenValidationServiceImpl(credentialClient); - } - - @Test - void validate_success() { - var token = TokenRepresentation.Builder.newInstance().token("test").build(); - var rule = mock(TokenValidationRule.class); - var claim = ClaimToken.Builder.newInstance().build(); - - when(credentialClient.validate(token)).thenReturn(Result.success(claim)); - when(rule.checkRule(any(), any())).thenReturn(Result.success()); - - var result = validationService.validate(token, List.of(rule)); - - assertThat(result).isNotNull().extracting(Result::getContent).isEqualTo(claim); - - verify(credentialClient).validate(token); - verify(rule).checkRule(eq(claim), any()); - } - - @Test - void validate_fail_whenClientFails() { - var token = TokenRepresentation.Builder.newInstance().token("test").build(); - var rule = mock(TokenValidationRule.class); - - when(credentialClient.validate(token)).thenReturn(Result.failure("failure")); - when(rule.checkRule(any(), any())).thenReturn(Result.success()); - - var result = validationService.validate(token, List.of(rule)); - - assertThat(result).isNotNull().matches(Result::failed); - - verify(credentialClient).validate(token); - verifyNoInteractions(rule); - } - - @Test - void validate_fail_whenRuleFails() { - var token = TokenRepresentation.Builder.newInstance().token("test").build(); - var rule = mock(TokenValidationRule.class); - var claim = ClaimToken.Builder.newInstance().build(); - - - when(credentialClient.validate(token)).thenReturn(Result.success(claim)); - when(rule.checkRule(any(), any())).thenReturn(Result.failure("failure")); - - var result = validationService.validate(token, List.of(rule)); - - assertThat(result).isNotNull().matches(Result::failed); - - verify(credentialClient).validate(token); - verify(rule).checkRule(eq(claim), any()); - } -} diff --git a/edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/rule/SsiAudienceValidationRuleTest.java b/edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/rule/SsiAudienceValidationRuleTest.java deleted file mode 100644 index 8eea66edf..000000000 --- a/edc-extensions/ssi/ssi-identity-core/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/rule/SsiAudienceValidationRuleTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.identity.rule; - -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.token.spi.TokenValidationRule; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static java.util.Collections.emptyMap; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE; - -public class SsiAudienceValidationRuleTest { - - private final String endpointAudience = "test-audience"; - private final TokenValidationRule rule = new SsiAudienceValidationRule(endpointAudience); - - @Test - void validAudience() { - var token = ClaimToken.Builder.newInstance() - .claim(AUDIENCE, List.of(endpointAudience)) - .build(); - - var result = rule.checkRule(token, emptyMap()); - - assertThat(result.succeeded()).isTrue(); - } - - @Test - void validationKoBecauseAudienceNotRespected() { - var token = ClaimToken.Builder.newInstance() - .claim(AUDIENCE, List.of("fake-audience")) - .build(); - - var result = rule.checkRule(token, emptyMap()); - - assertThat(result.succeeded()).isFalse(); - assertThat(result.getFailureMessages()).hasSize(1) - .contains("Token audience (aud) claim did not contain audience: test-audience"); - } - - @Test - void validationKoBecauseAudienceNotProvided() { - var token = ClaimToken.Builder.newInstance() - .build(); - - var result = rule.checkRule(token, emptyMap()); - - assertThat(result.succeeded()).isFalse(); - assertThat(result.getFailureMessages()).hasSize(1) - .contains("Required audience (aud) claim is missing in token"); - } -} diff --git a/edc-extensions/ssi/ssi-identity-extractor/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/CredentialIdentityExtractor.java b/edc-extensions/ssi/ssi-identity-extractor/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/CredentialIdentityExtractor.java deleted file mode 100644 index 26cc9ae60..000000000 --- a/edc-extensions/ssi/ssi-identity-extractor/src/main/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/CredentialIdentityExtractor.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.identity.extractor; - -import jakarta.json.JsonObject; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.agent.ParticipantAgentServiceExtension; -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdFieldExtractor; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; - -import static org.eclipse.edc.spi.agent.ParticipantAgent.PARTICIPANT_IDENTITY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CREDENTIAL_SUBJECT; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.HOLDER_IDENTIFIER; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.SUMMARY_CREDENTIAL_TYPE; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTypeFunctions.extractObjectsOfType; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdValueFunctions.extractStringValue; - -public class CredentialIdentityExtractor implements ParticipantAgentServiceExtension { - - private static final String IDENTITY_EXTRACTOR_PREFIX = "Identity extractor:"; - - private final JsonLdFieldExtractor holderIdentifierExtractor = JsonLdFieldExtractor.Builder.newInstance() - .field(HOLDER_IDENTIFIER) - .fieldAlias("holderIdentifier") - .errorPrefix(IDENTITY_EXTRACTOR_PREFIX) - .build(); - private final JsonLdFieldExtractor credentialSubjectExtractor = JsonLdFieldExtractor.Builder.newInstance() - .field(CREDENTIAL_SUBJECT) - .fieldAlias("credentialSubject") - .errorPrefix(IDENTITY_EXTRACTOR_PREFIX) - .build(); - - @Override - public @NotNull Map attributesFor(ClaimToken token) { - var vp = (JsonObject) token.getClaim(VP_PROPERTY); - - var extractionResult = Optional.ofNullable(vp) - .map(v -> extractObjectsOfType(SUMMARY_CREDENTIAL_TYPE, v)) - .orElse(Stream.empty()) - .map(this::extractHolderIdentifier) - .findFirst() - .orElseThrow(() -> new EdcException("Failed to extract identity from the membership credential")); - - var bpn = extractionResult.orElseThrow((failure) -> new EdcException(failure.getFailureDetail())); - return Map.of(PARTICIPANT_IDENTITY, bpn); - - } - - private Result extractHolderIdentifier(JsonObject credential) { - return this.credentialSubjectExtractor.extract(credential) - .compose(holderIdentifierExtractor::extract) - .compose(this::extractHolderIdentifierValue); - } - - private Result extractHolderIdentifierValue(JsonObject identifier) { - var bpn = extractStringValue(identifier); - if (bpn == null) { - return Result.failure("Failed to find the holder identifier"); - } else { - return Result.success(bpn); - } - } -} diff --git a/edc-extensions/ssi/ssi-identity-extractor/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/ssi/ssi-identity-extractor/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension deleted file mode 100644 index da0c5e345..000000000 --- a/edc-extensions/ssi/ssi-identity-extractor/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ /dev/null @@ -1,20 +0,0 @@ -################################################################################# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# -# 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 -################################################################################# - -org.eclipse.tractusx.edc.iam.ssi.identity.extractor.SsiIdentityExtractorExtension diff --git a/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/CredentialIdentityExtractorTest.java b/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/CredentialIdentityExtractorTest.java deleted file mode 100644 index 4caee164d..000000000 --- a/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/CredentialIdentityExtractorTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.identity.extractor; - -import com.fasterxml.jackson.core.JsonProcessingException; -import jakarta.json.JsonObject; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryContext; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.eclipse.edc.spi.agent.ParticipantAgent.PARTICIPANT_IDENTITY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_SUMMARY_NS_V1; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.createObjectMapper; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.expand; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SIMPLE_VP; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP_NO_HOLDER; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP_NO_SUBJECT; - -public class CredentialIdentityExtractorTest { - - static final Map CONTEXT_CACHE = Map.of(CX_SUMMARY_NS_V1, SummaryContext.SUMMARY_CONTEXT); - - CredentialIdentityExtractor extractor = new CredentialIdentityExtractor(); - - @Test - void attributeFor() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); - var attributes = extractor.attributesFor(ClaimToken.Builder.newInstance().claim(VP_PROPERTY, vp).build()); - - assertThat(attributes).contains(Map.entry(PARTICIPANT_IDENTITY, "BPN of holder")); - } - - @Test - void attributeFor_exception_whenVpNotPresent() { - assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().build())) - .isInstanceOf(EdcException.class) - .hasMessage("Failed to extract identity from the membership credential"); - } - - @Test - void attributeFor_exception_whenCredentialTypeNotMatch() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(SIMPLE_VP, JsonObject.class), CONTEXT_CACHE); - assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().claim(VP_PROPERTY, vp).build())) - .isInstanceOf(EdcException.class) - .hasMessage("Failed to extract identity from the membership credential"); - } - - @Test - void attributeFor_exception_whenHolderIdentifierNotFound() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(SUMMARY_VP_NO_HOLDER, JsonObject.class), CONTEXT_CACHE); - assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().claim(VP_PROPERTY, vp).build())) - .isInstanceOf(EdcException.class) - .hasMessage("Identity extractor: no holderIdentifier found"); - } - - @Test - void attributeFor_exception_whenCredentialSubjectNotFound() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(SUMMARY_VP_NO_SUBJECT, JsonObject.class), CONTEXT_CACHE); - assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().claim(VP_PROPERTY, vp).build())) - .isInstanceOf(EdcException.class) - .hasMessage("Identity extractor: no credentialSubject found"); - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/README.md b/edc-extensions/ssi/ssi-miw-credential-client/README.md deleted file mode 100644 index 883165147..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# MIW Client Credential Module - -This module contains an implementation of the `SsiCredentialClient` interface for SSI. -It basically narrows down to two operations: - -- obtaining a token for protocol communication -- validating the token - -For validating the token accordingly to the first milestone [here](https://github.com/eclipse-tractusx/ssi-docu/tree/main/docs/architecture/cx-3-2), the implemetation -just call the MIW for checking that the token and the VP claim inside are correct. Then extract the `JWT` claims into the `ClaimToken` for further checks. - -For obtaining a `JWT` token also it reaches the MIW, that will create a token with the `VP` claim inside. - -This module also contains two additional validation rules of VP/VC on the provider side. - -- `SsiCredentialIssuerValidationRule` checks if the issuer of the Verifiable Credential matches `tx.ssi.miw.authority.issuer` -- `SsiCredentialSubjectIdValidationRule` checks if the issuer of the JWT/VP matches the credential subject id in the Verifiable Credential - -## Configuration - -| Key | Required | Example | Description | -|----------------------------------|----------|----------------|-----------------------------------| -| tx.ssi.miw.url | X | | MIW URL | -| tx.ssi.miw.authority.id | X | | BPN number of the authority | -| tx.ssi.miw.authority.issuer | | | The id of the issuer (DID) | -| tx.ssi.oauth.token.url | X | | Token URL (Keycloak) | -| tx.ssi.oauth.client.id | X | | Client id | -| tx.ssi.oauth.client.secret.alias | X | | Vault alias for the client secret | - -By default, the `tx.ssi.miw.authority.issuer` is composed with `did:web::` - -Another mandatory settings is `tx.ssi.endpoint.audience` which is described [here](../ssi-identity-core/README.md) - -> Note: the `edc.participant.id` should match the BPN number contained in the OAuth2/Keycloak token and the one assigned by the portal to the user's organization. diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwApiClientExtension.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwApiClientExtension.java deleted file mode 100644 index c78354ff0..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwApiClientExtension.java +++ /dev/null @@ -1,66 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw; - -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provider; -import org.eclipse.edc.spi.http.EdcHttpClient; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClient; -import org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl; -import org.eclipse.tractusx.edc.iam.ssi.miw.config.SsiMiwConfiguration; -import org.eclipse.tractusx.edc.iam.ssi.miw.oauth2.MiwOauth2Client; - - -@Extension(SsiMiwApiClientExtension.EXTENSION_NAME) -public class SsiMiwApiClientExtension implements ServiceExtension { - - public static final String EXTENSION_NAME = "SSI MIW Api Client"; - - @Inject - private MiwOauth2Client oauth2Client; - - @Inject - private EdcHttpClient httpClient; - - @Inject - private TypeManager typeManager; - - @Inject - private Monitor monitor; - - @Inject - private SsiMiwConfiguration miwConfiguration; - - @Override - public String name() { - return EXTENSION_NAME; - } - - @Provider - public MiwApiClient apiClient(ServiceExtensionContext context) { - return new MiwApiClientImpl(httpClient, miwConfiguration.getUrl(), oauth2Client, context.getParticipantId(), miwConfiguration.getAuthorityId(), typeManager.getMapper(), monitor); - } - -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwConfigurationExtension.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwConfigurationExtension.java deleted file mode 100644 index 95e435608..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwConfigurationExtension.java +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw; - -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Provider; -import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.iam.ssi.miw.config.SsiMiwConfiguration; - -import java.net.URI; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; - -import static java.lang.String.format; -import static org.eclipse.tractusx.edc.iam.ssi.miw.utils.PathUtils.removeTrailingSlash; - - -@Extension(SsiMiwConfigurationExtension.EXTENSION_NAME) -public class SsiMiwConfigurationExtension implements ServiceExtension { - - - @Setting(value = "MIW API base url") - public static final String MIW_BASE_URL = "tx.ssi.miw.url"; - @Setting(value = "MIW Authority ID") - public static final String MIW_AUTHORITY_ID = "tx.ssi.miw.authority.id"; - @Setting(value = "MIW Authority Issuer") - public static final String MIW_AUTHORITY_ISSUER = "tx.ssi.miw.authority.issuer"; - public static final String AUTHORITY_ID_TEMPLATE = "did:web:%s:%s"; - protected static final String EXTENSION_NAME = "SSI Miw configuration extension"; - - @Provider - public SsiMiwConfiguration miwConfiguration(ServiceExtensionContext context) { - var baseUrl = removeTrailingSlash(context.getConfig().getString(MIW_BASE_URL)); - var authorityId = context.getConfig().getString(MIW_AUTHORITY_ID); - var authorityIssuer = authorityIssuer(context, baseUrl, authorityId); - - return SsiMiwConfiguration.Builder.newInstance() - .url(baseUrl) - .authorityId(authorityId) - .authorityIssuer(authorityIssuer) - .build(); - } - - - private String authorityIssuer(ServiceExtensionContext context, String baseUrl, String authorityId) { - var uri = URI.create(baseUrl); - var defaultAuthorityIssuer = format(AUTHORITY_ID_TEMPLATE, URLEncoder.encode(uri.getAuthority(), StandardCharsets.UTF_8), authorityId); - return context.getConfig().getString(MIW_AUTHORITY_ISSUER, defaultAuthorityIssuer); - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwOauth2ClientExtension.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwOauth2ClientExtension.java deleted file mode 100644 index e4becb2c0..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwOauth2ClientExtension.java +++ /dev/null @@ -1,83 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw; - -import org.eclipse.edc.iam.oauth2.spi.client.Oauth2Client; -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provider; -import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.iam.ssi.miw.oauth2.MiwOauth2Client; -import org.eclipse.tractusx.edc.iam.ssi.miw.oauth2.MiwOauth2ClientConfiguration; -import org.eclipse.tractusx.edc.iam.ssi.miw.oauth2.MiwOauth2ClientImpl; - -import java.util.Objects; - -import static org.eclipse.tractusx.edc.iam.ssi.miw.utils.PathUtils.removeTrailingSlash; - - -@Extension(SsiMiwOauth2ClientExtension.EXTENSION_NAME) -public class SsiMiwOauth2ClientExtension implements ServiceExtension { - - public static final String EXTENSION_NAME = "SSI MIW OAuth2 Client"; - - @Setting(value = "OAuth2 endpoint for requesting a token") - public static final String TOKEN_URL = "tx.ssi.oauth.token.url"; - - - @Setting(value = "OAuth2 client id") - public static final String CLIENT_ID = "tx.ssi.oauth.client.id"; - - @Setting(value = "Vault alias of OAuth2 client secret") - public static final String CLIENT_SECRET_ALIAS = "tx.ssi.oauth.client.secret.alias"; - - @Inject - private Oauth2Client oauth2Client; - - @Inject - private Vault vault; - - @Override - public String name() { - return EXTENSION_NAME; - } - - @Provider - public MiwOauth2Client oauth2Client(ServiceExtensionContext context) { - return new MiwOauth2ClientImpl(oauth2Client, createConfiguration(context)); - } - - private MiwOauth2ClientConfiguration createConfiguration(ServiceExtensionContext context) { - var tokenUrl = removeTrailingSlash(context.getConfig().getString(TOKEN_URL)); - var clientId = context.getConfig().getString(CLIENT_ID); - var clientSecretAlias = context.getConfig().getString(CLIENT_SECRET_ALIAS); - var clientSecret = vault.resolveSecret(clientSecretAlias); - Objects.requireNonNull(clientSecret, "Client secret could not be retrieved"); - - return MiwOauth2ClientConfiguration.Builder.newInstance() - .tokenUrl(tokenUrl) - .clientId(clientId) - .clientSecret(clientSecret) - .build(); - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtension.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtension.java deleted file mode 100644 index 469df860e..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtension.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw; - -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.token.spi.TokenValidationRulesRegistry; -import org.eclipse.tractusx.edc.iam.ssi.miw.config.SsiMiwConfiguration; -import org.eclipse.tractusx.edc.iam.ssi.miw.rule.SsiCredentialIssuerValidationRule; -import org.eclipse.tractusx.edc.iam.ssi.miw.rule.SsiCredentialSubjectIdValidationRule; - -import static org.eclipse.tractusx.edc.iam.ssi.spi.SsiConstants.SSI_TOKEN_CONTEXT; - -@Extension(SsiMiwValidationRuleExtension.EXTENSION_NAME) -public class SsiMiwValidationRuleExtension implements ServiceExtension { - - protected static final String EXTENSION_NAME = "SSI MIW validation rules extension"; - @Inject - private TokenValidationRulesRegistry registry; - - @Inject - private Monitor monitor; - - @Inject - private SsiMiwConfiguration miwConfiguration; - - @Override - public String name() { - return EXTENSION_NAME; - } - - @Override - public void initialize(ServiceExtensionContext context) { - registry.addRule(SSI_TOKEN_CONTEXT, new SsiCredentialSubjectIdValidationRule(monitor)); - registry.addRule(SSI_TOKEN_CONTEXT, new SsiCredentialIssuerValidationRule(miwConfiguration.getAuthorityIssuer(), monitor)); - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClient.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClient.java deleted file mode 100644 index cc90325f0..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClient.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.api; - -import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; -import org.eclipse.edc.spi.result.Result; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -@ExtensionPoint -public interface MiwApiClient { - String VP = "vp"; - - Result>> getCredentials(Set types); - - Result> createPresentation(List> credentials, String audience); - - Result verifyPresentation(String jwtPresentation, String audience); - -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImpl.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImpl.java deleted file mode 100644 index aa0410acc..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImpl.java +++ /dev/null @@ -1,208 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.api; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import okhttp3.MediaType; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import org.eclipse.edc.spi.http.EdcHttpClient; -import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.tractusx.edc.iam.ssi.miw.oauth2.MiwOauth2Client; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -import static java.lang.String.format; -import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwFallbackFactories.retryWhenStatusIsNotIn; - -public class MiwApiClientImpl implements MiwApiClient { - - public static final MediaType TYPE_JSON = MediaType.parse("application/json"); - public static final String CREDENTIAL_PATH = "/api/credentials"; - public static final String PRESENTATIONS_PATH = "/api/presentations"; - public static final String PRESENTATIONS_VALIDATION_PATH = "/api/presentations/validation"; - public static final String HOLDER_IDENTIFIER = "holderIdentifier"; - - public static final String ISSUER_IDENTIFIER = "issuerIdentifier"; - public static final String VERIFIABLE_CREDENTIALS = "verifiableCredentials"; - public static final String VP_FIELD = "vp"; - public static final String CONTENT_FIELD = "content"; - private static final String PRESENTATIONS_QUERY_PARAMS = "?asJwt=true&audience=%s"; - private final EdcHttpClient httpClient; - private final String baseUrl; - private final MiwOauth2Client oauth2Client; - private final ObjectMapper mapper; - private final Monitor monitor; - - private final String authorityId; - - private final String participantId; - - public MiwApiClientImpl(EdcHttpClient httpClient, String baseUrl, MiwOauth2Client oauth2Client, String participantId, String authorityId, ObjectMapper mapper, Monitor monitor) { - this.httpClient = httpClient; - this.baseUrl = baseUrl; - this.oauth2Client = oauth2Client; - this.participantId = participantId; - this.authorityId = authorityId; - this.mapper = mapper; - this.monitor = monitor; - } - - @Override - public Result>> getCredentials(Set types) { - - var params = new ArrayList(); - params.add(format("%s=%s", ISSUER_IDENTIFIER, authorityId)); - - if (!types.isEmpty()) { - params.add(format("type=%s", String.join(",", types))); - } - - var queryParams = "?" + String.join("&", params); - var url = baseUrl + CREDENTIAL_PATH + queryParams; - - return baseRequestWithToken().map(builder -> builder.get().url(url).build()) - .compose(request -> executeRequest(request, new TypeReference>() { - })) - .compose(this::handleGetCredentialResponse); - } - - @Override - public Result> createPresentation(List> credentials, String audience) { - try { - var body = Map.of(HOLDER_IDENTIFIER, participantId, VERIFIABLE_CREDENTIALS, credentials); - var url = baseUrl + PRESENTATIONS_PATH + format(PRESENTATIONS_QUERY_PARAMS, audience); - var requestBody = RequestBody.create(mapper.writeValueAsString(body), TYPE_JSON); - - return baseRequestWithToken().map(builder -> builder.post(requestBody).url(url).build()) - .compose(request -> executeRequest(request, new TypeReference<>() { - })); - } catch (JsonProcessingException e) { - return Result.failure(e.getMessage()); - } - } - - @Override - public Result verifyPresentation(String jwtPresentation, String audience) { - try { - var body = Map.of(VP_FIELD, jwtPresentation); - var url = baseUrl + PRESENTATIONS_VALIDATION_PATH + format(PRESENTATIONS_QUERY_PARAMS, audience); - var requestBody = RequestBody.create(mapper.writeValueAsString(body), TYPE_JSON); - - return baseRequestWithToken().map(builder -> builder.post(requestBody).url(url).build()) - .compose(request -> executeRequest(request, new TypeReference>() { - })) - .compose(this::handleVerifyResult); - } catch (JsonProcessingException e) { - return Result.failure(e.getMessage()); - } - } - - private Result>> handleGetCredentialResponse(Map response) { - var content = response.get(CONTENT_FIELD); - - if (content == null) { - return Result.failure("Missing content field in the credentials response"); - } - return Result.success((List>) content); - } - - private Result handleVerifyResult(Map response) { - var valid = Optional.ofNullable(response.get("valid")) - .map(Boolean.TRUE::equals) - .orElse(false); - - if (valid) { - return Result.success(); - } else { - var msg = "MIW verification failed"; - monitor.severe(msg); - return Result.failure(msg); - } - } - - private Result executeRequest(Request request, TypeReference typeReference) { - try (var response = httpClient.execute(request, List.of(retryWhenStatusIsNotIn(200, 201)))) { - return handleResponse(response, typeReference); - } catch (MiwClientException e) { - if (e.getResponse() != null) { - return handleError(e.getResponse()); - } else { - return Result.failure(e.getMessage()); - } - } catch (IOException e) { - return Result.failure(e.getMessage()); - } - } - - private Result handleResponse(Response response, TypeReference tr) { - if (response.isSuccessful()) { - return handleSuccess(response, tr); - } else { - return handleError(response); - } - } - - private Result handleSuccess(Response response, TypeReference tr) { - try { - var body = Objects.requireNonNull(response.body()).string(); - return Result.success(mapper.readValue(body, tr)); - } catch (IOException e) { - monitor.severe("Failed to parse response from MIW"); - return Result.failure(e.getMessage()); - } - } - - private Result handleError(Response response) { - var body = ""; - if (response.body() != null) { - try { - body = response.body().string(); - } catch (IOException e) { - monitor.severe("Failed to read response from MIW"); - return Result.failure(e.getMessage()); - } - } - var code = response.code(); - monitor.severe(format("MIW API returned %s with body: %s", code, body)); - return Result.failure(format("MIW API returned %s", code)); - } - - private Result baseRequestWithToken() { - return oauth2Client.obtainRequestToken() - .map(this::baseRequestWithToken); - } - - private Request.Builder baseRequestWithToken(TokenRepresentation tokenRepresentation) { - return new Request.Builder() - .addHeader("Authorization", format("Bearer %s", tokenRepresentation.getToken())); - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwClientException.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwClientException.java deleted file mode 100644 index 65bf7a6af..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwClientException.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.api; - -import okhttp3.Response; -import org.eclipse.edc.spi.http.EdcHttpClientException; - -/** - * Custom client exception for handling failure and retries when fetching data from MIW. - */ -public class MiwClientException extends EdcHttpClientException { - private final Response response; - - public MiwClientException(String message) { - this(message, null); - } - - public MiwClientException(String message, Response response) { - super(message); - this.response = response; - } - - public Response getResponse() { - return response; - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwFallbackFactories.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwFallbackFactories.java deleted file mode 100644 index a4638b2f9..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwFallbackFactories.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.api; - -import dev.failsafe.Fallback; -import dev.failsafe.event.ExecutionAttemptedEvent; -import dev.failsafe.function.CheckedFunction; -import okhttp3.Response; -import org.eclipse.edc.spi.http.FallbackFactory; - -import java.util.Arrays; -import java.util.stream.Collectors; - -import static java.lang.String.format; - -public interface MiwFallbackFactories { - - static FallbackFactory retryWhenStatusIsNot(int status) { - return retryWhenStatusIsNotIn(status); - } - - /** - * Verifies that the response has a specific statuses, otherwise it should be retried - * - * @return the {@link FallbackFactory} - */ - static FallbackFactory retryWhenStatusIsNotIn(int... status) { - var codes = Arrays.stream(status).boxed().collect(Collectors.toSet()); - return request -> { - CheckedFunction, Exception> exceptionSupplier = event -> { - var response = event.getLastResult(); - if (response == null) { - return new MiwClientException(event.getLastException().getMessage()); - } else { - return new MiwClientException(format("Server response to %s was not one of %s but was %s", request, Arrays.toString(status), response.code()), response); - } - }; - return Fallback.builderOfException(exceptionSupplier) - .handleResultIf(r -> !codes.contains(r.code())) - .build(); - }; - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/config/SsiMiwConfiguration.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/config/SsiMiwConfiguration.java deleted file mode 100644 index 028f11a8b..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/config/SsiMiwConfiguration.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.config; - -import java.util.Objects; - -public class SsiMiwConfiguration { - - protected String url; - protected String authorityId; - protected String authorityIssuer; - - public String getAuthorityId() { - return authorityId; - } - - public String getUrl() { - return url; - } - - public String getAuthorityIssuer() { - return authorityIssuer; - } - - public static class Builder { - private final SsiMiwConfiguration config; - - private Builder() { - config = new SsiMiwConfiguration(); - } - - public static Builder newInstance() { - return new Builder(); - } - - public Builder url(String url) { - config.url = url; - return this; - } - - public Builder authorityId(String authorityId) { - config.authorityId = authorityId; - return this; - } - - public Builder authorityIssuer(String authorityIssuer) { - config.authorityIssuer = authorityIssuer; - return this; - } - - public SsiMiwConfiguration build() { - Objects.requireNonNull(config.url); - Objects.requireNonNull(config.authorityIssuer); - Objects.requireNonNull(config.authorityId); - return config; - } - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/credentials/SsiMiwCredentialClient.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/credentials/SsiMiwCredentialClient.java deleted file mode 100644 index 2011cbd8f..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/credentials/SsiMiwCredentialClient.java +++ /dev/null @@ -1,130 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.credentials; - -import com.nimbusds.jwt.SignedJWT; -import jakarta.json.Json; -import org.eclipse.edc.jsonld.spi.JsonLd; -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.TokenParameters; -import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClient; -import org.eclipse.tractusx.edc.iam.ssi.spi.SsiCredentialClient; - -import java.text.ParseException; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE; -import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SCOPE; -import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClient.VP; - -public class SsiMiwCredentialClient implements SsiCredentialClient { - - private final MiwApiClient apiClient; - - private final JsonLd jsonLdService; - private final Monitor monitor; - - public SsiMiwCredentialClient(MiwApiClient apiClient, JsonLd jsonLdService, Monitor monitor) { - this.apiClient = apiClient; - this.jsonLdService = jsonLdService; - this.monitor = monitor; - } - - @Override - public Result obtainClientCredentials(TokenParameters parameters) { - var scopes = Arrays.stream(parameters.getStringClaim(SCOPE).split(" ")) - .map(String::trim) - .filter((s) -> !s.isEmpty()) - .collect(Collectors.toSet()); - - return apiClient.getCredentials(scopes) - .compose(credentials -> createPresentation(credentials, parameters)) - .compose(this::createToken); - } - - @Override - public Result validate(TokenRepresentation tokenRepresentation) { - return extractClaims(tokenRepresentation) - .compose(claimToken -> validatePresentation(claimToken, tokenRepresentation)); - } - - private Result createToken(Map presentationResponse) { - var vp = presentationResponse.get(VP); - if (vp instanceof String) { - return Result.success(TokenRepresentation.Builder.newInstance().token((String) vp).build()); - } else { - return Result.failure("Missing or invalid format for Verifiable Presentation"); - } - } - - private Result> createPresentation(List> credentials, TokenParameters tokenParameters) { - if (!credentials.isEmpty()) { - return apiClient.createPresentation(credentials, tokenParameters.getStringClaim(AUDIENCE)); - } else { - return Result.failure("Cannot create a presentation from an empty credentials list"); - } - } - - private Result validatePresentation(ClaimToken claimToken, TokenRepresentation tokenRepresentation) { - return claimToken.getListClaim(AUDIENCE).stream().map(String.class::cast).findFirst() - .map(audience -> apiClient.verifyPresentation(tokenRepresentation.getToken(), audience) - .compose(v -> Result.success(claimToken))) - .orElseGet(() -> Result.failure("Required audience (aud) claim is missing in token")); - } - - private Result extractClaims(TokenRepresentation tokenRepresentation) { - try { - var jwt = SignedJWT.parse(tokenRepresentation.getToken()); - - var tokenBuilder = ClaimToken.Builder.newInstance(); - jwt.getJWTClaimsSet().getClaims().entrySet().stream() - .filter(entry -> entry.getValue() != null) - .map(this::mapClaim) - .peek(this::logIfError) - .filter(Result::succeeded) - .map(Result::getContent) - .forEach(entry -> tokenBuilder.claim(entry.getKey(), entry.getValue())); - - return Result.success(tokenBuilder.build()); - } catch (ParseException e) { - return Result.failure(e.getMessage()); - } - } - - private Result> mapClaim(Map.Entry entry) { - if (entry.getKey().equals(VP)) { - var json = Json.createObjectBuilder((Map) entry.getValue()).build(); - return jsonLdService.expand(json) - .map((expanded) -> Map.entry(entry.getKey(), expanded)); - } else { - return Result.success(entry); - } - } - - private void logIfError(Result result) { - result.onFailure(f -> monitor.warning(f.getFailureDetail())); - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2ClientConfiguration.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2ClientConfiguration.java deleted file mode 100644 index c17f2dfae..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/oauth2/MiwOauth2ClientConfiguration.java +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.oauth2; - -/** - * Configuration of the OAuth2 client - */ -public class MiwOauth2ClientConfiguration { - private String tokenUrl; - private String clientId; - - private String clientSecret; - private String scope; - - public String getScope() { - return scope; - } - - public String getClientId() { - return clientId; - } - - public String getClientSecret() { - return clientSecret; - } - - public String getTokenUrl() { - return tokenUrl; - } - - public static class Builder { - private final MiwOauth2ClientConfiguration configuration = new MiwOauth2ClientConfiguration(); - - private Builder() { - } - - public static Builder newInstance() { - return new Builder(); - } - - public Builder tokenUrl(String url) { - configuration.tokenUrl = url; - return this; - } - - public Builder clientId(String clientId) { - configuration.clientId = clientId; - return this; - } - - public Builder scope(String scope) { - configuration.scope = scope; - return this; - } - - public Builder clientSecret(String clientSecret) { - configuration.clientSecret = clientSecret; - return this; - } - - public MiwOauth2ClientConfiguration build() { - return configuration; - } - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRule.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRule.java deleted file mode 100644 index 412ad9ada..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRule.java +++ /dev/null @@ -1,104 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.rule; - -import jakarta.json.JsonObject; -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.token.spi.TokenValidationRule; -import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdFieldExtractor; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; - -import static java.lang.String.format; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CREDENTIAL_ISSUER; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.SUMMARY_CREDENTIAL_TYPE; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTypeFunctions.extractObjectsOfType; - -/** - * {@link TokenValidationRule} that compares the issuer in the Verifiable Credential (Summary) with the one provided - * by configuration. - */ -public class SsiCredentialIssuerValidationRule implements TokenValidationRule { - - private static final String SUBJECT_ISSUER_EXTRACTOR_PREFIX = "Credential issuer extractor:"; - - private static final String SUBJECT_ISSUER_FIELD_ALIAS = "issuer"; - - private final String credentialIssuer; - - private final Monitor monitor; - - private final JsonLdFieldExtractor credentialIssuerExtractor = JsonLdFieldExtractor.Builder.newInstance() - .field(CREDENTIAL_ISSUER) - .fieldAlias(SUBJECT_ISSUER_FIELD_ALIAS) - .errorPrefix(SUBJECT_ISSUER_EXTRACTOR_PREFIX) - .build(); - - public SsiCredentialIssuerValidationRule(String credentialIssuer, Monitor monitor) { - this.credentialIssuer = credentialIssuer; - this.monitor = monitor; - } - - @Override - public Result checkRule(@NotNull ClaimToken toVerify, @Nullable Map additional) { - - var vp = (JsonObject) toVerify.getClaim(VP_PROPERTY); - - return Optional.ofNullable(vp) - .map(v -> extractObjectsOfType(SUMMARY_CREDENTIAL_TYPE, v)) - .orElse(Stream.empty()) - .map(this::extractIssuer) - .findFirst() - .orElseGet(() -> Result.failure("Failed to extract credential subject from the membership credential")) - .compose(this::validateCredentialIssuer) - .onFailure(failure -> monitor.severe(failure.getFailureDetail())); - - } - - private Result validateCredentialIssuer(String extractedCredentialIssuer) { - if (credentialIssuer.equals(extractedCredentialIssuer)) { - return Result.success(); - } else { - return Result.failure(format("Invalid credential issuer: expected %s, found %s", credentialIssuer, extractedCredentialIssuer)); - } - } - - private Result extractIssuer(JsonObject credential) { - return this.credentialIssuerExtractor.extract(credential) - .compose(this::extractIssuerValue); - } - - private Result extractIssuerValue(JsonObject issuer) { - var issuerValue = issuer.getString(ID); - if (issuerValue == null) { - return Result.failure("Failed to find the issuer"); - } else { - return Result.success(issuerValue); - } - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRule.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRule.java deleted file mode 100644 index 1c2e80315..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRule.java +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.rule; - -import jakarta.json.JsonObject; -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.token.spi.TokenValidationRule; -import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdFieldExtractor; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; - -import static java.lang.String.format; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; -import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CREDENTIAL_SUBJECT; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.SUMMARY_CREDENTIAL_TYPE; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTypeFunctions.extractObjectsOfType; - -/** - * {@link TokenValidationRule} that compares the issuer of the VP (JWT format) with the credential subject id of - * the Verifiable Credential (Summary) - */ -public class SsiCredentialSubjectIdValidationRule implements TokenValidationRule { - - private static final String CREDENTIAL_SUBJECT_EXTRACTOR_PREFIX = "Credential subject extractor:"; - private static final String CREDENTIAL_SUBJECT_FIELD_ALIAS = "credentialSubject"; - - private final Monitor monitor; - - private final JsonLdFieldExtractor credentialSubjectExtractor = JsonLdFieldExtractor.Builder.newInstance() - .field(CREDENTIAL_SUBJECT) - .fieldAlias(CREDENTIAL_SUBJECT_FIELD_ALIAS) - .errorPrefix(CREDENTIAL_SUBJECT_EXTRACTOR_PREFIX) - .build(); - - public SsiCredentialSubjectIdValidationRule(Monitor monitor) { - this.monitor = monitor; - } - - @Override - public Result checkRule(@NotNull ClaimToken toVerify, @Nullable Map additional) { - var issuer = toVerify.getStringClaim(ISSUER); - - if (issuer == null) { - return Result.failure("Required issuer (iss) claim is missing in token"); - } - var vp = (JsonObject) toVerify.getClaim(VP_PROPERTY); - - return Optional.ofNullable(vp) - .map(v -> extractObjectsOfType(SUMMARY_CREDENTIAL_TYPE, v)) - .orElse(Stream.empty()) - .map(this::extractSubjectId) - .findFirst() - .orElseGet(() -> Result.failure("Failed to extract credential subject from the membership credential")) - .compose(credentialSubjectId -> validateCredentialSubjectId(credentialSubjectId, issuer)) - .onFailure((failure -> monitor.severe(failure.getFailureDetail()))); - - } - - private Result validateCredentialSubjectId(String credentialSubjectId, String issuer) { - if (issuer.equals(credentialSubjectId)) { - return Result.success(); - } else { - return Result.failure(format("Issuer %s and credential subject id %s don't match", issuer, credentialSubjectId)); - } - } - - private Result extractSubjectId(JsonObject credential) { - return this.credentialSubjectExtractor.extract(credential) - .compose(this::extractId); - } - - private Result extractId(JsonObject credentialSubject) { - var id = credentialSubject.getString(ID); - if (id == null) { - return Result.failure("Failed to find the id in credential subject"); - } else { - return Result.success(id); - } - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/ssi/ssi-miw-credential-client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension deleted file mode 100644 index 6919e95a8..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ /dev/null @@ -1,24 +0,0 @@ -################################################################################# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# -# 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 -################################################################################# - -org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwCredentialClientExtension -org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwApiClientExtension -org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwOauth2ClientExtension -org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwValidationRuleExtension -org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwConfigurationExtension diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwConfigurationExtensionTest.java b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwConfigurationExtensionTest.java deleted file mode 100644 index c3333468e..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwConfigurationExtensionTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw; - -import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.configuration.Config; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwConfigurationExtension.AUTHORITY_ID_TEMPLATE; -import static org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwConfigurationExtension.MIW_AUTHORITY_ID; -import static org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwConfigurationExtension.MIW_AUTHORITY_ISSUER; -import static org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwConfigurationExtension.MIW_BASE_URL; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(DependencyInjectionExtension.class) -public class SsiMiwConfigurationExtensionTest { - - @Test - void initialize(ServiceExtensionContext context, SsiMiwConfigurationExtension extension) { - var url = "http://localhost:8080"; - var authorityId = "id"; - var authorityIssuer = "issuer"; - - var cfg = mock(Config.class); - when(context.getConfig()).thenReturn(cfg); - - when(cfg.getString(MIW_BASE_URL)).thenReturn(url); - when(cfg.getString(MIW_AUTHORITY_ID)).thenReturn(authorityId); - when(cfg.getString(eq(MIW_AUTHORITY_ISSUER), anyString())).thenReturn(authorityIssuer); - - var miwConfig = extension.miwConfiguration(context); - - verify(cfg).getString(MIW_BASE_URL); - verify(cfg).getString(MIW_AUTHORITY_ID); - verify(cfg).getString(eq(MIW_AUTHORITY_ISSUER), anyString()); - - assertThat(miwConfig.getUrl()).isEqualTo(url); - assertThat(miwConfig.getAuthorityId()).isEqualTo(authorityId); - assertThat(miwConfig.getAuthorityIssuer()).isEqualTo(authorityIssuer); - - } - - @Test - void initialize_withDefaultIssuer(ServiceExtensionContext context, SsiMiwConfigurationExtension extension) { - var url = "http://localhost:8080"; - var authorityId = "id"; - - var cfg = mock(Config.class); - when(context.getConfig()).thenReturn(cfg); - - when(cfg.getString(MIW_BASE_URL)).thenReturn(url); - when(cfg.getString(MIW_AUTHORITY_ID)).thenReturn(authorityId); - when(cfg.getString(eq(MIW_AUTHORITY_ISSUER), anyString())).thenAnswer(answer -> answer.getArgument(1)); - - var miwConfig = extension.miwConfiguration(context); - - verify(cfg).getString(MIW_BASE_URL); - verify(cfg).getString(MIW_AUTHORITY_ID); - verify(cfg).getString(eq(MIW_AUTHORITY_ISSUER), anyString()); - - assertThat(miwConfig.getUrl()).isEqualTo(url); - assertThat(miwConfig.getAuthorityId()).isEqualTo(authorityId); - assertThat(miwConfig.getAuthorityIssuer()).isEqualTo(format(AUTHORITY_ID_TEMPLATE, "localhost%3A8080", authorityId)); - - } - - @Test - void initialize_withTrailingUrl(ServiceExtensionContext context, SsiMiwConfigurationExtension extension) { - var url = "http://localhost:8080/"; - var authorityId = "id"; - - var cfg = mock(Config.class); - when(context.getConfig()).thenReturn(cfg); - - when(cfg.getString(MIW_BASE_URL)).thenReturn(url); - when(cfg.getString(MIW_AUTHORITY_ID)).thenReturn(authorityId); - when(cfg.getString(eq(MIW_AUTHORITY_ISSUER), anyString())).thenAnswer(answer -> answer.getArgument(1)); - - var miwConfig = extension.miwConfiguration(context); - - verify(cfg).getString(MIW_BASE_URL); - verify(cfg).getString(MIW_AUTHORITY_ID); - verify(cfg).getString(eq(MIW_AUTHORITY_ISSUER), anyString()); - - assertThat(miwConfig.getUrl()).isEqualTo("http://localhost:8080"); - assertThat(miwConfig.getAuthorityId()).isEqualTo(authorityId); - assertThat(miwConfig.getAuthorityIssuer()).isEqualTo(format(AUTHORITY_ID_TEMPLATE, "localhost%3A8080", authorityId)); - - } - -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwOauth2ClientExtensionTest.java b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwOauth2ClientExtensionTest.java deleted file mode 100644 index e35d733ff..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwOauth2ClientExtensionTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw; - -import org.assertj.core.api.InstanceOfAssertFactories; -import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.configuration.Config; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClient; -import org.eclipse.tractusx.edc.iam.ssi.miw.oauth2.MiwOauth2ClientConfiguration; -import org.eclipse.tractusx.edc.iam.ssi.miw.oauth2.MiwOauth2ClientImpl; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwOauth2ClientExtension.CLIENT_ID; -import static org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwOauth2ClientExtension.CLIENT_SECRET_ALIAS; -import static org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwOauth2ClientExtension.TOKEN_URL; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(DependencyInjectionExtension.class) -public class SsiMiwOauth2ClientExtensionTest { - - Vault vault = mock(Vault.class); - - @BeforeEach - void setup(ServiceExtensionContext context) { - context.registerService(MiwApiClient.class, mock(MiwApiClient.class)); - context.registerService(TypeManager.class, new TypeManager()); - context.registerService(Vault.class, vault); - } - - @Test - void initialize(ServiceExtensionContext context, SsiMiwOauth2ClientExtension extension) { - var config = mock(Config.class); - when(context.getConfig()).thenReturn(config); - when(config.getString(TOKEN_URL)).thenReturn("url"); - when(config.getString(CLIENT_ID)).thenReturn("clientId"); - when(config.getString(CLIENT_SECRET_ALIAS)).thenReturn("clientSecretAlias"); - when(vault.resolveSecret("clientSecretAlias")).thenReturn("clientSecret"); - - assertThat(extension.oauth2Client(context)).isInstanceOf(MiwOauth2ClientImpl.class); - verify(config).getString(TOKEN_URL); - verify(config).getString(CLIENT_ID); - verify(config).getString(CLIENT_SECRET_ALIAS); - verify(vault).resolveSecret("clientSecretAlias"); - } - - @Test - void initialize_withTrailingUrl(ServiceExtensionContext context, SsiMiwOauth2ClientExtension extension) { - var config = mock(Config.class); - when(context.getConfig()).thenReturn(config); - when(config.getString(TOKEN_URL)).thenReturn("http://localhost:8080/"); - when(config.getString(CLIENT_ID)).thenReturn("clientId"); - when(config.getString(CLIENT_SECRET_ALIAS)).thenReturn("clientSecretAlias"); - when(vault.resolveSecret("clientSecretAlias")).thenReturn("clientSecret"); - - assertThat(extension.oauth2Client(context)) - .asInstanceOf(InstanceOfAssertFactories.type(MiwOauth2ClientImpl.class)) - .extracting(MiwOauth2ClientImpl::getConfiguration) - .extracting(MiwOauth2ClientConfiguration::getTokenUrl) - .isEqualTo("http://localhost:8080"); - - verify(config).getString(TOKEN_URL); - verify(config).getString(CLIENT_ID); - verify(config).getString(CLIENT_SECRET_ALIAS); - verify(vault).resolveSecret("clientSecretAlias"); - } - - -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtensionTest.java b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtensionTest.java deleted file mode 100644 index 2273cb466..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtensionTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw; - -import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.token.spi.TokenValidationRulesRegistry; -import org.eclipse.tractusx.edc.iam.ssi.miw.config.SsiMiwConfiguration; -import org.eclipse.tractusx.edc.iam.ssi.miw.rule.SsiCredentialIssuerValidationRule; -import org.eclipse.tractusx.edc.iam.ssi.miw.rule.SsiCredentialSubjectIdValidationRule; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static org.eclipse.tractusx.edc.iam.ssi.spi.SsiConstants.SSI_TOKEN_CONTEXT; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(DependencyInjectionExtension.class) -public class SsiMiwValidationRuleExtensionTest { - - private final TokenValidationRulesRegistry registry = mock(TokenValidationRulesRegistry.class); - private final SsiMiwConfiguration cfg = mock(SsiMiwConfiguration.class); - - @BeforeEach - void setup(ServiceExtensionContext context) { - context.registerService(SsiMiwConfiguration.class, cfg); - context.registerService(TokenValidationRulesRegistry.class, registry); - } - - @Test - void initialize(ServiceExtensionContext context, SsiMiwValidationRuleExtension extension) { - when(cfg.getAuthorityIssuer()).thenReturn("issuer"); - - extension.initialize(context); - verify(registry).addRule(eq(SSI_TOKEN_CONTEXT), isA(SsiCredentialSubjectIdValidationRule.class)); - verify(registry).addRule(eq(SSI_TOKEN_CONTEXT), isA(SsiCredentialIssuerValidationRule.class)); - - verify(cfg).getAuthorityIssuer(); - } - -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImplTest.java b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImplTest.java deleted file mode 100644 index 83db6299b..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImplTest.java +++ /dev/null @@ -1,301 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.api; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import okhttp3.Interceptor; -import okhttp3.MediaType; -import okhttp3.Protocol; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.ResponseBody; -import okio.Buffer; -import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.tractusx.edc.iam.ssi.miw.oauth2.MiwOauth2Client; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.invocation.InvocationOnMock; - -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; - -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.junit.testfixtures.TestUtils.testHttpClient; -import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl.CREDENTIAL_PATH; -import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl.HOLDER_IDENTIFIER; -import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl.PRESENTATIONS_PATH; -import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl.PRESENTATIONS_VALIDATION_PATH; -import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl.VERIFIABLE_CREDENTIALS; -import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl.VP_FIELD; -import static org.mockito.ArgumentMatchers.contains; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class MiwApiClientImplTest { - - static final String BASE_URL = "http://localhost:8080"; - private final Consumer emptyAcceptor = (r) -> { - }; - Interceptor interceptor = mock(Interceptor.class); - MiwApiClientImpl client; - Monitor monitor = mock(Monitor.class); - MiwOauth2Client oauth2Client = mock(MiwOauth2Client.class); - ObjectMapper mapper = new ObjectMapper(); - String participantId = "participantId"; - String authorityId = "authorityId"; - - @BeforeEach - void setup() { - client = new MiwApiClientImpl(testHttpClient(interceptor), BASE_URL, oauth2Client, participantId, authorityId, mapper, monitor); - } - - @Test - void getCredentials() throws IOException { - - var credentialType = "test"; - - var response = Map.of("content", List.of(Map.of("id", "test"))); - var expectedUrl = format(BASE_URL + CREDENTIAL_PATH + "?issuerIdentifier=%s&type=%s", authorityId, credentialType); - - Consumer requestAcceptor = (request) -> { - assertThat(request.url().url().toString()).isEqualTo(expectedUrl); - }; - - when(interceptor.intercept(isA(Interceptor.Chain.class))) - .thenAnswer(invocation -> createResponse(200, invocation, requestAcceptor, response)); - - when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("testToken").build())); - - var result = client.getCredentials(Set.of("test")); - - assertThat(result).isNotNull().matches(Result::succeeded) - .extracting(Result::getContent) - .asList().hasSize(1); - } - - @Test - void getCredentials_fails_whenMiwFails() throws IOException { - - when(interceptor.intercept(isA(Interceptor.Chain.class))) - .thenAnswer(invocation -> createResponse(500, invocation)); - - when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("testToken").build())); - - var result = client.getCredentials(Set.of("test")); - - assertThat(result).isNotNull().matches(Result::failed); - } - - @Test - void getCredentials_fails_whenTokenRequestFails() { - - when(oauth2Client.obtainRequestToken()).thenReturn(Result.failure("Token fetch failure")); - - var result = client.getCredentials(Set.of("test")); - - assertThat(result).isNotNull().matches(Result::failed); - } - - @Test - void createPresentation() throws IOException { - var audience = "audience"; - var response = Map.of("vp", Map.of()); - var expectedUrl = format(BASE_URL + PRESENTATIONS_PATH + "?asJwt=true&audience=%s", audience); - - Consumer requestAcceptor = (request) -> { - var expectedBody = Map.of(HOLDER_IDENTIFIER, participantId, VERIFIABLE_CREDENTIALS, List.of()); - var body = getBody(request, new TypeReference>() { - }); - - assertThat(body).containsAllEntriesOf(expectedBody); - - assertThat(request.url().url().toString()).isEqualTo(expectedUrl); - }; - - when(interceptor.intercept(isA(Interceptor.Chain.class))) - .thenAnswer(invocation -> createResponse(200, invocation, requestAcceptor, response)); - - when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("testToken").build())); - - var result = client.createPresentation(List.of(), audience); - - assertThat(result).isNotNull().matches(Result::succeeded); - } - - @Test - void createPresentation_fails_whenMiwFails() throws IOException { - - when(interceptor.intercept(isA(Interceptor.Chain.class))) - .thenAnswer(invocation -> createResponse(500, invocation, emptyAcceptor, "Request Failed")); - - when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("testToken").build())); - - var result = client.createPresentation(List.of(), "audience"); - - assertThat(result).isNotNull().matches(Result::failed); - - verify(monitor).severe(contains("Request Failed")); - } - - @Test - void createPresentation_fails_whenTokenRequestFails() { - - when(oauth2Client.obtainRequestToken()).thenReturn(Result.failure("Token fetch failure")); - - var result = client.createPresentation(List.of(), "audience"); - - assertThat(result).isNotNull().matches(Result::failed); - } - - @Test - void verifyPresentation() throws IOException { - var jwt = "jwt"; - var verifyRequest = Map.of(VP_FIELD, jwt); - var audience = "audience"; - var expectedUrl = format(BASE_URL + PRESENTATIONS_VALIDATION_PATH + "?asJwt=true&audience=%s", audience); - - Consumer requestAcceptor = (request) -> { - - var body = getBody(request, new TypeReference>() { - }); - - assertThat(body).containsAllEntriesOf(verifyRequest); - assertThat(request.url().url().toString()).isEqualTo(expectedUrl); - }; - var verifyResponse = Map.of("valid", true); - - when(interceptor.intercept(isA(Interceptor.Chain.class))) - .thenAnswer(invocation -> createResponse(200, invocation, requestAcceptor, verifyResponse)); - - when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("testToken").build())); - - var result = client.verifyPresentation(jwt, "audience"); - - assertThat(result).isNotNull().matches(Result::succeeded); - } - - @Test - void verifyPresentation_fails_whenNotVerified() throws IOException { - var jwt = "jwt"; - var verifyRequest = Map.of(VP_FIELD, jwt); - var audience = "audience"; - var expectedUrl = format(BASE_URL + PRESENTATIONS_VALIDATION_PATH + "?asJwt=true&audience=%s", audience); - - Consumer requestAcceptor = (request) -> { - - var body = getBody(request, new TypeReference>() { - }); - - assertThat(body).containsAllEntriesOf(verifyRequest); - assertThat(request.url().url().toString()).isEqualTo(expectedUrl); - }; - var verifyResponse = Map.of("valid", false); - - when(interceptor.intercept(isA(Interceptor.Chain.class))) - .thenAnswer(invocation -> createResponse(200, invocation, requestAcceptor, verifyResponse)); - - when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("testToken").build())); - - var result = client.verifyPresentation(jwt, "audience"); - - assertThat(result).isNotNull().matches(Result::failed); - } - - @Test - void verifyPresentation_fails_whenMiwFails() throws IOException { - - when(interceptor.intercept(isA(Interceptor.Chain.class))) - .thenAnswer(invocation -> createResponse(500, invocation)); - - when(oauth2Client.obtainRequestToken()).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("testToken").build())); - - var result = client.verifyPresentation("jwt", "audience"); - - assertThat(result).isNotNull().matches(Result::failed); - } - - @Test - void verifyPresentation_fails_whenTokenRequestFails() throws IOException { - - when(oauth2Client.obtainRequestToken()).thenReturn(Result.failure("Token fetch failure")); - - var result = client.verifyPresentation("jwt", "audience"); - - assertThat(result).isNotNull().matches(Result::failed); - } - - - private Response createResponse(int code, InvocationOnMock invocation) { - return createResponse(code, invocation, (req) -> { - }, null); - } - - private Response createResponse(int code, InvocationOnMock invocation, Object body) { - return createResponse(code, invocation, (req) -> { - }, body); - } - - - private Response createResponse(int code, InvocationOnMock invocation, Consumer consumer, Object body) { - var bodyString = Optional.ofNullable(body).map(this::toJson).orElse(""); - var request = getRequest(invocation); - consumer.accept(request); - return new Response.Builder() - .protocol(Protocol.HTTP_1_1) - .request(request) - .code(code) - .message("") - .body(ResponseBody.create(bodyString, MediaType.parse("application/json"))) - .build(); - } - - private String toJson(Object body) { - try { - return mapper.writeValueAsString(body); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - private T getBody(Request request, TypeReference typeReference) { - try (var buffer = new Buffer()) { - Objects.requireNonNull(request.body()).writeTo(buffer); - return mapper.readValue(buffer.inputStream(), typeReference); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private Request getRequest(InvocationOnMock invocation) { - return invocation.getArgument(0, Interceptor.Chain.class).request(); - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/credentials/SsiMiwCredentialClientTest.java b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/credentials/SsiMiwCredentialClientTest.java deleted file mode 100644 index 37725dbe3..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/credentials/SsiMiwCredentialClientTest.java +++ /dev/null @@ -1,157 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.credentials; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.crypto.RSASSASigner; -import com.nimbusds.jose.jwk.KeyUse; -import com.nimbusds.jose.jwk.RSAKey; -import com.nimbusds.jose.jwk.gen.RSAKeyGenerator; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import jakarta.json.Json; -import org.eclipse.edc.jsonld.spi.JsonLd; -import org.eclipse.edc.spi.iam.TokenParameters; -import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClient; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.security.PrivateKey; -import java.time.Instant; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE; -import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SCOPE; -import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClient.VP; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - -public class SsiMiwCredentialClientTest { - - private final String audience = "audience"; - SsiMiwCredentialClient credentialClient; - MiwApiClient apiClient = mock(MiwApiClient.class); - JsonLd jsonLdService = mock(JsonLd.class); - - Monitor monitor = mock(Monitor.class); - private RSAKey key; - - @BeforeEach - void setup() throws JOSEException { - credentialClient = new SsiMiwCredentialClient(apiClient, jsonLdService, monitor); - key = testKey(); - } - - @Test - void validate_success() throws JOSEException { - var claims = createClaims(Instant.now()); - var jwt = createJwt(UUID.randomUUID().toString(), claims, key.toPrivateKey()); - when(apiClient.verifyPresentation(jwt, audience)).thenReturn(Result.success()); - when(jsonLdService.expand(any())).thenReturn(Result.success(Json.createObjectBuilder().build())); - - var result = credentialClient.validate(TokenRepresentation.Builder.newInstance().token(jwt).build()); - - assertThat(result).isNotNull().matches(Result::succeeded); - verify(apiClient).verifyPresentation(jwt, audience); - } - - @Test - void validate_success_whenClientFails() throws JOSEException { - var claims = createClaims(Instant.now()); - var jwt = createJwt(UUID.randomUUID().toString(), claims, key.toPrivateKey()); - when(apiClient.verifyPresentation(jwt, audience)).thenReturn(Result.failure("fail")); - when(jsonLdService.expand(any())).thenReturn(Result.success(Json.createObjectBuilder().build())); - - var result = credentialClient.validate(TokenRepresentation.Builder.newInstance().token(jwt).build()); - - assertThat(result).isNotNull().matches(Result::failed); - verify(apiClient).verifyPresentation(jwt, audience); - } - - @Test - void validate_fail_whenInvalidToken() throws JOSEException { - - var result = credentialClient.validate(TokenRepresentation.Builder.newInstance().token("invalid").build()); - - assertThat(result).isNotNull().matches(Result::failed); - verifyNoInteractions(apiClient); - } - - @Test - void obtainCredentials_success() { - - var jwt = "serialized"; - Map credential = Map.of(); - Map presentation = Map.of(VP, jwt); - - var credentials = List.of(credential); - - when(apiClient.getCredentials(Set.of())).thenReturn(Result.success(credentials)); - when(apiClient.createPresentation(credentials, audience)).thenReturn(Result.success(presentation)); - var result = credentialClient.obtainClientCredentials(TokenParameters.Builder.newInstance().claims(AUDIENCE, audience).claims(SCOPE, "").build()); - - assertThat(result).isNotNull() - .extracting(Result::getContent) - .extracting(TokenRepresentation::getToken) - .isEqualTo(jwt); - - verify(apiClient).getCredentials(Set.of()); - } - - private JWTClaimsSet createClaims(Instant exp) { - return new JWTClaimsSet.Builder() - .claim("foo", "bar") - .claim(VP, Map.of()) - .audience(audience) - .expirationTime(Date.from(exp)) - .build(); - } - - private String createJwt(String publicKeyId, JWTClaimsSet claimsSet, PrivateKey pk) { - var header = new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(publicKeyId).build(); - try { - SignedJWT jwt = new SignedJWT(header, claimsSet); - jwt.sign(new RSASSASigner(pk)); - return jwt.serialize(); - } catch (JOSEException e) { - throw new AssertionError(e); - } - } - - private RSAKey testKey() throws JOSEException { - return new RSAKeyGenerator(2048) - .keyUse(KeyUse.SIGNATURE) // indicate the intended use of the key - .keyID(UUID.randomUUID().toString()) // give the key a unique ID - .generate(); - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRuleTest.java b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRuleTest.java deleted file mode 100644 index 22dbf4dcf..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRuleTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.rule; - -import com.fasterxml.jackson.core.JsonProcessingException; -import jakarta.json.JsonObject; -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryContext; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_SUMMARY_NS_V1; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.createObjectMapper; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.expand; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP; -import static org.mockito.Mockito.mock; - -public class SsiCredentialIssuerValidationRuleTest { - - static final Map CONTEXT_CACHE = Map.of(CX_SUMMARY_NS_V1, SummaryContext.SUMMARY_CONTEXT); - - SsiCredentialIssuerValidationRule validationRule; - - @Test - void checkRule() throws JsonProcessingException { - validationRule = new SsiCredentialIssuerValidationRule("did:web:issuer-example.com", mock(Monitor.class)); - var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); - var claimToken = ClaimToken.Builder.newInstance().claim(VP_PROPERTY, vp).build(); - var result = validationRule.checkRule(claimToken, Map.of()); - - assertThat(result.succeeded()).isTrue(); - } - - - @Test - void checkRule_shouldFail_whenIssuerIsWrong() throws JsonProcessingException { - validationRule = new SsiCredentialIssuerValidationRule("issuer", mock(Monitor.class)); - var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); - var claimToken = ClaimToken.Builder.newInstance().claim(VP_PROPERTY, vp).build(); - var result = validationRule.checkRule(claimToken, Map.of()); - - assertThat(result.succeeded()).isFalse(); - } -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRuleTest.java b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRuleTest.java deleted file mode 100644 index 19d001e02..000000000 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRuleTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.miw.rule; - -import com.fasterxml.jackson.core.JsonProcessingException; -import jakarta.json.JsonObject; -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryContext; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_SUMMARY_NS_V1; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.createObjectMapper; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.expand; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP; -import static org.mockito.Mockito.mock; - -public class SsiCredentialSubjectIdValidationRuleTest { - - static final Map CONTEXT_CACHE = Map.of(CX_SUMMARY_NS_V1, SummaryContext.SUMMARY_CONTEXT); - - private final SsiCredentialSubjectIdValidationRule validationRule = new SsiCredentialSubjectIdValidationRule(mock(Monitor.class)); - - @Test - void checkRule() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); - var claimToken = ClaimToken.Builder.newInstance() - .claim(VP_PROPERTY, vp) - .claim(ISSUER, "did:web:example.com").build(); - - var result = validationRule.checkRule(claimToken, Map.of()); - - assertThat(result.succeeded()).isTrue(); - } - - @Test - void checkRule_shouldFail_whenIssuerMissingInClaims() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); - var claimToken = ClaimToken.Builder.newInstance() - .claim(VP_PROPERTY, vp) - .build(); - - var result = validationRule.checkRule(claimToken, Map.of()); - - assertThat(result.succeeded()).isFalse(); - } - - @Test - void checkRule_shouldFail_whenWrongIssuerInClaims() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); - var claimToken = ClaimToken.Builder.newInstance() - .claim(VP_PROPERTY, vp) - .claim(ISSUER, "wrong").build(); - - var result = validationRule.checkRule(claimToken, Map.of()); - - assertThat(result.succeeded()).isFalse(); - } - -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/build.gradle.kts b/edc-extensions/tokenrefresh-handler/build.gradle.kts similarity index 72% rename from edc-extensions/ssi/ssi-miw-credential-client/build.gradle.kts rename to edc-extensions/tokenrefresh-handler/build.gradle.kts index c5babfce4..535170d42 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/build.gradle.kts +++ b/edc-extensions/tokenrefresh-handler/build.gradle.kts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -18,21 +18,24 @@ ********************************************************************************/ plugins { - `java-library` `maven-publish` + `java-library` } dependencies { - implementation(project(":spi:ssi-spi")) + implementation(project(":core:core-utils")) + implementation(project(":spi:core-spi")) + implementation(project(":spi:tokenrefresh-spi")) implementation(libs.edc.spi.core) + implementation(libs.edc.spi.edrstore) implementation(libs.edc.spi.http) - implementation(libs.edc.spi.jsonld) - implementation(libs.edc.auth.oauth2.client) - implementation(libs.edc.spi.jwt) implementation(libs.edc.spi.token) - implementation(libs.jakartaJson) + implementation(libs.edc.spi.jwt) + implementation(libs.edc.spi.identitytrust) + implementation(libs.edc.lib.util) implementation(libs.nimbus.jwt) - testImplementation(testFixtures(project(":spi:ssi-spi"))) - testImplementation(testFixtures(libs.edc.junit)) + + testImplementation(libs.edc.junit) + testImplementation(libs.restAssured) } diff --git a/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerExtension.java b/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerExtension.java new file mode 100644 index 000000000..2208cf6b8 --- /dev/null +++ b/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerExtension.java @@ -0,0 +1,68 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.common.tokenrefresh; + +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceCache; +import org.eclipse.edc.http.spi.EdcHttpClient; +import org.eclipse.edc.iam.identitytrust.spi.SecureTokenService; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.tractusx.edc.core.utils.RequiredConfigWarnings; +import org.eclipse.tractusx.edc.spi.tokenrefresh.common.TokenRefreshHandler; + +import static org.eclipse.tractusx.edc.common.tokenrefresh.TokenRefreshHandlerExtension.NAME; + + +@Extension(value = NAME) +public class TokenRefreshHandlerExtension implements ServiceExtension { + public static final String NAME = "Token Refresh Handler Extension"; + // this setting is defined by the IdentityAndTrustExtension + private static final String PARTICIPANT_DID_PROPERTY = "edc.iam.issuer.id"; + @Inject + private EndpointDataReferenceCache edrStore; + @Inject + private EdcHttpClient httpClient; + @Inject + private SecureTokenService secureTokenService; + @Inject + private TypeManager typeManager; + + @Override + public String name() { + return NAME; + } + + @Provider + public TokenRefreshHandler createTokenRefreshHander(ServiceExtensionContext context) { + return new TokenRefreshHandlerImpl(edrStore, httpClient, getOwnDid(context), context.getMonitor(), secureTokenService, typeManager.getMapper()); + } + + private String getOwnDid(ServiceExtensionContext context) { + var did = context.getConfig().getString(PARTICIPANT_DID_PROPERTY, null); + if (did == null) { + RequiredConfigWarnings.warningNotPresent(context.getMonitor().withPrefix("Token Refresh Handler"), PARTICIPANT_DID_PROPERTY); + } + return did; + } +} diff --git a/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImpl.java b/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImpl.java new file mode 100644 index 000000000..7ede6ca8e --- /dev/null +++ b/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImpl.java @@ -0,0 +1,186 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.common.tokenrefresh; + +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.HttpUrl; +import okhttp3.Request; +import okhttp3.RequestBody; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceCache; +import org.eclipse.edc.http.spi.EdcHttpClient; +import org.eclipse.edc.iam.identitytrust.spi.SecureTokenService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.result.ServiceResult; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.util.string.StringUtils; +import org.eclipse.tractusx.edc.spi.tokenrefresh.common.TokenRefreshHandler; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; + +import java.io.IOException; +import java.util.Map; + +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.JWT_ID; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SUBJECT; +import static org.eclipse.edc.spi.result.Result.success; +import static org.eclipse.edc.util.string.StringUtils.isNullOrBlank; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_AUTHORIZATION; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_EXPIRES_IN; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_AUDIENCE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_ENDPOINT; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_TOKEN; + +public class TokenRefreshHandlerImpl implements TokenRefreshHandler { + private final EndpointDataReferenceCache edrCache; + private final EdcHttpClient httpClient; + private final String ownDid; + private final Monitor monitor; + private final SecureTokenService secureTokenService; + private final ObjectMapper objectMapper; + + /** + * Creates a new TokenRefreshHandler + * + * @param edrCache a persistent storage where {@link DataAddress} objects are stored. + * @param httpClient needed to make the actual refresh call against the refresh endpoint + * @param ownDid the DID of this connector + * @param secureTokenService Service to generate the authentication token + * @param objectMapper ObjectMapper to interpret JSON responses + */ + public TokenRefreshHandlerImpl(EndpointDataReferenceCache edrCache, + EdcHttpClient httpClient, + String ownDid, + Monitor monitor, + SecureTokenService secureTokenService, + ObjectMapper objectMapper) { + this.edrCache = edrCache; + this.httpClient = httpClient; + this.ownDid = ownDid; + this.monitor = monitor; + this.secureTokenService = secureTokenService; + this.objectMapper = objectMapper; + } + + @Override + public ServiceResult refreshToken(String tokenId) { + var edrResult = edrCache.get(tokenId); + if (edrResult.failed()) { + return ServiceResult.notFound(edrResult.getFailureDetail()); + } + var edr = edrResult.getContent(); + return refreshToken(tokenId, edr); + } + + @Override + public ServiceResult refreshToken(String tokenId, DataAddress edr) { + var accessToken = edr.getStringProperty(EDR_PROPERTY_AUTHORIZATION); + var refreshToken = edr.getProperties().get(EDR_PROPERTY_REFRESH_TOKEN); + var refreshEndpoint = edr.getProperties().get(EDR_PROPERTY_REFRESH_ENDPOINT); + var refreshAudience = edr.getProperties().get(EDR_PROPERTY_REFRESH_AUDIENCE); + + if (isNullOrBlank(accessToken)) { + return ServiceResult.badRequest("Cannot perform token refresh: required property 'authorization' not found on EDR."); + } + if (isNullOrBlank(StringUtils.toString(refreshToken))) { + return ServiceResult.badRequest("Cannot perform token refresh: required property 'refreshToken' not found on EDR."); + } + if (isNullOrBlank(StringUtils.toString(refreshEndpoint))) { + return ServiceResult.badRequest("Cannot perform token refresh: required property 'refreshEndpoint' not found on EDR."); + } + if (isNullOrBlank(StringUtils.toString(refreshAudience))) { + return ServiceResult.badRequest("Cannot perform token refresh: required property 'refreshAudience' not found on EDR."); + } + + var claims = Map.of( + JWT_ID, tokenId, + ISSUER, ownDid, + SUBJECT, ownDid, + AUDIENCE, refreshAudience.toString(), + "token", accessToken + ); + + var result = secureTokenService.createToken(claims, null) + .compose(authToken -> createTokenRefreshRequest(refreshEndpoint.toString(), refreshToken.toString(), "Bearer %s".formatted(authToken.getToken()))); + + if (result.failed()) { + return ServiceResult.badRequest("Could not execute token refresh: " + result.getFailureDetail()); + } + + return executeRequest(result.getContent()) + .map(tr -> createNewEdr(edr, tr)); + } + + private DataAddress createNewEdr(DataAddress oldEdr, TokenResponse tokenResponse) { + return DataAddress.Builder.newInstance() + .type(oldEdr.getType()) + .properties(oldEdr.getProperties()) + .property(EDR_PROPERTY_AUTHORIZATION, tokenResponse.accessToken()) + .property(EDR_PROPERTY_REFRESH_TOKEN, tokenResponse.refreshToken()) + .property(EDR_PROPERTY_EXPIRES_IN, String.valueOf(tokenResponse.expiresInSeconds())) + .build(); + } + + private ServiceResult executeRequest(Request tokenRefreshRequest) { + try (var response = httpClient.execute(tokenRefreshRequest)) { + if (response.isSuccessful()) { + if (response.body() != null) { + + var jsonBody = response.body().string(); + if (!StringUtils.isNullOrEmpty(jsonBody)) { + var tokenResponse = objectMapper.readValue(jsonBody, TokenResponse.class); + return ServiceResult.success(tokenResponse); + } + } + return ServiceResult.badRequest("Token refresh successful, but body was empty."); + } + return switch (response.code()) { + case 401 -> ServiceResult.unauthorized(response.message()); + case 409 -> ServiceResult.conflict(response.message()); + case 404 -> ServiceResult.notFound(response.message()); + default -> ServiceResult.badRequest(response.message()); + }; + } catch (IOException e) { + monitor.warning("Error executing token refresh request", e); + return ServiceResult.from(StoreResult.generalError("Error executing token refresh request: %s".formatted(e))); + } + } + + private Result createTokenRefreshRequest(String refreshEndpoint, String refreshToken, String bearerToken) { + // see https://github.com/eclipse-tractusx/tractusx-profiles/blob/main/tx/refresh/refresh.token.grant.profile.md#3-the-refresh-request + if (!refreshEndpoint.endsWith("/token")) { + refreshEndpoint += "/token"; + } + var url = HttpUrl.parse(refreshEndpoint) + .newBuilder() + .addQueryParameter("grant_type", "refresh_token") + .addQueryParameter("refresh_token", refreshToken) + .build(); + + return success(new Request.Builder() + .addHeader("Authorization", bearerToken) + .url(url) + .post(RequestBody.create(new byte[0])) + .build()); + } + +} diff --git a/edc-extensions/tokenrefresh-handler/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/tokenrefresh-handler/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..4c237a271 --- /dev/null +++ b/edc-extensions/tokenrefresh-handler/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,20 @@ +################################################################################# +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft +# +# 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 +################################################################################# + +org.eclipse.tractusx.edc.common.tokenrefresh.TokenRefreshHandlerExtension \ No newline at end of file diff --git a/edc-extensions/tokenrefresh-handler/src/test/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImplTest.java b/edc-extensions/tokenrefresh-handler/src/test/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImplTest.java new file mode 100644 index 000000000..e210ecbad --- /dev/null +++ b/edc-extensions/tokenrefresh-handler/src/test/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImplTest.java @@ -0,0 +1,226 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.common.tokenrefresh; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import okhttp3.MediaType; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.assertj.core.api.Assertions; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceCache; +import org.eclipse.edc.http.spi.EdcHttpClient; +import org.eclipse.edc.iam.identitytrust.spi.SecureTokenService; +import org.eclipse.edc.spi.iam.TokenRepresentation; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +import java.io.IOException; +import java.util.stream.Stream; + +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_AUTHORIZATION; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_EXPIRES_IN; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_AUDIENCE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_ENDPOINT; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_TOKEN; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + + +class TokenRefreshHandlerImplTest { + public static final String REFRESH_ENDPOINT = "http://fizz.buzz/quazz"; + private static final String CONSUMER_DID = "did:web:bob"; + private static final String PROVIDER_DID = "did:web:alice"; + private final EndpointDataReferenceCache edrCache = mock(); + private final EdcHttpClient mockedHttpClient = mock(); + private final SecureTokenService mockedTokenService = mock(); + private TokenRefreshHandlerImpl tokenRefreshHandler; + private ObjectMapper objectMapper; + + private static String createJwt() { + try { + var providerKey = new ECKeyGenerator(Curve.P_256).generate(); + var signedJwt = new SignedJWT(new JWSHeader(JWSAlgorithm.ES256), new JWTClaimsSet.Builder().issuer(PROVIDER_DID).build()); + signedJwt.sign(new ECDSASigner(providerKey)); + return signedJwt.serialize(); + } catch (JOSEException e) { + throw new RuntimeException(e); + } + } + + @BeforeEach + void setup() { + objectMapper = new ObjectMapper(); + tokenRefreshHandler = new TokenRefreshHandlerImpl(edrCache, mockedHttpClient, CONSUMER_DID, mock(), + mockedTokenService, objectMapper); + } + + @Test + void refresh_validateCorrectRequest() throws IOException { + when(edrCache.get(anyString())).thenReturn(StoreResult.success(createEdr().build())); + when(mockedTokenService.createToken(anyMap(), isNull())).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-auth-token").build())); + var tokenResponse = new TokenResponse("new-access-token", "new-refresh-token", 60 * 5L, "bearer"); + var successResponse = createResponse(tokenResponse, 200, ""); + when(mockedHttpClient.execute(any())).thenReturn(successResponse); + var res = tokenRefreshHandler.refreshToken("token-id"); + assertThat(res).isSucceeded() + .satisfies(tr -> { + Assertions.assertThat(tr.getProperties()).containsEntry(EDR_PROPERTY_AUTHORIZATION, "new-access-token"); + Assertions.assertThat(tr.getProperties()).containsEntry(EDR_PROPERTY_EXPIRES_IN, "300"); + Assertions.assertThat(tr.getProperties()).containsEntry(EDR_PROPERTY_REFRESH_TOKEN, "new-refresh-token"); + Assertions.assertThat(tr.getProperties()).containsEntry(EDR_PROPERTY_REFRESH_ENDPOINT, REFRESH_ENDPOINT); + }); + } + + @Test + void refresh_edrNotFound() { + when(edrCache.get(anyString())).thenReturn(StoreResult.notFound("foo")); + + assertThat(tokenRefreshHandler.refreshToken("token-id")).isFailed() + .detail().isEqualTo("foo"); + + verify(edrCache).get(eq("token-id")); + verifyNoMoreInteractions(edrCache); + verifyNoInteractions(mockedHttpClient, mockedTokenService); + } + + @ParameterizedTest(name = "{3}") + @ArgumentsSource(InvalidEdrProvider.class) + void refresh_edrLacksRequiredProperties(String authorization, String refreshToken, String refreshEndpoint, String desc) { + var invalidEdr = DataAddress.Builder.newInstance().type("test-type") + .property(EDR_PROPERTY_AUTHORIZATION, authorization) + .property(EDR_PROPERTY_REFRESH_TOKEN, refreshToken) + .property(EDR_PROPERTY_REFRESH_ENDPOINT, refreshEndpoint) + .build(); + when(edrCache.get(anyString())).thenReturn(StoreResult.success(invalidEdr)); + + assertThat(tokenRefreshHandler.refreshToken("token-id")).isFailed() + .detail() + .matches("^Cannot perform token refresh: required property '(authorization|refreshToken|refreshEndpoint)' not found on EDR.$"); + } + + @Test + void refresh_endpointReturnsFailure() throws IOException { + when(edrCache.get(anyString())).thenReturn(StoreResult.success(createEdr().build())); + when(mockedTokenService.createToken(anyMap(), isNull())).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-auth-token").build())); + var response401 = createResponse(null, 401, "Not authorized"); + + when(mockedHttpClient.execute(any())).thenReturn(response401); + + var res = tokenRefreshHandler.refreshToken("token-id"); + assertThat(res).isFailed() + .detail().isEqualTo("Not authorized"); + } + + @Test + void refresh_endpointReturnsEmptyBody() throws IOException { + when(edrCache.get(anyString())).thenReturn(StoreResult.success(createEdr().build())); + when(mockedTokenService.createToken(anyMap(), isNull())).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-auth-token").build())); + var successResponse = createResponse(null, 200, ""); + when(mockedHttpClient.execute(any())).thenReturn(successResponse); + + var res = tokenRefreshHandler.refreshToken("token-id"); + assertThat(res).isFailed() + .detail().isEqualTo("Token refresh successful, but body was empty."); + } + + @Test + void refresh_ioException() throws IOException { + when(edrCache.get(anyString())).thenReturn(StoreResult.success(createEdr().build())); + when(mockedTokenService.createToken(anyMap(), isNull())).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-auth-token").build())); + when(mockedHttpClient.execute(any())).thenThrow(new IOException("test exception")); + + assertThat(tokenRefreshHandler.refreshToken("token-id")).isFailed() + .detail().isEqualTo("Error executing token refresh request: java.io.IOException: test exception"); + } + + @Test + void refresh_tokenGenerationFailed() { + when(edrCache.get(anyString())).thenReturn(StoreResult.success(createEdr().build())); + when(mockedTokenService.createToken(anyMap(), isNull())).thenReturn(Result.failure("foobar")); + assertThat(tokenRefreshHandler.refreshToken("token-id")).isFailed() + .detail().isEqualTo("Could not execute token refresh: foobar"); + } + + @NotNull + private Response createResponse(Object responseBodyObject, int statusCode, String message) throws JsonProcessingException { + var body = responseBodyObject == null ? new byte[0] : objectMapper.writeValueAsBytes(responseBodyObject); + return new Response.Builder() + .code(statusCode) + .protocol(Protocol.HTTP_1_1) + .message(message) + .request(new Request.Builder().url(REFRESH_ENDPOINT).build()) + .body(ResponseBody.create(body, MediaType.parse("application/json"))) + .build(); + } + + private DataAddress.Builder createEdr() { + return DataAddress.Builder.newInstance() + .type("HttpData") + .property(EDR_PROPERTY_AUTHORIZATION, createJwt()) + .property(EDR_PROPERTY_REFRESH_TOKEN, "foo-refresh-token") + .property(EDR_PROPERTY_REFRESH_ENDPOINT, REFRESH_ENDPOINT) + .property(EDR_PROPERTY_REFRESH_AUDIENCE, CONSUMER_DID); + } + + private static class InvalidEdrProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) { + return Stream.of( + Arguments.of(createJwt(), "foo-refresh-token", null, "refresh endpoint is null"), + Arguments.of(createJwt(), "foo-refresh-token", "", "refresh endpoint is empty"), + Arguments.of(createJwt(), "foo-refresh-token", " ", "refresh endpoint is blank"), + Arguments.of(createJwt(), null, REFRESH_ENDPOINT, "refresh token is null"), + Arguments.of(createJwt(), "", REFRESH_ENDPOINT, "refresh token is empty"), + Arguments.of(createJwt(), " ", REFRESH_ENDPOINT, "refresh token is blank"), + Arguments.of(null, "foo-refresh-token", REFRESH_ENDPOINT, "access token is null") + ); + } + } +} \ No newline at end of file diff --git a/edc-extensions/transferprocess-sftp-client/build.gradle.kts b/edc-extensions/transferprocess-sftp-client/build.gradle.kts index 67501fb0d..5ca1d98e2 100644 --- a/edc-extensions/transferprocess-sftp-client/build.gradle.kts +++ b/edc-extensions/transferprocess-sftp-client/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { implementation(libs.edc.spi.dataplane.dataplane) implementation(libs.edc.dpf.util) implementation(libs.edc.dpf.core) - implementation(libs.edc.policy.engine) + implementation(libs.edc.lib.policyengine) implementation(libs.bouncyCastle.bcpkixJdk18on) implementation(libs.apache.sshd.core) diff --git a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSinkFactory.java b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSinkFactory.java index 82c9a56d5..13089d78e 100644 --- a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSinkFactory.java +++ b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSinkFactory.java @@ -23,7 +23,7 @@ import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSink; import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSinkFactory; import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; +import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; import org.eclipse.tractusx.edc.transferprocess.sftp.common.EdcSftpException; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; import org.jetbrains.annotations.NotNull; @@ -32,7 +32,7 @@ public class SftpDataSinkFactory implements DataSinkFactory { @Override - public boolean canHandle(DataFlowRequest request) { + public boolean canHandle(DataFlowStartMessage request) { try { SftpDataAddress.fromDataAddress(request.getDestinationDataAddress()); return true; @@ -42,7 +42,7 @@ public boolean canHandle(DataFlowRequest request) { } @Override - public DataSink createSink(DataFlowRequest request) { + public DataSink createSink(DataFlowStartMessage request) { if (!canHandle(request)) { return null; } @@ -63,9 +63,9 @@ public DataSink createSink(DataFlowRequest request) { } @Override - public @NotNull Result validateRequest(DataFlowRequest request) { + public @NotNull Result validateRequest(DataFlowStartMessage request) { if (!canHandle(request)) { - return Result.failure(String.format("Invalid DataFlowRequest: %s", request.getId())); + return Result.failure(String.format("Invalid DataFlowStartMessage: %s", request.getId())); } return Result.success(); diff --git a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceFactory.java b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceFactory.java index 4254dd9fc..b85fa3410 100644 --- a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceFactory.java +++ b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceFactory.java @@ -22,14 +22,14 @@ import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSource; import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSourceFactory; import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; +import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; import org.eclipse.tractusx.edc.transferprocess.sftp.common.EdcSftpException; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; import org.jetbrains.annotations.NotNull; public class SftpDataSourceFactory implements DataSourceFactory { @Override - public boolean canHandle(DataFlowRequest request) { + public boolean canHandle(DataFlowStartMessage request) { try { SftpDataAddress.fromDataAddress(request.getSourceDataAddress()); return true; @@ -39,7 +39,7 @@ public boolean canHandle(DataFlowRequest request) { } @Override - public DataSource createSource(DataFlowRequest request) { + public DataSource createSource(DataFlowStartMessage request) { if (!canHandle(request)) { return null; } @@ -57,9 +57,9 @@ public DataSource createSource(DataFlowRequest request) { } @Override - public @NotNull Result validateRequest(DataFlowRequest request) { + public @NotNull Result validateRequest(DataFlowStartMessage request) { if (!canHandle(request)) { - return Result.failure(String.format("Invalid DataFlowRequest: %s", request.getId())); + return Result.failure(String.format("Invalid DataFlowStartMessage: %s", request.getId())); } return Result.success(); } diff --git a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSinkFactoryTest.java b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSinkFactoryTest.java index 57f4171cb..85225e4e3 100644 --- a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSinkFactoryTest.java +++ b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSinkFactoryTest.java @@ -22,7 +22,7 @@ import org.apache.sshd.sftp.client.SftpClient; import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; +import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; @@ -43,7 +43,7 @@ void validate_valid() { var sftpDataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - var request = Mockito.mock(DataFlowRequest.class); + var request = Mockito.mock(DataFlowStartMessage.class); Mockito.when(request.getDestinationDataAddress()).thenReturn(sftpDataAddress); Assertions.assertTrue(dataSinkFactory.validateRequest(request).succeeded()); @@ -53,7 +53,7 @@ void validate_valid() { void validate_invalidDataAddressType() { var dataSinkFactory = new SftpDataSinkFactory(); var dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); - var request = Mockito.mock(DataFlowRequest.class); + var request = Mockito.mock(DataFlowStartMessage.class); Mockito.when(request.getDestinationDataAddress()).thenReturn(dataAddress); Assertions.assertTrue(dataSinkFactory.validateRequest(request).failed()); @@ -73,7 +73,7 @@ void validate_invalidDataAddressParameters() { final var dataAddress = DataAddress.Builder.newInstance().properties(properties).build(); - var request = Mockito.mock(DataFlowRequest.class); + var request = Mockito.mock(DataFlowStartMessage.class); Mockito.when(request.getDestinationDataAddress()).thenReturn(dataAddress); Assertions.assertTrue(dataSinkFactory.validateRequest(request).failed()); @@ -94,7 +94,7 @@ void createSink_successful() { var sftpDataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - var request = Mockito.mock(DataFlowRequest.class); + var request = Mockito.mock(DataFlowStartMessage.class); Mockito.when(request.getDestinationDataAddress()).thenReturn(sftpDataAddress); try (var staticWrapper = @@ -113,7 +113,7 @@ void createSink_successful() { void createSink_invalidDataAddressType() { var dataSinkFactory = new SftpDataSinkFactory(); var dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); - var request = Mockito.mock(DataFlowRequest.class); + var request = Mockito.mock(DataFlowStartMessage.class); Mockito.when(request.getDestinationDataAddress()).thenReturn(dataAddress); Assertions.assertNull(dataSinkFactory.createSink(request)); diff --git a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceFactoryTest.java b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceFactoryTest.java index a45e18c79..8f32555ad 100644 --- a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceFactoryTest.java +++ b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceFactoryTest.java @@ -22,7 +22,7 @@ import org.apache.sshd.sftp.client.SftpClient; import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; +import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; @@ -47,7 +47,7 @@ void validate_valid() { var sftpDataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - var request = mock(DataFlowRequest.class); + var request = mock(DataFlowStartMessage.class); when(request.getSourceDataAddress()).thenReturn(sftpDataAddress); Assertions.assertTrue(dataSourceFactory.validateRequest(request).succeeded()); @@ -57,7 +57,7 @@ void validate_valid() { void validate_invalidDataAddressType() { var dataSourceFactory = new SftpDataSourceFactory(); var dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); - var request = mock(DataFlowRequest.class); + var request = mock(DataFlowStartMessage.class); when(request.getSourceDataAddress()).thenReturn(dataAddress); Assertions.assertTrue(dataSourceFactory.validateRequest(request).failed()); @@ -76,7 +76,7 @@ void validate_invalidDataAddressParameters() { "userPassword", "password"); var dataAddress = DataAddress.Builder.newInstance().properties(properties).build(); - var request = mock(DataFlowRequest.class); + var request = mock(DataFlowStartMessage.class); when(request.getSourceDataAddress()).thenReturn(dataAddress); Assertions.assertTrue(dataSourceFactory.validateRequest(request).failed()); @@ -93,7 +93,7 @@ void createSink_successful() { var sftpDataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - var request = mock(DataFlowRequest.class); + var request = mock(DataFlowStartMessage.class); when(request.getSourceDataAddress()).thenReturn(sftpDataAddress); try (var staticWrapper = @@ -112,7 +112,7 @@ void createSink_successful() { void createSink_invalidDataAddressType() { var dataSourceFactory = new SftpDataSourceFactory(); var dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); - var request = mock(DataFlowRequest.class); + var request = mock(DataFlowStartMessage.class); when(request.getSourceDataAddress()).thenReturn(dataAddress); Assertions.assertNull(dataSourceFactory.createSource(request)); diff --git a/edc-extensions/transferprocess-sftp-provisioner/build.gradle.kts b/edc-extensions/transferprocess-sftp-provisioner/build.gradle.kts index 67b6aee63..07f96e598 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/build.gradle.kts +++ b/edc-extensions/transferprocess-sftp-provisioner/build.gradle.kts @@ -26,7 +26,7 @@ dependencies { implementation(project(":edc-extensions:transferprocess-sftp-common")) implementation(libs.edc.spi.core) - implementation(libs.edc.policy.engine) + implementation(libs.edc.lib.policyengine) implementation(libs.edc.spi.transfer) testImplementation(libs.edc.junit) diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisioner.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisioner.java index 0aa507416..a0dc9891d 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisioner.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisioner.java @@ -19,11 +19,11 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import org.eclipse.edc.connector.transfer.spi.provision.Provisioner; -import org.eclipse.edc.connector.transfer.spi.types.DeprovisionedResource; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionResponse; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionedResource; -import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; +import org.eclipse.edc.connector.controlplane.transfer.spi.provision.Provisioner; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.DeprovisionedResource; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ProvisionResponse; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ProvisionedResource; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ResourceDefinition; import org.eclipse.edc.policy.engine.spi.PolicyEngine; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.response.ResponseStatus; diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinition.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinition.java index fd13eb2d0..45c71eff3 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinition.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinition.java @@ -19,7 +19,7 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ResourceDefinition; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; public class SftpProviderResourceDefinition extends ResourceDefinition { diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGenerator.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGenerator.java index 8f1748cfe..370b1054e 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGenerator.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGenerator.java @@ -19,9 +19,9 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import org.eclipse.edc.connector.transfer.spi.provision.ProviderResourceDefinitionGenerator; -import org.eclipse.edc.connector.transfer.spi.types.DataRequest; -import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; +import org.eclipse.edc.connector.controlplane.transfer.spi.provision.ProviderResourceDefinitionGenerator; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ResourceDefinition; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.types.domain.DataAddress; import org.eclipse.tractusx.edc.transferprocess.sftp.common.EdcSftpException; @@ -32,7 +32,7 @@ public class SftpProviderResourceDefinitionGenerator implements ProviderResourceDefinitionGenerator { @Override - public @Nullable ResourceDefinition generate(DataRequest dataRequest, DataAddress assetAddress, Policy policy) { + public @Nullable ResourceDefinition generate(TransferProcess transferProcess, DataAddress assetAddress, Policy policy) { try { var sftpDataAddress = SftpDataAddress.fromDataAddress(assetAddress); return new SftpProviderResourceDefinition(NoOpSftpProvisioner.PROVIDER_TYPE, sftpDataAddress); @@ -42,7 +42,7 @@ public class SftpProviderResourceDefinitionGenerator implements ProviderResource } @Override - public boolean canGenerate(DataRequest dataRequest, DataAddress dataAddress, Policy policy) { + public boolean canGenerate(TransferProcess transferProcess, DataAddress dataAddress, Policy policy) { try { SftpDataAddress.fromDataAddress(dataAddress); } catch (EdcSftpException e) { diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionedContentResource.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionedContentResource.java index 61bb9089e..2879252a7 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionedContentResource.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionedContentResource.java @@ -19,7 +19,7 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionedContentResource; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ProvisionedContentResource; import org.eclipse.edc.policy.model.Policy; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerExtension.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerExtension.java index 5b36db1a9..b3d5c0a14 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerExtension.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerExtension.java @@ -19,8 +19,8 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import org.eclipse.edc.connector.transfer.spi.provision.ProviderResourceDefinitionGenerator; -import org.eclipse.edc.connector.transfer.spi.provision.ProvisionManager; +import org.eclipse.edc.connector.controlplane.transfer.spi.provision.ProviderResourceDefinitionGenerator; +import org.eclipse.edc.connector.controlplane.transfer.spi.provision.ProvisionManager; import org.eclipse.edc.policy.engine.spi.PolicyEngine; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provides; diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisionerTest.java b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisionerTest.java index cfbe7a9a3..4fc772118 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisionerTest.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisionerTest.java @@ -20,8 +20,8 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionedContentResource; -import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ProvisionedContentResource; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.ResourceDefinition; import org.eclipse.edc.policy.engine.spi.PolicyEngine; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.result.AbstractResult; diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGeneratorTest.java b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGeneratorTest.java index 7d997e007..1e2de4965 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGeneratorTest.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGeneratorTest.java @@ -21,7 +21,7 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import org.eclipse.edc.connector.transfer.spi.types.DataRequest; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.types.domain.DataAddress; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; @@ -33,7 +33,6 @@ import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; -import static org.eclipse.tractusx.edc.transferprocess.sftp.provisioner.NoOpSftpProvisioner.DATA_ADDRESS_TYPE; import static org.eclipse.tractusx.edc.transferprocess.sftp.provisioner.NoOpSftpProvisioner.PROVIDER_TYPE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -63,15 +62,15 @@ void generate_successful() throws NoSuchAlgorithmException { var port = 22; var path = "path"; - var dataRequest = DataRequest.Builder.newInstance().destinationType(DATA_ADDRESS_TYPE).build(); var sftpUser = SftpUser.Builder.newInstance().name(name).password(password).keyPair(keyPair).build(); var sftpLocation = SftpLocation.Builder.newInstance().host(host).port(port).path(path).build(); final DataAddress dataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); var policy = Policy.Builder.newInstance().build(); + var transferProcess = TransferProcess.Builder.newInstance().build(); var resourceDefinition = - (SftpProviderResourceDefinition) generator.generate(dataRequest, dataAddress, policy); + (SftpProviderResourceDefinition) generator.generate(transferProcess, dataAddress, policy); assertNotNull(resourceDefinition); var sftpDataAddress = resourceDefinition.getSftpDataAddress(); @@ -87,13 +86,13 @@ void generate_successful() throws NoSuchAlgorithmException { @Test void generate_wrongDataAddressType() { - var dataRequest = - DataRequest.Builder.newInstance().destinationType(DATA_ADDRESS_TYPE).build(); var dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); var policy = Policy.Builder.newInstance().build(); + var transferProcess = TransferProcess.Builder.newInstance().build(); + var resourceDefinition = - (SftpProviderResourceDefinition) generator.generate(dataRequest, dataAddress, policy); + (SftpProviderResourceDefinition) generator.generate(transferProcess, dataAddress, policy); assertNull(resourceDefinition); } diff --git a/edc-tests/deployment/src/main/resources/helm/tractusx-connector-azure-vault-test.yaml b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-azure-vault-test.yaml index 98ac76659..0b9e0e5e6 100644 --- a/edc-tests/deployment/src/main/resources/helm/tractusx-connector-azure-vault-test.yaml +++ b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-azure-vault-test.yaml @@ -25,6 +25,17 @@ fullnameOverride: tx-prod ################################ participant: id: "test-participant" +iatp: + # Decentralized IDentifier + id: "did:web:changeme" + sts: + dim: + url: "https://somewhere.dim.org" + oauth: + token_url: "https://changeme.org" + client: + id: "test-client-id" + secret_alias: "test-alias" controlplane: service: type: NodePort @@ -35,13 +46,12 @@ controlplane: pullPolicy: Never tag: "latest" repository: "edc-controlplane-postgresql-azure-vault" - ssi: - miw: - url: "http://localhost:8080" - authorityId: "authorityId" securityContext: # avoids some errors in the log: cannot write temp files of large multipart requests when R/O readOnlyRootFilesystem: false + bdrs: + server: + url: "https://bdrs.test.org" dataplane: endpoints: proxy: @@ -57,6 +67,11 @@ dataplane: endpointOverride: http://minio:9000 secretAccessKey: qwerty123 accessKeyId: qwerty123 + token: + signer: + privatekey_alias: "key-1" + verifier: + publickey_alias: "key-1" postgresql: jdbcUrl: jdbc:postgresql://{{ .Release.Name }}-postgresql:5432/edc auth: diff --git a/edc-tests/deployment/src/main/resources/helm/tractusx-connector-memory-test.yaml b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-memory-test.yaml index 354c667ae..9032eab3a 100644 --- a/edc-tests/deployment/src/main/resources/helm/tractusx-connector-memory-test.yaml +++ b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-memory-test.yaml @@ -23,16 +23,23 @@ fullnameOverride: tx-inmem participant: id: "test-participant" +iatp: + # Decentralized IDentifier + id: "did:web:changeme" + sts: + dim: + url: "https://somewhere.dim.org" + oauth: + token_url: "https://changeme.org" + client: + id: "test-client-id" + secret_alias: "test-alias" runtime: service: type: NodePort endpoints: management: authKey: password - ssi: - miw: - url: "http://localhost:8080" - authorityId: "authorityId" image: pullPolicy: Never tag: "latest" @@ -40,11 +47,16 @@ runtime: securityContext: # avoids some errors in the log: cannot write temp files of large multipart requests when R/O readOnlyRootFilesystem: false + bdrs: + server: + url: "https://bdrs.test.org" + token: + signer: + privatekey_alias: "key-1" + verifier: + publickey_alias: "key-1" vault: secretNames: - transferProxyTokenEncryptionAesKey: aes-keys - # this must be set through CLI args: --set vault.secrets=$YOUR_VAULT_SECRETS where YOUR_VAULT_SECRETS should - # be a string in the format "key1:secret1;key2:secret2;..." secrets: backendService: httpProxyTokenReceiverUrl: "http://backend:8080" diff --git a/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml index 20ec65cbc..dc6c63bf8 100644 --- a/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml +++ b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml @@ -24,7 +24,27 @@ fullnameOverride: tx-prod ################################ participant: id: "test-participant" +iatp: + # Decentralized IDentifier + id: "did:web:changeme" + sts: + dim: + url: "https://somewhere.dim.org" + oauth: + token_url: "https://changeme.org" + client: + id: "test-client-id" + secret_alias: "test-alias" controlplane: + # the ssi object is still needed for the upgradeability test + # todo: remove this after 0.7.0 is released + ssi: + miw: + url: "http://localhost:8080" + authorityId: "authorityId" + oauth: + client: + secretAlias: "client-secret" service: type: NodePort endpoints: @@ -37,13 +57,9 @@ controlplane: securityContext: # avoids some errors in the log: cannot write temp files of large multipart requests when R/O readOnlyRootFilesystem: false - ssi: - miw: - url: "http://localhost:8080" - authorityId: "authorityId" - oauth: - client: - secretAlias: "client-secret" + bdrs: + server: + url: "https://bdrs.test.org" dataplane: endpoints: proxy: @@ -59,6 +75,11 @@ dataplane: endpointOverride: http://minio:9000 secretAccessKey: qwerty123 accessKeyId: qwerty123 + token: + signer: + privatekey_alias: "key-1" + verifier: + publickey_alias: "key-1" postgresql: jdbcUrl: jdbc:postgresql://{{ .Release.Name }}-postgresql:5432/edc auth: @@ -73,6 +94,21 @@ vault: # this must be set through CLI args: --set vault.secrets=$YOUR_VAULT_SECRETS where YOUR_VAULT_SECRETS should # be a string in the format "key1:secret1;key2:secret2;..." secrets: + + # the post-start object is still needed for the upgradeability test + # todo: remove this after 0.7.0 is released + server: + postStart: + - sh + - -c + - |- + { + sleep 5 + + /bin/vault kv put secret/client-secret content=kEmH7QRPWhKfy8f+x0pFMw== + + /bin/vault kv put secret/aes-keys content=YWVzX2VuY2tleV90ZXN0Cg== + } backendService: httpProxyTokenReceiverUrl: "http://backend:8080" tests: diff --git a/edc-tests/deployment/src/main/resources/prepare-test.sh b/edc-tests/deployment/src/main/resources/prepare-test.sh deleted file mode 100755 index ba3848a6b..000000000 --- a/edc-tests/deployment/src/main/resources/prepare-test.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# -# 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 -# -# Contributors: -# Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation -# -# - -set -euo pipefail - -if [ "$#" -lt 1 ]; then - echo "usage prepare-test.sh PATH_TO_YAML" - echo "" - echo "Please provide the path to the YAML file, which contains the config for the test infrastructure! In most cases - this will be edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml" - exit 42 -fi - -VALUES_FILE=$1 - -CLIENT_SECRET=$(openssl rand -base64 16) -AES_KEY=$(echo aes_enckey_test | base64) -echo "$AES_KEY" > aes.key -echo "$CLIENT_SECRET" > client.secret - -# add a "postStart" command to the vault config, that creates a oauth client secret and an aes-keys secret -yq -i ".vault.server.postStart |= [\"sh\",\"-c\",\"{\nsleep 5\n -/bin/vault kv put secret/client-secret content=$CLIENT_SECRET\n -/bin/vault kv put secret/aes-keys content=$AES_KEY\n}\"]" "$VALUES_FILE" \ No newline at end of file diff --git a/edc-tests/e2e-tests/build.gradle.kts b/edc-tests/e2e-tests/build.gradle.kts index 378e8499d..5211310e9 100644 --- a/edc-tests/e2e-tests/build.gradle.kts +++ b/edc-tests/e2e-tests/build.gradle.kts @@ -23,53 +23,19 @@ plugins { } dependencies { - testImplementation(project(":spi:edr-spi")) - testImplementation(project(":edc-extensions:edr:edr-api")) testImplementation(libs.okhttp.mockwebserver) testImplementation(libs.restAssured) testImplementation(libs.nimbus.jwt) - testImplementation(libs.postgres) testImplementation(libs.awaitility) - testImplementation(libs.aws.s3) - testImplementation(libs.edc.spi.core) - testImplementation(libs.edc.junit) - testImplementation(libs.edc.spi.policy) - testImplementation(libs.edc.spi.contract) - testImplementation(libs.edc.core.api) - testImplementation(libs.edc.spi.catalog) - testImplementation(libs.edc.api.catalog) - testImplementation(libs.edc.api.contractnegotiation) - testImplementation(libs.edc.api.transferprocess) - testImplementation(libs.edc.spi.dataplane.selector) - testImplementation(libs.edc.ext.jsonld) - testImplementation(libs.edc.dsp) - testImplementation(libs.edc.identity.jws2020) - testImplementation(libs.edc.identity.vc.ldp) - testImplementation(libs.edc.ih.spi.store) + testImplementation(libs.edc.lib.boot) testImplementation(testFixtures(libs.edc.sql.core)) testImplementation(testFixtures(libs.edc.api.management.test.fixtures)) - testImplementation(libs.awaitility) testImplementation(project(":edc-extensions:bpn-validation:bpn-validation-spi")) - testImplementation(libs.edc.auth.oauth2.client) - testImplementation(libs.testcontainers.junit) - testImplementation(libs.testcontainers.postgres) - testImplementation(libs.testcontainers.vault) - testImplementation(libs.bouncyCastle.bcpkixJdk18on) + testImplementation(project(":spi:core-spi")) + testImplementation(testFixtures(project(":edc-tests:edc-controlplane:fixtures"))) testCompileOnly(project(":edc-tests:runtime:extensions")) - testCompileOnly(project(":edc-tests:runtime:runtime-memory")) - testCompileOnly(project(":edc-tests:runtime:iatp:runtime-memory-sts")) - testCompileOnly(project(":edc-tests:runtime:iatp:runtime-memory-iatp-ih")) - testCompileOnly(project(":edc-tests:runtime:runtime-memory-ssi")) - testCompileOnly(project(":edc-tests:runtime:runtime-postgresql")) - - testFixturesImplementation(libs.junit.jupiter.api) - testFixturesImplementation(libs.edc.spi.core) - testFixturesImplementation(libs.edc.junit) - testFixturesImplementation(libs.edc.spi.policy) - testFixturesImplementation(libs.edc.spi.contract) - testFixturesImplementation(project(":spi:edr-spi")) - testFixturesImplementation(project(":edc-extensions:bpn-validation:bpn-validation-spi")) + testCompileOnly(project(":edc-tests:runtime:iatp:runtime-memory-iatp-dim")) } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/AssetHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/AssetHelperFunctions.java deleted file mode 100644 index e9b1224fb..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/AssetHelperFunctions.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.helpers; - -import jakarta.json.Json; -import jakarta.json.JsonObject; -import jakarta.json.JsonObjectBuilder; - -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -import static org.eclipse.edc.spi.CoreConstants.EDC_PREFIX; - -public class AssetHelperFunctions { - - /** - * Creates an asset with the given ID and props using the participant's Data Management API - */ - public static JsonObject createAsset(String id, JsonObject assetProperties, JsonObject dataAddress) { - return Json.createObjectBuilder() - .add(CONTEXT, createContextBuilder()) - .add(TYPE, "Asset") - .add(ID, id) - .add(EDC_NAMESPACE + "properties", assetProperties) - .add(EDC_NAMESPACE + "dataAddress", dataAddress) - .build(); - - - } - - public static JsonObjectBuilder createDataAddressBuilder(String type) { - return Json.createObjectBuilder() - .add(TYPE, EDC_NAMESPACE + "DataAddress") - .add(EDC_NAMESPACE + "type", type); - } - - public static JsonObjectBuilder createContextBuilder() { - return Json.createObjectBuilder() - .add(EDC_PREFIX, EDC_NAMESPACE); - } - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractDefinitionHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractDefinitionHelperFunctions.java deleted file mode 100644 index a286091c0..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractDefinitionHelperFunctions.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.helpers; - -import jakarta.json.Json; -import jakarta.json.JsonObject; - -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; - -public class ContractDefinitionHelperFunctions { - - public static JsonObject createContractDefinition(String assetId, String definitionId, String accessPolicyId, String contractPolicyId) { - return Json.createObjectBuilder() - .add(ID, definitionId) - .add(TYPE, EDC_NAMESPACE + "ContractDefinition") - .add(EDC_NAMESPACE + "accessPolicyId", accessPolicyId) - .add(EDC_NAMESPACE + "contractPolicyId", contractPolicyId) - .add(EDC_NAMESPACE + "assetsSelector", Json.createArrayBuilder() - .add(Json.createObjectBuilder() - .add(TYPE, "CriterionDto") - .add(EDC_NAMESPACE + "operandLeft", EDC_NAMESPACE + "id") - .add(EDC_NAMESPACE + "operator", "=") - .add(EDC_NAMESPACE + "operandRight", assetId) - .build()) - .build()) - .build(); - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractNegotiationHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractNegotiationHelperFunctions.java deleted file mode 100644 index da4b033b6..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractNegotiationHelperFunctions.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.helpers; - -import jakarta.json.Json; -import jakarta.json.JsonObject; -import org.eclipse.edc.jsonld.TitaniumJsonLd; -import org.eclipse.edc.jsonld.spi.JsonLd; -import org.eclipse.edc.spi.monitor.Monitor; - -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -import static org.mockito.Mockito.mock; - -public class ContractNegotiationHelperFunctions { - - private static final JsonLd JSON_LD = new TitaniumJsonLd(mock(Monitor.class)); - - public static JsonObject createNegotiationRequest(String counterPartyAddress, String providerId, JsonObject policy) { - return Json.createObjectBuilder() - .add(TYPE, EDC_NAMESPACE + "NegotiationInitiateRequestDto") - .add(EDC_NAMESPACE + "providerId", providerId) - .add(EDC_NAMESPACE + "counterPartyAddress", counterPartyAddress) - .add(EDC_NAMESPACE + "protocol", DATASPACE_PROTOCOL_HTTP) - .add(EDC_NAMESPACE + "policy", JSON_LD.compact(policy).getContent()) - .build(); - } - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/DimHelper.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/DimHelper.java new file mode 100644 index 000000000..08c0d1210 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/DimHelper.java @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.helpers; + +import org.eclipse.tractusx.edc.lifecycle.DimParticipant; + +import java.net.URI; +import java.util.Objects; + +import static java.lang.String.format; +import static java.lang.System.getenv; + +public interface DimHelper { + + /** + * Configure a {@link DimParticipant} from env variables + * + * @param name The participant name + * @return The composed {@link DimParticipant} + */ + static DimParticipant configureParticipant(String name, String bdrsUrl) { + var bpn = getEnv(format("DIM_%s_BPN", name)); + var dimUrl = getEnv(format("DIM_%s_DIM_URL", name)); + var stsUrl = getEnv(format("DIM_%s_STS_URL", name)); + var stsClientId = getEnv(format("DIM_%s_STS_CLIENT_ID", name)); + var stsClientSecret = getEnv(format("DIM_%s_STS_CLIENT_SECRET", name)); + var did = getEnv(format("DIM_%s_DID", name)); + var trustedIssuer = getEnv("DIM_TRUSTED_ISSUER"); + return DimParticipant.Builder.newInstance().id(bpn) + .name(name) + .stsUri(URI.create(stsUrl)) + .stsClientId(stsClientId) + .stsClientSecret(stsClientSecret) + .dimUri(URI.create(dimUrl)) + .trustedIssuer(trustedIssuer) + .did(did) + .bdrsUri(URI.create(bdrsUrl)) + .build(); + } + + private static String getEnv(String env) { + return Objects.requireNonNull(getenv(env), "%s env variable not present".formatted(env)); + } + +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/IatpHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/IatpHelperFunctions.java deleted file mode 100644 index 5ea0a7646..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/IatpHelperFunctions.java +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.helpers; - -import jakarta.json.Json; -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import org.bouncycastle.openssl.jcajce.JcaPEMWriter; -import org.eclipse.edc.spi.EdcException; -import org.testcontainers.shaded.org.bouncycastle.jce.provider.BouncyCastleProvider; - -import java.io.IOException; -import java.io.StringWriter; -import java.security.InvalidAlgorithmParameterException; -import java.security.Key; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.spec.ECGenParameterSpec; -import java.time.Instant; -import java.util.function.Supplier; - -public class IatpHelperFunctions { - - /** - * Returns the Pem representation of a {@link Key} - * - * @param key The input key - * @return The pem encoded key - */ - public static String toPemEncoded(Key key) { - var writer = new StringWriter(); - try (var jcaPEMWriter = new JcaPEMWriter(writer)) { - jcaPEMWriter.writeObject(key); - } catch (IOException e) { - throw new EdcException("Unable to convert private in PEM format ", e); - } - return writer.toString(); - } - - - public static KeyPair generateKeyPair() { - try { - KeyPairGenerator gen = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider()); - gen.initialize(new ECGenParameterSpec("secp256r1")); - return gen.generateKeyPair(); - } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { - throw new RuntimeException(e); - } - } - - public static JsonObject createVc(String issuer, String type, Supplier subjectSupplier) { - return Json.createObjectBuilder() - .add("@context", context()) - .add("type", types(type)) - .add("credentialSubject", subjectSupplier.get()) - .add("issuer", issuer) - .add("issuanceDate", Instant.now().toString()) - .build(); - } - - public static JsonObject membershipSubject(String did, String id) { - return Json.createObjectBuilder() - .add("type", "MembershipCredential") - .add("holderIdentifier", id) - .add("status", "Active") - .add("memberOf", "Catena-X") - .add("startTime", Instant.now().toString()) - .add("id", did) - .build(); - - } - - public static JsonObject frameworkAgreementSubject(String did, String id, String type) { - return Json.createObjectBuilder() - .add("type", type) - .add("holderIdentifier", id) - .add("useCaseType", type) - .add("contractVersion", "1.0.0") - .add("contractTemplate", "https://public.catena-x.org/contracts/traceabilty.v1.pdf") - .add("id", did) - .build(); - - } - - private static JsonArray types(String type) { - return Json.createArrayBuilder() - .add("VerifiableCredential") - .add(type) - .build(); - } - - private static JsonArray context() { - return Json.createArrayBuilder() - .add("https://www.w3.org/2018/credentials/v1") - .add("https://w3id.org/security/suites/jws-2020/v1") - .add("https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json") - .build(); - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/DataWiper.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/DataWiper.java index 2bb1b12e7..904b97626 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/DataWiper.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/DataWiper.java @@ -19,16 +19,16 @@ package org.eclipse.tractusx.edc.lifecycle; -import org.eclipse.edc.connector.contract.spi.offer.store.ContractDefinitionStore; -import org.eclipse.edc.connector.policy.spi.store.PolicyDefinitionStore; -import org.eclipse.edc.spi.asset.AssetIndex; +import org.eclipse.edc.connector.controlplane.asset.spi.index.AssetIndex; +import org.eclipse.edc.connector.controlplane.contract.spi.offer.store.ContractDefinitionStore; +import org.eclipse.edc.connector.controlplane.policy.spi.store.PolicyDefinitionStore; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_BPN; /** * Helper class to delete all objects from a runtime's data stores. @@ -72,10 +72,10 @@ public void clearAssetIndex() { } public void clearEdrCache() { - var edrCache = context.getService(EndpointDataReferenceCache.class); - edrCache.queryForEntries(QuerySpec.max()).forEach(entry -> { + var edrCache = context.getService(EndpointDataReferenceStore.class); + edrCache.query(QuerySpec.max()).getContent().forEach(entry -> { try { - edrCache.deleteByTransferProcessId(entry.getTransferProcessId()); + edrCache.delete(entry.getTransferProcessId()); } catch (Exception e) { context.getMonitor().warning("Failed to clean up the cache", e); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/DimParticipant.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/DimParticipant.java new file mode 100644 index 000000000..e521753a6 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/DimParticipant.java @@ -0,0 +1,75 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.lifecycle; + +import org.eclipse.tractusx.edc.tests.participant.TractusxIatpParticipantBase; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Extension of {@link TractusxIatpParticipantBase} with DIM specific configuration + */ +public class DimParticipant extends TractusxIatpParticipantBase { + + protected URI dimUri; + + protected URI bdrsUri; + + @Override + public Map iatpConfiguration(TractusxIatpParticipantBase... others) { + var config = new HashMap<>(super.iatpConfiguration(others)); + config.put("edc.iam.sts.dim.url", dimUri.toString()); + config.put("tx.iam.iatp.bdrs.server.url", bdrsUri.toString()); + config.put("edc.transfer.proxy.token.verifier.publickey.alias", getKeyId()); + return config; + } + + public static class Builder extends TractusxIatpParticipantBase.Builder { + + protected Builder() { + super(new DimParticipant()); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder dimUri(URI dimUri) { + participant.dimUri = dimUri; + return self(); + } + + public Builder bdrsUri(URI bdrsUri) { + participant.bdrsUri = bdrsUri; + return self(); + } + + @Override + public DimParticipant build() { + super.build(); + Objects.requireNonNull(participant.dimUri, "DIM URI should not be null"); + Objects.requireNonNull(participant.bdrsUri, "BDRS URI should not be null"); + return participant; + } + } +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/ParticipantRuntime.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/ParticipantRuntime.java index 3a644a074..9ba359c1e 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/ParticipantRuntime.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/ParticipantRuntime.java @@ -19,15 +19,14 @@ package org.eclipse.tractusx.edc.lifecycle; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import org.eclipse.edc.boot.system.injection.InjectionContainer; import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; -import org.eclipse.edc.spi.iam.AudienceResolver; -import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.security.Vault; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.injection.InjectionContainer; -import org.eclipse.edc.spi.types.domain.message.RemoteMessage; -import org.eclipse.tractusx.edc.token.MockBpnIdentityService; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; @@ -35,24 +34,23 @@ import java.util.List; import java.util.Map; -import static org.eclipse.tractusx.edc.helpers.IatpHelperFunctions.generateKeyPair; -import static org.eclipse.tractusx.edc.helpers.IatpHelperFunctions.toPemEncoded; - public class ParticipantRuntime extends EdcRuntimeExtension implements BeforeAllCallback, AfterAllCallback { - private final Map properties; private DataWiper wiper; - public ParticipantRuntime(String moduleName, String runtimeName, String bpn, Map properties) { + + public ParticipantRuntime(String moduleName, String runtimeName, Map properties) { super(moduleName, runtimeName, properties); this.properties = properties; - if (!properties.containsKey("tx.ssi.miw.url") && !properties.containsKey("edc.iam.issuer.id")) { - this.registerServiceMock(IdentityService.class, new MockBpnIdentityService(bpn)); - this.registerServiceMock(AudienceResolver.class, RemoteMessage::getCounterPartyAddress); - } } + public ParticipantRuntime(String runtimeName, Map properties, String... modules) { + super(runtimeName, properties, modules); + this.properties = properties; + } + + @Override public void beforeTestExecution(ExtensionContext extensionContext) { //do nothing - we only want to start the runtime once @@ -84,12 +82,16 @@ protected void bootExtensions(ServiceExtensionContext context, List properties) { var privateAlias = properties.get("edc.transfer.proxy.token.signer.privatekey.alias"); var publicAlias = properties.get("edc.transfer.proxy.token.verifier.publickey.alias"); - if (privateAlias != null && publicAlias != null) { - var keyPair = generateKeyPair(); - var vault = getContext().getService(Vault.class); - vault.storeSecret(privateAlias, toPemEncoded(keyPair.getPrivate())); - vault.storeSecret(publicAlias, toPemEncoded(keyPair.getPublic())); + try { + var ecKey = new ECKeyGenerator(Curve.P_256).keyID(publicAlias).generate(); + var vault = getContext().getService(Vault.class); + vault.storeSecret(privateAlias, ecKey.toJSONString()); + vault.storeSecret(publicAlias, ecKey.toPublicJWK().toJSONString()); + } catch (JOSEException e) { + throw new RuntimeException(e); + } + } } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/PgHashicorpParticipantRuntime.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/PgHashicorpParticipantRuntime.java deleted file mode 100644 index 942ba81a2..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/PgHashicorpParticipantRuntime.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.lifecycle; - -import org.junit.jupiter.api.extension.ExtensionContext; -import org.testcontainers.vault.VaultContainer; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import static java.lang.String.format; - -public class PgHashicorpParticipantRuntime extends PgParticipantRuntime { - - static final String DOCKER_IMAGE_NAME = "vault:1.9.6"; - static final String TOKEN = UUID.randomUUID().toString(); - - - public final VaultContainer vaultContainer = new VaultContainer<>(DOCKER_IMAGE_NAME) - .withVaultToken(TOKEN); - private final String vaultDirectory; - - public PgHashicorpParticipantRuntime(String moduleName, String runtimeName, String bpn, String vaultDirectory, Map properties) { - super(moduleName, runtimeName, bpn, properties); - this.vaultDirectory = vaultDirectory; - } - - @Override - public void beforeAll(ExtensionContext context) throws Exception { - vaultContainer.start(); - config().forEach(System::setProperty); - super.beforeAll(context); - } - - @Override - public void afterAll(ExtensionContext context) throws Exception { - super.afterAll(context); - vaultContainer.stop(); - vaultContainer.close(); - } - - @Override - protected void mockVault() { - - } - - private Map config() { - return new HashMap<>() { - { - put("edc.vault.hashicorp.url", format("http://%s:%s", vaultContainer.getHost(), vaultContainer.getFirstMappedPort())); - put("edc.vault.hashicorp.token", TOKEN); - put("edc.edr.vault.path", vaultDirectory); - } - }; - } -} diff --git a/core/edr-cache-core/build.gradle.kts b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Runtimes.java similarity index 73% rename from core/edr-cache-core/build.gradle.kts rename to edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Runtimes.java index 9312ef4b3..6b265ddb4 100644 --- a/core/edr-cache-core/build.gradle.kts +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Runtimes.java @@ -17,19 +17,15 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -plugins { - `java-library` -} - -dependencies { - implementation(libs.edc.spi.core) - implementation(libs.edc.config.filesystem) - implementation(libs.edc.util) +package org.eclipse.tractusx.edc.lifecycle; - implementation(project(":spi:edr-spi")) +import java.util.Map; - testImplementation(testFixtures(project(":spi:edr-spi"))) - testImplementation(libs.edc.core.connector) +public interface Runtimes { + static ParticipantRuntime dimRuntime(String name, Map configuration) { + return new ParticipantRuntime(name, configuration, + ":edc-tests:runtime:iatp:runtime-memory-iatp-dim" + ); + } } - diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/ParticipantDataApi.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/ParticipantDataApi.java deleted file mode 100644 index a9ca1cb7f..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/ParticipantDataApi.java +++ /dev/null @@ -1,143 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.lifecycle.tx; - -import io.restassured.response.Response; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; - -import java.util.Map; - -import static io.restassured.RestAssured.given; -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant.API_KEY; -import static org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant.PROXY_SUBPATH; - - -/** - * E2E test helper for fetching the data - */ -public class ParticipantDataApi { - - private final TxParticipant participant; - - public ParticipantDataApi(TxParticipant participant) { - this.participant = participant; - } - - /** - * Pull the data using the Consumer DataPlane and the provider's gateway with an asset id - * - * @return the data - */ - public String pullProxyDataByAssetId(TxParticipant provider, String assetId) { - var body = Map.of("assetId", assetId, "endpointUrl", format("%s/gateway/aas/test", provider.gatewayEndpoint())); - return getProxyData(body); - } - - /** - * Pull the data using the Consumer DataPlane and the provider's gateway with an transfer process id - * - * @return the data - */ - public String pullProxyDataByTransferProcessId(TxParticipant provider, String transferProcessId) { - var body = Map.of("transferProcessId", transferProcessId, - "endpointUrl", format("%s/gateway/aas/test", provider.gatewayEndpoint())); - return getProxyData(body); - - } - - /** - * Pull the data using the Consumer DataPlane and the provider's gateway with an asset id - * - * @return the data - */ - public Response pullProxyDataResponseByAssetId(TxParticipant provider, String assetId) { - var body = Map.of("assetId", assetId, - "endpointUrl", format("%s/gateway/aas/test", provider.gatewayEndpoint()), - "providerId", provider.getBpn()); - return proxyRequest(body); - } - - /** - * Pull the data using the Consumer DataPlane with an asset id - * - * @return the data - */ - public String pullProviderDataPlaneDataByAssetId(TxParticipant provider, String assetId) { - var body = Map.of("assetId", assetId); - return getProxyData(body); - } - - /** - * Pull the data using the Consumer DataPlane with an asset id and additional parameters - * - * @return the data - */ - public String pullProviderDataPlaneDataByAssetIdAndCustomProperties(TxParticipant provider, String assetId, String path, String params) { - var body = Map.of("assetId", assetId, "pathSegments", path, "queryParams", params); - return getProxyData(body); - } - - /** - * Pull the data using the Consumer DataPlane - * - * @return the data - */ - public String pullProviderDataPlaneDataByTransferProcessId(TxParticipant provider, String transferProcessId) { - var body = Map.of("transferProcessId", transferProcessId); - return getProxyData(body); - - } - - /** - * Pull the data with an {@link EndpointDataReference} - * - * @param edr The edr - * @param queryParams additional params - * @return the data - */ - public String pullData(EndpointDataReference edr, Map queryParams) { - var response = given() - .baseUri(edr.getEndpoint()) - .header(edr.getAuthKey(), edr.getAuthCode()) - .queryParams(queryParams) - .when() - .get(); - assertThat(response.statusCode()).isBetween(200, 300); - return response.body().asString(); - } - - private String getProxyData(Map body) { - return proxyRequest(body) - .then() - .assertThat().statusCode(200) - .extract().body().asString(); - } - - private Response proxyRequest(Map body) { - return given() - .baseUri(participant.dataPlaneProxy().toString()) - .header("x-api-key", API_KEY) - .contentType("application/json") - .body(body) - .post(PROXY_SUBPATH); - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/iatp/IatpParticipant.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/iatp/IatpParticipant.java deleted file mode 100644 index f5ed7d1a3..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/iatp/IatpParticipant.java +++ /dev/null @@ -1,152 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.lifecycle.tx.iatp; - -import com.nimbusds.jose.jwk.Curve; -import com.nimbusds.jose.jwk.ECKey; -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.iam.did.spi.document.Service; -import org.eclipse.edc.iam.did.spi.document.VerificationMethod; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; - -import java.net.URI; -import java.security.KeyPair; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; - -import static java.lang.String.format; -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; -import static org.eclipse.tractusx.edc.helpers.IatpHelperFunctions.generateKeyPair; -import static org.eclipse.tractusx.edc.helpers.IatpHelperFunctions.toPemEncoded; -import static org.eclipse.tractusx.edc.lifecycle.tx.iatp.DataspaceIssuer.DATASPACE_ISSUER; - -/** - * Wrapper of {@link TxParticipant} with IATP specific configurations - */ -public class IatpParticipant { - - public static final String KEY_ID = "#key1"; - public static final String DID_EXAMPLE = "did:example:"; - protected final URI csService = URI.create("http://localhost:" + getFreePort() + "/api/resolution"); - private final TxParticipant participant; - private final URI stsUri; - private final KeyPair keyPair; - private final DidDocument didDocument; - private final String privateKey; - private final String publicKey; - - public IatpParticipant(TxParticipant participant, URI stsUri) { - this.participant = participant; - this.stsUri = stsUri; - this.keyPair = generateKeyPair(); - this.didDocument = generateDidDocument(); - this.privateKey = toPemEncoded(keyPair.getPrivate()); - this.publicKey = toPemEncoded(keyPair.getPublic()); - } - - public String getBpn() { - return participant.getBpn(); - } - - public String getName() { - return participant.getName(); - } - - public Map iatpConfiguration(TxParticipant... others) { - var did = DID_EXAMPLE + participant.getName().toLowerCase(); - var iatpConfiguration = new HashMap<>(participant.getConfiguration()) { - { - - put("edc.iam.sts.oauth.token.url", stsUri + "/token"); - put("edc.iam.sts.oauth.client.id", getBpn()); - put("edc.iam.sts.oauth.client.secret.alias", "client_secret_alias"); - put("edc.iam.issuer.id", did); - put("edc.ih.iam.id", participant.getBpn()); - put("tx.vault.seed.secrets", "client_secret_alias:client_secret"); - put("edc.ih.iam.publickey.pem", publicKey()); - put("web.http.resolution.port", String.valueOf(csService.getPort())); - put("web.http.resolution.path", csService.getPath()); - put("edc.agent.identity.key", "client_id"); - put("edc.iam.trusted-issuer.issuer.id", DATASPACE_ISSUER); - } - }; - - Stream.concat(Stream.of(participant), Arrays.stream(others)).forEach(p -> { - var prefix = format("tx.iam.iatp.audiences.%s", p.getName().toLowerCase()); - var participantDid = DID_EXAMPLE + p.getName().toLowerCase(); - iatpConfiguration.put(prefix + "_endpoint" + ".from", p.getProtocolEndpoint().getUrl().toString()); - iatpConfiguration.put(prefix + "_endpoint" + ".to", participantDid); - iatpConfiguration.put(prefix + "_id" + ".from", p.getBpn()); - iatpConfiguration.put(prefix + "_id" + ".to", participantDid); - }); - return iatpConfiguration; - } - - public String didUrl() { - return DID_EXAMPLE + participant.getName().toLowerCase(); - } - - public String verificationId() { - return didUrl() + KEY_ID; - } - - public String privateKey() { - return privateKey; - } - - public String publicKey() { - return publicKey; - } - - public DidDocument didDocument() { - return didDocument; - } - - private DidDocument generateDidDocument() { - var service = new Service(); - service.setId("#credential-service"); - service.setType("CredentialService"); - service.setServiceEndpoint(csService + "/participants/" + getBpn()); - - var ecKey = new ECKey.Builder(Curve.P_256, (ECPublicKey) keyPair.getPublic()) - .privateKey((ECPrivateKey) keyPair.getPrivate()) - .build(); - - var verificationMethod = VerificationMethod.Builder.newInstance() - .id(verificationId()) - .controller(didUrl()) - .type("JsonWebKey2020") - .publicKeyJwk(ecKey.toPublicJWK().toJSONObject()) - .build(); - - return DidDocument.Builder.newInstance() - .id(didUrl()) - .service(List.of(service)) - .authentication(List.of("#key1")) - .verificationMethod(List.of(verificationMethod)) - .build(); - } - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tag/MiwIntegrationTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tag/DimIntegrationTest.java similarity index 95% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tag/MiwIntegrationTest.java rename to edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tag/DimIntegrationTest.java index c3db731a4..267814b79 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tag/MiwIntegrationTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tag/DimIntegrationTest.java @@ -30,7 +30,7 @@ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @IntegrationTest -@Tag("MiwIntegrationTest") -public @interface MiwIntegrationTest { +@Tag("DimIntegrationTest") +public @interface DimIntegrationTest { } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/AbstractCatalogTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/AbstractCatalogTest.java deleted file mode 100644 index 497d021ee..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/AbstractCatalogTest.java +++ /dev/null @@ -1,205 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.catalog; - - -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static java.util.stream.IntStream.range; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.test.system.utils.PolicyFixtures.noConstraintPolicy; -import static org.eclipse.tractusx.edc.helpers.CatalogHelperFunctions.getDatasetAssetId; -import static org.eclipse.tractusx.edc.helpers.CatalogHelperFunctions.getDatasetPolicies; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.BUSINESS_PARTNER_LEGACY_EVALUATION_KEY; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.bnpPolicy; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.bpnGroupPolicy; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.frameworkPolicy; -import static org.eclipse.tractusx.edc.helpers.QueryHelperFunctions.createQuery; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_NAME; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_NAME; - -public abstract class AbstractCatalogTest { - - - protected static final TxParticipant SOKRATES = TxParticipant.Builder.newInstance() - .name(SOKRATES_NAME) - .id(SOKRATES_BPN) - .build(); - - - protected static final TxParticipant PLATO = TxParticipant.Builder.newInstance() - .name(PLATO_NAME) - .id(PLATO_BPN) - .build(); - - - @Test - @DisplayName("Plato gets catalog from Sokrates. No constraints.") - void requestCatalog_fulfillsPolicy_shouldReturnOffer() { - // arrange - SOKRATES.createAsset("test-asset"); - var ap = SOKRATES.createPolicyDefinition(noConstraintPolicy()); - var cp = SOKRATES.createPolicyDefinition(noConstraintPolicy()); - SOKRATES.createContractDefinition("test-asset", "test-def", ap, cp); - - // act - var catalog = PLATO.getCatalogDatasets(SOKRATES); - - // assert - assertThat(catalog).isNotEmpty() - .hasSize(1) - .allSatisfy(co -> { - assertThat(getDatasetAssetId(co)).isEqualTo("test-asset"); - }); - - } - - @Test - @DisplayName("Verify that Plato receives only the offers he is permitted to (using the legacy BPN validation)") - void requestCatalog_filteredByBpnLegacy_shouldReject() { - var onlyPlatoPolicy = bnpPolicy("BPN1", "BPN2", PLATO.getBpn()); - var onlyDiogenesPolicy = bnpPolicy("ARISTOTELES-BPN"); - - var onlyPlatoId = SOKRATES.createPolicyDefinition(onlyPlatoPolicy); - var onlyDiogenesId = SOKRATES.createPolicyDefinition(onlyDiogenesPolicy); - var noConstraintPolicyId = SOKRATES.createPolicyDefinition(noConstraintPolicy()); - - SOKRATES.createAsset("test-asset1"); - SOKRATES.createAsset("test-asset2"); - SOKRATES.createAsset("test-asset3"); - - SOKRATES.createContractDefinition("test-asset1", "def1", noConstraintPolicyId, noConstraintPolicyId); - SOKRATES.createContractDefinition("test-asset2", "def2", onlyPlatoId, noConstraintPolicyId); - SOKRATES.createContractDefinition("test-asset3", "def3", onlyDiogenesId, noConstraintPolicyId); - - - // act - var catalog = PLATO.getCatalogDatasets(SOKRATES); - assertThat(catalog).hasSize(2); - } - - - @Test - @DisplayName("Verify that Plato receives only the offers he is permitted to (using the legacy BPN validation)") - void requestCatalog_filteredByBpnLegacy_WithNamespace_shouldReject() { - - var onlyPlatoPolicy = bnpPolicy("BPN1", "BPN2", PLATO.getBpn()); - var onlyDiogenesPolicy = frameworkPolicy(Map.of(BUSINESS_PARTNER_LEGACY_EVALUATION_KEY, "ARISTOTELES-BPN")); - - var onlyPlatoId = SOKRATES.createPolicyDefinition(onlyPlatoPolicy); - var onlyDiogenesId = SOKRATES.createPolicyDefinition(onlyDiogenesPolicy); - var noConstraintPolicyId = SOKRATES.createPolicyDefinition(noConstraintPolicy()); - - SOKRATES.createAsset("test-asset1"); - SOKRATES.createAsset("test-asset2"); - SOKRATES.createAsset("test-asset3"); - - SOKRATES.createContractDefinition("test-asset1", "def1", noConstraintPolicyId, noConstraintPolicyId); - SOKRATES.createContractDefinition("test-asset2", "def2", onlyPlatoId, noConstraintPolicyId); - SOKRATES.createContractDefinition("test-asset3", "def3", onlyDiogenesId, noConstraintPolicyId); - - - // act - var catalog = PLATO.getCatalogDatasets(SOKRATES); - assertThat(catalog).hasSize(2); - } - - @Test - @DisplayName("Verify that Plato receives only the offers he is permitted to (using the new BPN validation)") - void requestCatalog_filteredByBpn_shouldReject() { - - var mustBeGreekPhilosopher = bpnGroupPolicy(Operator.IS_ANY_OF, "greek_customer", "philosopher"); - var mustBeGreekMathematician = bpnGroupPolicy(Operator.IS_ALL_OF, "greek_customer", "mathematician"); - - - SOKRATES.storeBusinessPartner(PLATO.getBpn(), "greek_customer", "philosopher"); - var philosopherId = SOKRATES.createPolicyDefinition(mustBeGreekPhilosopher); - var mathId = SOKRATES.createPolicyDefinition(mustBeGreekMathematician); - var noConstraintPolicyId = SOKRATES.createPolicyDefinition(noConstraintPolicy()); - - SOKRATES.createAsset("test-asset1"); - SOKRATES.createAsset("test-asset2"); - SOKRATES.createAsset("test-asset3"); - - SOKRATES.createContractDefinition("test-asset1", "def1", noConstraintPolicyId, noConstraintPolicyId); - SOKRATES.createContractDefinition("test-asset2", "def2", philosopherId, noConstraintPolicyId); - SOKRATES.createContractDefinition("test-asset3", "def3", mathId, noConstraintPolicyId); - - - // act - var catalog = PLATO.getCatalogDatasets(SOKRATES); - assertThat(catalog).hasSize(2); - } - - @Test - @DisplayName("Multiple ContractDefinitions exist for one Asset") - void requestCatalog_multipleOffersForAsset() { - SOKRATES.storeBusinessPartner(PLATO.getBpn(), "test-group"); - SOKRATES.createAsset("asset-1"); - var noConstraintId = SOKRATES.createPolicyDefinition(noConstraintPolicy()); - var groupConstraintId = SOKRATES.createPolicyDefinition(bpnGroupPolicy(Operator.IS_ANY_OF, "test-group")); - - SOKRATES.createContractDefinition("asset-1", "def1", noConstraintId, noConstraintId); - SOKRATES.createContractDefinition("asset-1", "def2", groupConstraintId, noConstraintId); - - var catalog = PLATO.getCatalogDatasets(SOKRATES); - assertThat(catalog).hasSize(1) - .allSatisfy(cd -> { - assertThat(getDatasetAssetId(cd)).isEqualTo("asset-1"); - assertThat(getDatasetPolicies(cd)).hasSize(2); - }); - } - - @Test - @DisplayName("Catalog with 1000 offers") - void requestCatalog_of1000Assets_shouldContainAll() { - var policy = bpnGroupPolicy(Operator.IS_NONE_OF, "test-group1", "test-group2"); - var policyId = SOKRATES.createPolicyDefinition(policy); - var noConstraintId = SOKRATES.createPolicyDefinition(noConstraintPolicy()); - SOKRATES.storeBusinessPartner(PLATO.getBpn(), "test-group-3"); - - range(0, 1000) - .forEach(i -> { - var assetId = "asset-" + i; - SOKRATES.createAsset(assetId); - SOKRATES.createContractDefinition(assetId, "def-" + i, policyId, noConstraintId); - }); - - // request all at once - var dataset = PLATO.getCatalogDatasets(SOKRATES, createQuery(1000, 0)); - assertThat(dataset).hasSize(1000); - - // request in chunks - var o2 = PLATO.getCatalogDatasets(SOKRATES, createQuery(500, 0)); - var o3 = PLATO.getCatalogDatasets(SOKRATES, createQuery(500, 500)); - assertThat(o2).doesNotContainAnyElementsOf(o3); - - } - - -} - diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogInMemoryTest.java deleted file mode 100644 index 1f93986a5..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogInMemoryTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.catalog; - -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.junit.jupiter.api.extension.RegisterExtension; - -@EndToEndTest -public class CatalogInMemoryTest extends AbstractCatalogTest { - - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - - @RegisterExtension - protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogPostgresqlTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogPostgresqlTest.java deleted file mode 100644 index ffde53573..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogPostgresqlTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.catalog; - -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; -import org.eclipse.tractusx.edc.lifecycle.PgParticipantRuntime; -import org.junit.jupiter.api.extension.RegisterExtension; - -@PostgresqlDbIntegrationTest -public class CatalogPostgresqlTest extends AbstractCatalogTest { - - @RegisterExtension - protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - @RegisterExtension - protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/DimCatalogIntegrationTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/DimCatalogIntegrationTest.java new file mode 100644 index 000000000..02e776aa3 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/DimCatalogIntegrationTest.java @@ -0,0 +1,130 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.catalog; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.tractusx.edc.lifecycle.DimParticipant; +import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; +import org.eclipse.tractusx.edc.tag.DimIntegrationTest; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.model.HttpResponse; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Map; +import java.util.zip.GZIPOutputStream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.connector.controlplane.test.system.utils.PolicyFixtures.noConstraintPolicy; +import static org.eclipse.edc.util.io.Ports.getFreePort; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_POLICY_NS; +import static org.eclipse.tractusx.edc.helpers.DimHelper.configureParticipant; +import static org.eclipse.tractusx.edc.lifecycle.Runtimes.dimRuntime; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_NAME; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_NAME; +import static org.eclipse.tractusx.edc.tests.helpers.CatalogHelperFunctions.getDatasetAssetId; +import static org.eclipse.tractusx.edc.tests.helpers.PolicyHelperFunctions.frameworkPolicy; +import static org.mockserver.model.HttpRequest.request; + +@DimIntegrationTest +@Disabled +public class DimCatalogIntegrationTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final Integer BDRS_PORT = getFreePort(); + private static final String BDRS_URL = "http://localhost:%s/api".formatted(BDRS_PORT); + + protected static final DimParticipant SOKRATES = configureParticipant(SOKRATES_NAME, BDRS_URL); + protected static final DimParticipant PLATO = configureParticipant(PLATO_NAME, BDRS_URL); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = dimRuntime(PLATO.getName(), PLATO.iatpConfiguration(SOKRATES)); + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = dimRuntime(SOKRATES.getName(), SOKRATES.iatpConfiguration(PLATO)); + private static ClientAndServer bdrsServer; + + @BeforeAll + static void beforeAll() { + bdrsServer = ClientAndServer.startClientAndServer(BDRS_PORT); + bdrsServer.when(request() + .withMethod("GET") + .withPath("/api/bpn-directory")) + .respond(HttpResponse.response() + .withHeader("Content-Encoding", "gzip") + .withBody(createGzipStream()) + .withStatusCode(200)); + + } + + private static byte[] createGzipStream() { + var data = Map.of(SOKRATES.getBpn(), SOKRATES.getDid(), + PLATO.getBpn(), PLATO.getDid()); + + var bas = new ByteArrayOutputStream(); + try (var gzip = new GZIPOutputStream(bas)) { + gzip.write(MAPPER.writeValueAsBytes(data)); + } catch (IOException e) { + throw new RuntimeException(e); + } + return bas.toByteArray(); + } + + @AfterAll + static void afterAll() { + bdrsServer.stop(); + } + + @Test + @DisplayName("Verify that Sokrates receives only the offers he is permitted to") + void requestCatalog_filteredByDismantler_shouldReturnOffer() { + // arrange + PLATO.createAsset("test-asset"); + PLATO.createAsset("test-asset-1"); + + var bpnAccessPolicy = frameworkPolicy(Map.of(CX_POLICY_NS + "Membership", "active")); + var dismantlerAccessPolicy = frameworkPolicy(Map.of(CX_POLICY_NS + "Dismantler", "active")); + + var bpnAccessId = PLATO.createPolicyDefinition(bpnAccessPolicy); + var contractPolicyId = PLATO.createPolicyDefinition(noConstraintPolicy()); + var dismantlerAccessPolicyId = PLATO.createPolicyDefinition(dismantlerAccessPolicy); + + PLATO.createContractDefinition("test-asset", "test-def", bpnAccessId, contractPolicyId); + PLATO.createContractDefinition("test-asset-1", "test-def-2", dismantlerAccessPolicyId, contractPolicyId); + + // act + var catalog = SOKRATES.getCatalogDatasets(PLATO); + + // assert + assertThat(catalog).isNotEmpty() + .hasSize(1) + .allSatisfy(co -> { + assertThat(getDatasetAssetId(co)).isEqualTo("test-asset"); + }); + + } + +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/MiwSsiCatalogTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/MiwSsiCatalogTest.java deleted file mode 100644 index e067c19b2..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/MiwSsiCatalogTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.catalog; - -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; -import org.eclipse.tractusx.edc.tag.MiwIntegrationTest; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.test.system.utils.PolicyFixtures.noConstraintPolicy; -import static org.eclipse.tractusx.edc.helpers.CatalogHelperFunctions.getDatasetAssetId; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.TX_NAMESPACE; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.frameworkPolicy; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.frameworkTemplatePolicy; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_NAME; - -@MiwIntegrationTest -public class MiwSsiCatalogTest { - - protected static final TxParticipant SOKRATES = TxParticipant.Builder.newInstance() - .name(SOKRATES_NAME) - .id(SOKRATES_BPN) - .build(); - static final String MIW_SOKRATES_URL = "http://localhost:8000"; - static final String OAUTH_TOKEN_URL = "http://localhost:8080/realms/miw_test/protocol/openid-connect/token"; - - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory-ssi", - SOKRATES.getName(), - SOKRATES.getBpn(), - sokratesSsiMiwConfiguration() - ); - - public static Map sokratesSsiMiwConfiguration() { - var ssiConfiguration = new HashMap() { - { - put("tx.ssi.miw.url", MIW_SOKRATES_URL); - put("tx.ssi.oauth.token.url", OAUTH_TOKEN_URL); - put("tx.ssi.oauth.client.id", "miw_private_client"); - put("tx.ssi.oauth.client.secret.alias", "client_secret_alias"); - put("tx.ssi.miw.authority.id", "BPNL000000000000"); - put("tx.ssi.miw.authority.issuer", "did:web:localhost%3A8000:BPNL000000000000"); - put("tx.vault.seed.secrets", "client_secret_alias:miw_private_client"); - put("tx.ssi.endpoint.audience", SOKRATES.getProtocolEndpoint().getUrl().toString()); - } - }; - var baseConfiguration = SOKRATES.getConfiguration(); - ssiConfiguration.putAll(baseConfiguration); - return ssiConfiguration; - } - - @Test - @DisplayName("Verify that Sokrates receives only the offers he is permitted to") - void requestCatalog_fulfillsPolicy_shouldReturnOffer() { - // arrange - SOKRATES.createAsset("test-asset"); - SOKRATES.createAsset("test-asset-1"); - - var bpnAccessPolicy = frameworkPolicy(Map.of(TX_NAMESPACE + "BPN", "active")); - var dismantlerAccessPolicy = frameworkPolicy(Map.of(TX_NAMESPACE + "Dismantler", "active")); - - var bpnAccessId = SOKRATES.createPolicyDefinition(bpnAccessPolicy); - var contractPolicyId = SOKRATES.createPolicyDefinition(noConstraintPolicy()); - var dismantlerAccessPolicyId = SOKRATES.createPolicyDefinition(dismantlerAccessPolicy); - - SOKRATES.createContractDefinition("test-asset", "test-def", bpnAccessId, contractPolicyId); - SOKRATES.createContractDefinition("test-asset-1", "test-def-2", dismantlerAccessPolicyId, contractPolicyId); - - - // act - var catalog = SOKRATES.getCatalogDatasets(SOKRATES); - - // assert - assertThat(catalog).isNotEmpty() - .hasSize(1) - .allSatisfy(co -> { - assertThat(getDatasetAssetId(co)).isEqualTo("test-asset"); - }); - - } - - - @Test - @DisplayName("Verify that Sokrates receives only the offers he is permitted to using @context") - void requestCatalog_fulfillsPolicy_shouldReturnOffer_withContexts() { - // arrange - SOKRATES.createAsset("test-asset"); - SOKRATES.createAsset("test-asset-1"); - - var bpnAccessPolicy = frameworkTemplatePolicy("test-ap1", "BPN"); - var contractPolicy = noConstraintPolicy(); - var dismantlerAccessPolicy = frameworkTemplatePolicy("test-ap2", "Dismantler"); - - SOKRATES.createPolicy(bpnAccessPolicy); - var contractPolicyId = SOKRATES.createPolicyDefinition(contractPolicy); - SOKRATES.createPolicy(dismantlerAccessPolicy); - - SOKRATES.createContractDefinition("test-asset", "test-def", "test-ap1", contractPolicyId); - SOKRATES.createContractDefinition("test-asset-1", "test-def-2", "test-ap2", contractPolicyId); - - // act - var catalog = SOKRATES.getCatalogDatasets(SOKRATES); - - // assert - assertThat(catalog).isNotEmpty() - .hasSize(1) - .allSatisfy(co -> { - assertThat(getDatasetAssetId(co)).isEqualTo("test-asset"); - }); - - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractDeleteEdrTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractDeleteEdrTest.java deleted file mode 100644 index 4a2a8164e..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractDeleteEdrTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.edr; - -import jakarta.json.Json; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Map; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.EXPIRED; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.bpnGroupPolicy; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_NAME; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_NAME; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_TIMEOUT; - -public abstract class AbstractDeleteEdrTest { - - protected static final TxParticipant SOKRATES = TxParticipant.Builder.newInstance() - .name(SOKRATES_NAME) - .id(SOKRATES_BPN) - .build(); - - protected static final TxParticipant PLATO = TxParticipant.Builder.newInstance() - .name(PLATO_NAME) - .id(PLATO_BPN) - .build(); - - @Test - @DisplayName("Verify that expired EDR are deleted") - void negotiateEdr_shouldRemoveExpiredEdrs() throws IOException { - - var assetId = UUID.randomUUID().toString(); - - var authCodeHeaderName = "test-authkey"; - var authCode = "test-authcode"; - - Map dataAddress = Map.of( - "name", "transfer-test", - "baseUrl", "http://test:8080", - "type", "HttpData", - "contentType", "application/json", - "authKey", authCodeHeaderName, - "authCode", authCode - ); - PLATO.createAsset(assetId, Map.of(), dataAddress); - - PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); - var accessPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.NEQ, "forbidden-policy")); - var contractPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.EQ, "test-group1", "test-group2")); - PLATO.createContractDefinition(assetId, "def-1", accessPolicy, contractPolicy); - - var callbacks = Json.createArrayBuilder() - .build(); - - SOKRATES.edrs().negotiateEdr(PLATO, assetId, callbacks); - - var expired = new ArrayList(); - - await().atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> { - var edrCaches = SOKRATES.edrs().getEdrEntriesByAssetId(assetId); - var localExpired = edrCaches.stream() - .filter(json -> json.asJsonObject().getJsonString("tx:edrState").getString().equals(EXPIRED.name())) - .map(json -> json.asJsonObject().getJsonString("transferProcessId").getString()) - .toList(); - assertThat(localExpired).hasSizeGreaterThan(0); - expired.add(localExpired.get(0)); - }); - - await().atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> expired.forEach((id) -> SOKRATES.edrs().getEdrRequest(id).statusCode(404))); - - } - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java deleted file mode 100644 index 1756a1f82..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java +++ /dev/null @@ -1,171 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.edr; - -import jakarta.json.Json; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationAgreed; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationFinalized; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationInitiated; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationRequested; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationVerified; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessInitiated; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessProvisioned; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessRequested; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessStarted; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; -import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.EDR_SIMPLE_TYPE; -import static org.eclipse.tractusx.edc.helpers.EdrNegotiationHelperFunctions.createCallback; -import static org.eclipse.tractusx.edc.helpers.EdrNegotiationHelperFunctions.createEvent; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.bpnGroupPolicy; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_NAME; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_NAME; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_POLL_INTERVAL; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_TIMEOUT; -import static org.eclipse.tractusx.edc.tests.edr.TestFunctions.waitForEvent; - -public abstract class AbstractNegotiateEdrTest { - - protected static final TxParticipant SOKRATES = TxParticipant.Builder.newInstance() - .name(SOKRATES_NAME) - .id(SOKRATES_BPN) - .build(); - - protected static final TxParticipant PLATO = TxParticipant.Builder.newInstance() - .name(PLATO_NAME) - .id(PLATO_BPN) - .build(); - - MockWebServer server; - - @BeforeEach - void setup() { - server = new MockWebServer(); - } - - @Test - @DisplayName("Verify that the callbacks are invoked when negotiating an EDR") - void negotiateEdr_shouldInvokeCallbacks() throws IOException { - - var expectedEvents = List.of( - createEvent(ContractNegotiationInitiated.class), - createEvent(ContractNegotiationRequested.class), - createEvent(ContractNegotiationAgreed.class), - createEvent(ContractNegotiationFinalized.class), - createEvent(ContractNegotiationVerified.class), - createEvent(TransferProcessInitiated.class), - createEvent(TransferProcessProvisioned.class), - createEvent(TransferProcessRequested.class), - createEvent(TransferProcessStarted.class)); - - var assetId = "api-asset-1"; - var url = server.url("/mock/api"); - server.start(); - - var authCodeHeaderName = "test-authkey"; - var authCode = "test-authcode"; - Map dataAddress = Map.of( - "name", "transfer-test", - "baseUrl", url.toString(), - "type", "HttpData", - "contentType", "application/json", - "authKey", authCodeHeaderName, - "authCode", authCode - ); - - PLATO.createAsset(assetId, Map.of(), dataAddress); - - PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); - var accessPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.IS_NONE_OF, "forbidden-policy")); - var contractPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.IS_ALL_OF, "test-group1", "test-group2")); - PLATO.createContractDefinition(assetId, "def-1", accessPolicy, contractPolicy); - - - expectedEvents.forEach(event -> server.enqueue(new MockResponse())); - - var callbacks = Json.createArrayBuilder() - .add(createCallback(url.toString(), true, Set.of("contract.negotiation", "transfer.process"))) - .build(); - - var contractNegotiationId = SOKRATES.edrs().negotiateEdr(PLATO, assetId, callbacks); - - var events = expectedEvents.stream() - .map(receivedEvent -> waitForEvent(server)) - .collect(Collectors.toList()); - - - await().pollInterval(ASYNC_POLL_INTERVAL) - .atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> { - var edrCaches = SOKRATES.edrs().getEdrEntriesByAssetId(assetId); - assertThat(edrCaches).hasSize(1); - }); - - assertThat(expectedEvents).usingRecursiveFieldByFieldElementComparator().containsAll(events); - - var edrCaches = SOKRATES.edrs().getEdrEntriesByAssetId(assetId); - - assertThat(edrCaches).hasSize(1); - - assertThat(SOKRATES.edrs().getEdrEntriesByContractNegotiationId(contractNegotiationId)).hasSize(1); - - assertThat(edrCaches).hasSize(1); - - var transferProcessId = edrCaches.get(0).asJsonObject().getString("transferProcessId"); - var cnId = edrCaches.get(0).asJsonObject().getString("contractNegotiationId"); - var agreementId = edrCaches.get(0).asJsonObject().getString("agreementId"); - - assertThat(cnId).isEqualTo(contractNegotiationId); - assertThat(SOKRATES.edrs().getEdrEntriesByAgreementId(agreementId)).hasSize(1); - - - var edr = SOKRATES.edrs().getEdr(transferProcessId); - - assertThat(edr.getJsonString("type").getString()).isEqualTo(EDR_SIMPLE_TYPE); - assertThat(edr.getJsonString("authCode").getString()).isNotNull(); - assertThat(edr.getJsonString("authKey").getString()).isNotNull(); - assertThat(edr.getJsonString("endpoint").getString()).isNotNull(); - assertThat(edr.getJsonString("id").getString()).isEqualTo(transferProcessId); - - } - - @AfterEach - void teardown() throws IOException { - server.shutdown(); - } - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractRenewalEdrTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractRenewalEdrTest.java deleted file mode 100644 index 14a673495..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractRenewalEdrTest.java +++ /dev/null @@ -1,183 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.edr; - -import jakarta.json.Json; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.assertj.core.api.Condition; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessStarted; -import org.eclipse.edc.connector.transfer.spi.types.TransferProcessStates; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.anyOf; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; -import static org.awaitility.pollinterval.FibonacciPollInterval.fibonacci; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.EXPIRED; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.NEGOTIATED; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.REFRESHING; -import static org.eclipse.tractusx.edc.helpers.EdrNegotiationHelperFunctions.createCallback; -import static org.eclipse.tractusx.edc.helpers.EdrNegotiationHelperFunctions.createEvent; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.bpnGroupPolicy; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_NAME; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_NAME; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_POLL_INTERVAL; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_TIMEOUT; -import static org.eclipse.tractusx.edc.tests.edr.TestFunctions.waitForEvent; - -public abstract class AbstractRenewalEdrTest { - - protected static final TxParticipant SOKRATES = TxParticipant.Builder.newInstance() - .name(SOKRATES_NAME) - .id(SOKRATES_BPN) - .build(); - - protected static final TxParticipant PLATO = TxParticipant.Builder.newInstance() - .name(PLATO_NAME) - .id(PLATO_BPN) - .build(); - - MockWebServer server; - - @BeforeEach - void setup() { - server = new MockWebServer(); - } - - @Test - @DisplayName("Verify that the EDR is renewed") - void negotiateEdr_shouldRenewTheEdr() throws IOException { - - var expectedEvents = List.of( - createEvent(TransferProcessStarted.class), - createEvent(TransferProcessStarted.class)); - - var assetId = UUID.randomUUID().toString(); - var url = server.url("/mock/api"); - server.start(); - - var authCodeHeaderName = "test-authkey"; - var authCode = "test-authcode"; - - Map dataAddress = Map.of( - "name", "transfer-test", - "baseUrl", url.toString(), - "type", "HttpData", - "contentType", "application/json", - "authKey", authCodeHeaderName, - "authCode", authCode - ); - - PLATO.createAsset(assetId, Map.of(), dataAddress); - - PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); - var accessPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.IS_NONE_OF, "forbidden-policy")); - var contractPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.IS_ANY_OF, "test-group1", "test-group2")); - PLATO.createContractDefinition(assetId, "def-1", accessPolicy, contractPolicy); - - var callbacks = Json.createArrayBuilder() - .add(createCallback(url.toString(), true, Set.of("transfer.process.started"))) - .build(); - - expectedEvents.forEach(event -> server.enqueue(new MockResponse())); - - SOKRATES.edrs().negotiateEdr(PLATO, assetId, callbacks); - - var events = expectedEvents.stream() - .map(receivedEvent -> waitForEvent(server)) - .collect(Collectors.toList()); - - assertThat(expectedEvents).usingRecursiveFieldByFieldElementComparator().containsAll(events); - - var edrCachesBuilder = Json.createArrayBuilder(); - - await().atMost(ASYNC_TIMEOUT) - .pollInterval(ASYNC_POLL_INTERVAL) - .untilAsserted(() -> { - var localEdrCaches = SOKRATES.edrs().getEdrEntriesByAssetId(assetId); - assertThat(localEdrCaches).hasSizeGreaterThan(1); - localEdrCaches.forEach(edrCachesBuilder::add); - }); - - var edrCaches = edrCachesBuilder.build(); - - assertThat(edrCaches) - .extracting(json -> json.asJsonObject().getJsonString("tx:edrState").getString()) - .areAtMost(1, anyOf(stateCondition(NEGOTIATED.name(), "Negotiated"), stateCondition(REFRESHING.name(), "Refreshing"))) - .areAtLeast(1, stateCondition(EXPIRED.name(), "Expired")); - - var transferProcessId = edrCaches.stream() - .filter(json -> json.asJsonObject().getJsonString("tx:edrState").getString().equals(EXPIRED.name())) - .map(json -> json.asJsonObject().getJsonString("transferProcessId").getString()) - .findFirst() - .orElseThrow(); - - // Check Termination on Sokrates - await().pollInterval(fibonacci()) - .atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> { - var tpState = SOKRATES.getTransferProcessState(transferProcessId); - assertThat(tpState).isNotNull().isEqualTo(TransferProcessStates.TERMINATED.toString()); - }); - - - // Check Termination on Plato - await().pollInterval(fibonacci()) - .atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> { - var tpState = PLATO.getTransferProcesses() - .stream() - .filter(json -> json.asJsonObject().getJsonString("correlationId").getString().equals(transferProcessId)) - .map(json -> json.asJsonObject().getJsonString("state").getString()) - .findFirst(); - - assertThat(tpState) - .isPresent() - .hasValue(TransferProcessStates.TERMINATED.toString()); - }); - } - - - @AfterEach - void teardown() throws IOException { - server.shutdown(); - } - - - private Condition stateCondition(String value, String description) { - return new Condition<>(m -> m.equals(value), description); - } - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/DeleteEdrInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/DeleteEdrInMemoryTest.java deleted file mode 100644 index 4e1c606b1..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/DeleteEdrInMemoryTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.edr; - - -import com.nimbusds.jose.util.Base64; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.security.SecureRandom; - -@EndToEndTest -public class DeleteEdrInMemoryTest extends AbstractDeleteEdrTest { - - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.renewalConfiguration("5") - ); - - @RegisterExtension - protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - PLATO.getName(), - PLATO.getBpn(), - PLATO.renewalConfiguration() - ); - - @BeforeAll - static void prepare() { - var bytes = new byte[32]; - - new SecureRandom().nextBytes(bytes); - var value = Base64.encode(bytes).toString(); - var vault = SOKRATES_RUNTIME.getContext().getService(Vault.class); - vault.storeSecret("test-alias", value); - vault = PLATO_RUNTIME.getContext().getService(Vault.class); - vault.storeSecret("test-alias", value); - - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/DeleteEdrPostgresqlTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/DeleteEdrPostgresqlTest.java deleted file mode 100644 index e86adb03a..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/DeleteEdrPostgresqlTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.edr; - -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; -import org.eclipse.tractusx.edc.lifecycle.PgParticipantRuntime; -import org.junit.jupiter.api.extension.RegisterExtension; - -@PostgresqlDbIntegrationTest -public class DeleteEdrPostgresqlTest extends AbstractDeleteEdrTest { - - @RegisterExtension - protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.renewalConfiguration("5") - ); - @RegisterExtension - protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - PLATO.getName(), - PLATO.getBpn(), - PLATO.renewalConfiguration() - ); - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrInMemoryTest.java deleted file mode 100644 index 8f7dd70a7..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrInMemoryTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.edr; - - -import com.nimbusds.jose.util.Base64; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.security.SecureRandom; - -@EndToEndTest -public class NegotiateEdrInMemoryTest extends AbstractNegotiateEdrTest { - - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - - @RegisterExtension - protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); - - @BeforeAll - static void prepare() { - var bytes = new byte[32]; - - new SecureRandom().nextBytes(bytes); - var value = Base64.encode(bytes).toString(); - var vault = SOKRATES_RUNTIME.getContext().getService(Vault.class); - vault.storeSecret("test-alias", value); - vault = PLATO_RUNTIME.getContext().getService(Vault.class); - vault.storeSecret("test-alias", value); - - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrPostgresqlHashicorpVaultTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrPostgresqlHashicorpVaultTest.java deleted file mode 100644 index 59de6d7dc..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrPostgresqlHashicorpVaultTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.edr; - -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; -import org.eclipse.tractusx.edc.lifecycle.PgHashicorpParticipantRuntime; -import org.eclipse.tractusx.edc.lifecycle.PgParticipantRuntime; -import org.junit.jupiter.api.extension.RegisterExtension; - -@PostgresqlDbIntegrationTest -public class NegotiateEdrPostgresqlHashicorpVaultTest extends AbstractNegotiateEdrTest { - - @RegisterExtension - protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); - private static final String VAULT_DIRECTORY = "testDir/"; - - @RegisterExtension - protected static final PgHashicorpParticipantRuntime SOKRATES_RUNTIME = new PgHashicorpParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql-hashicorp", - SOKRATES.getName(), - SOKRATES.getBpn(), - VAULT_DIRECTORY, - SOKRATES.getConfiguration() - ); - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrPostgresqlTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrPostgresqlTest.java deleted file mode 100644 index 671298b12..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrPostgresqlTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.edr; - -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; -import org.eclipse.tractusx.edc.lifecycle.PgParticipantRuntime; -import org.junit.jupiter.api.extension.RegisterExtension; - -@PostgresqlDbIntegrationTest -public class NegotiateEdrPostgresqlTest extends AbstractNegotiateEdrTest { - - @RegisterExtension - protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - @RegisterExtension - protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/RenewalEdrInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/RenewalEdrInMemoryTest.java deleted file mode 100644 index ae0b7df87..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/RenewalEdrInMemoryTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.edr; - - -import com.nimbusds.jose.util.Base64; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.security.SecureRandom; - -@EndToEndTest -public class RenewalEdrInMemoryTest extends AbstractRenewalEdrTest { - - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.renewalConfiguration() - ); - - @RegisterExtension - protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - PLATO.getName(), - PLATO.getBpn(), - PLATO.renewalConfiguration() - ); - - @BeforeAll - static void prepare() { - var bytes = new byte[32]; - - new SecureRandom().nextBytes(bytes); - var value = Base64.encode(bytes).toString(); - var vault = SOKRATES_RUNTIME.getContext().getService(Vault.class); - vault.storeSecret("test-alias", value); - vault = PLATO_RUNTIME.getContext().getService(Vault.class); - vault.storeSecret("test-alias", value); - - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/RenewalEdrPostgresqlTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/RenewalEdrPostgresqlTest.java deleted file mode 100644 index 0f8d81584..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/RenewalEdrPostgresqlTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.edr; - -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; -import org.eclipse.tractusx.edc.lifecycle.PgParticipantRuntime; -import org.junit.jupiter.api.extension.RegisterExtension; - -@PostgresqlDbIntegrationTest -public class RenewalEdrPostgresqlTest extends AbstractRenewalEdrTest { - - @RegisterExtension - protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.renewalConfiguration() - ); - @RegisterExtension - protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - PLATO.getName(), - PLATO.getBpn(), - PLATO.renewalConfiguration() - ); - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/negotiation/AbstractContractNegotiateTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/negotiation/AbstractContractNegotiateTest.java deleted file mode 100644 index df1d470f4..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/negotiation/AbstractContractNegotiateTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.negotiation; - -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.TX_NAMESPACE; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.bpnGroupPolicy; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.frameworkPolicy; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_NAME; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_NAME; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_POLL_INTERVAL; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_TIMEOUT; - -public abstract class AbstractContractNegotiateTest { - - protected static final TxParticipant SOKRATES = TxParticipant.Builder.newInstance() - .name(SOKRATES_NAME) - .id(SOKRATES_BPN) - .build(); - - protected static final TxParticipant PLATO = TxParticipant.Builder.newInstance() - .name(PLATO_NAME) - .id(PLATO_BPN) - .build(); - - @Test - @DisplayName("Verify contract negotiation fails with wrong policy") - void contractNegotiation_shouldFail_whenPolicyEvaluationFails() { - var assetId = "api-asset-1"; - var authCodeHeaderName = "test-authkey"; - var authCode = "test-authcode"; - - Map dataAddress = Map.of( - "name", "transfer-test", - "baseUrl", "http://testurl", - "type", "HttpData", - "contentType", "application/json", - "authKey", authCodeHeaderName, - "authCode", authCode - ); - - PLATO.createAsset(assetId, Map.of(), dataAddress); - - PLATO.storeBusinessPartner(SOKRATES.getBpn(), "allowed-group"); - var accessPolicyId = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.NEQ, "forbidden-group")); - var contractPolicyId = PLATO.createPolicyDefinition(frameworkPolicy(Map.of(TX_NAMESPACE + "Dismantler", "active"))); - PLATO.createContractDefinition(assetId, "def-1", accessPolicyId, contractPolicyId); - - var negotiationId = SOKRATES.initContractNegotiation(PLATO, assetId); - - // wait for the failed contract negotiation - await().pollInterval(ASYNC_POLL_INTERVAL) - .atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> { - var negotiationState = SOKRATES.getContractNegotiationState(negotiationId); - assertThat(negotiationState).isEqualTo(ContractNegotiationStates.TERMINATED.toString()); - var error = SOKRATES.getContractNegotiationError(negotiationId); - - assertThat(error).isNotNull(); - assertThat(error).contains("Contract offer is not valid: Policy in scope contract.negotiation not fulfilled"); - }); - } - - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/negotiation/SsiContractNegotiationInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/negotiation/SsiContractNegotiationInMemoryTest.java deleted file mode 100644 index 2fb8a50f2..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/negotiation/SsiContractNegotiationInMemoryTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.negotiation; - -import okhttp3.mockwebserver.MockWebServer; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.eclipse.tractusx.edc.token.KeycloakDispatcher; -import org.eclipse.tractusx.edc.token.MiwDispatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.io.IOException; - -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; - -@EndToEndTest -public class SsiContractNegotiationInMemoryTest extends AbstractContractNegotiateTest { - public static final String SUMMARY_VC_TEMPLATE = "summary-vc-no-dismantler.json"; - - @RegisterExtension - protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory-ssi", - PLATO.getName(), - PLATO.getBpn(), - PLATO.ssiConfiguration() - ); - - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory-ssi", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.ssiConfiguration() - ); - private static MockWebServer miwSokratesServer; - private static MockWebServer miwPlatoServer; - private static MockWebServer sokratesOauthServer; - private static MockWebServer platoOauthServer; - - - @BeforeAll - static void setup() throws IOException { - miwSokratesServer = new MockWebServer(); - miwPlatoServer = new MockWebServer(); - sokratesOauthServer = new MockWebServer(); - platoOauthServer = new MockWebServer(); - - - var credentialSubjectId = "did:web:example.com"; - - miwSokratesServer.start(SOKRATES.miwEndpoint().getPort()); - miwSokratesServer.setDispatcher(new MiwDispatcher(SOKRATES_BPN, SUMMARY_VC_TEMPLATE, credentialSubjectId, PLATO.getProtocolEndpoint().getUrl().toString())); - - miwPlatoServer.start(PLATO.miwEndpoint().getPort()); - miwPlatoServer.setDispatcher(new MiwDispatcher(PLATO_BPN, SUMMARY_VC_TEMPLATE, credentialSubjectId, SOKRATES.getProtocolEndpoint().getUrl().toString())); - - sokratesOauthServer.start(SOKRATES.authTokenEndpoint().getPort()); - sokratesOauthServer.setDispatcher(new KeycloakDispatcher()); - - platoOauthServer.start(PLATO.authTokenEndpoint().getPort()); - platoOauthServer.setDispatcher(new KeycloakDispatcher()); - } - - @AfterAll - static void teardown() throws IOException { - miwSokratesServer.shutdown(); - miwPlatoServer.shutdown(); - sokratesOauthServer.shutdown(); - platoOauthServer.shutdown(); - - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/AbstractPolicyMonitorTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/AbstractPolicyMonitorTest.java deleted file mode 100644 index fbad57b4c..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/AbstractPolicyMonitorTest.java +++ /dev/null @@ -1,120 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.policy; - -import jakarta.json.Json; -import jakarta.json.JsonObject; -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.Map; -import java.util.UUID; - -import static jakarta.json.Json.createObjectBuilder; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; -import static org.eclipse.edc.connector.transfer.spi.types.TransferProcessStates.STARTED; -import static org.eclipse.edc.connector.transfer.spi.types.TransferProcessStates.TERMINATED; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -import static org.eclipse.edc.test.system.utils.PolicyFixtures.inForceDatePolicy; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_NAME; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_NAME; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_TIMEOUT; - -public abstract class AbstractPolicyMonitorTest { - - protected static final TxParticipant SOKRATES = TxParticipant.Builder.newInstance() - .name(SOKRATES_NAME) - .id(SOKRATES_BPN) - .build(); - - - protected static final TxParticipant PLATO = TxParticipant.Builder.newInstance() - .name(PLATO_NAME) - .id(PLATO_BPN) - .build(); - private final MockWebServer server = new MockWebServer(); - - @Test - void shouldTerminateTransfer_whenPolicyExpires() throws IOException { - var assetId = UUID.randomUUID().toString(); - - Map dataAddress = Map.of( - "name", "transfer-test", - "baseUrl", "http://localhost:" + server.getPort(), - "type", "HttpData", - "contentType", "application/json", - "proxyQueryParams", "true" - ); - PLATO.createAsset(assetId, Map.of(), dataAddress); - startHttpServerProvider(); - - var policy = inForceDatePolicy("gteq", "contractAgreement+0s", "lteq", "contractAgreement+10s"); - var policyId = PLATO.createPolicyDefinition(policy); - PLATO.createContractDefinition(assetId, UUID.randomUUID().toString(), policyId, policyId); - - var consumerUrl = server.url("/mock/api/consumer"); - var destination = httpDataAddress(consumerUrl.toString()); - - var transferProcessId = SOKRATES.requestAsset(PLATO, assetId, Json.createObjectBuilder().build(), destination); - await().atMost(ASYNC_TIMEOUT).untilAsserted(() -> { - var state = SOKRATES.getTransferProcessState(transferProcessId); - assertThat(state).isEqualTo(STARTED.name()); - }); - - await().atMost(ASYNC_TIMEOUT).untilAsserted(() -> { - var state = SOKRATES.getTransferProcessState(transferProcessId); - assertThat(state).isEqualTo(TERMINATED.name()); - }); - } - - private JsonObject httpDataAddress(String baseUrl) { - return createObjectBuilder() - .add(TYPE, EDC_NAMESPACE + "DataAddress") - .add(EDC_NAMESPACE + "type", "HttpData") - .add(EDC_NAMESPACE + "properties", createObjectBuilder() - .add(EDC_NAMESPACE + "baseUrl", baseUrl) - .build()) - .build(); - } - - private void startHttpServerProvider() throws IOException { - server.setDispatcher(new Dispatcher() { - @NotNull - @Override - public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) { - if ("/mock/api/provider".equals(recordedRequest.getPath().split("\\?")[0])) { - return new MockResponse().setResponseCode(200); - } - return new MockResponse().setResponseCode(404); - } - }); - server.start(); - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/PolicyMonitorInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/PolicyMonitorInMemoryTest.java deleted file mode 100644 index e1f353b4c..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/PolicyMonitorInMemoryTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.policy; - -import com.nimbusds.jose.util.Base64; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.security.SecureRandom; - -@EndToEndTest -public class PolicyMonitorInMemoryTest extends AbstractPolicyMonitorTest { - - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - - @RegisterExtension - protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); - - @BeforeAll - static void prepare() { - var bytes = new byte[32]; - - new SecureRandom().nextBytes(bytes); - var value = Base64.encode(bytes).toString(); - SOKRATES_RUNTIME.getContext().getService(Vault.class).storeSecret("test-alias", value); - PLATO_RUNTIME.getContext().getService(Vault.class).storeSecret("test-alias", value); - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/PolicyMonitorPostgresqlTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/PolicyMonitorPostgresqlTest.java deleted file mode 100644 index b3bf139a1..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/PolicyMonitorPostgresqlTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.policy; - -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; -import org.eclipse.tractusx.edc.lifecycle.PgParticipantRuntime; -import org.junit.jupiter.api.extension.RegisterExtension; - -@PostgresqlDbIntegrationTest -public class PolicyMonitorPostgresqlTest extends AbstractPolicyMonitorTest { - - @RegisterExtension - protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - @RegisterExtension - protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/AbstractDataPlaneProxyTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/AbstractDataPlaneProxyTest.java deleted file mode 100644 index f5f020bd3..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/AbstractDataPlaneProxyTest.java +++ /dev/null @@ -1,328 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.proxy; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.json.Json; -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessStarted; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.spi.event.EventEnvelope; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; -import static org.eclipse.tractusx.edc.helpers.EdrNegotiationHelperFunctions.createCallback; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.bpnGroupPolicy; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_NAME; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_NAME; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_POLL_INTERVAL; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_TIMEOUT; - -public abstract class AbstractDataPlaneProxyTest { - - protected static final TxParticipant SOKRATES = TxParticipant.Builder.newInstance() - .name(SOKRATES_NAME) - .id(SOKRATES_BPN) - .build(); - - protected static final TxParticipant PLATO = TxParticipant.Builder.newInstance() - .name(PLATO_NAME) - .id(PLATO_BPN) - .build(); - private static final String CUSTOM_BASE_PATH = "/custom"; - private static final String CUSTOM_SUB_PATH = "/sub"; - private static final String CUSTOM_QUERY_PARAMS = "foo=bar"; - private static final String CUSTOM_FULL_PATH = CUSTOM_BASE_PATH + CUSTOM_SUB_PATH + "?" + CUSTOM_QUERY_PARAMS; - private final ObjectMapper mapper = new ObjectMapper(); - private MockWebServer server; - - @NotNull - private static Map dataAddress(String url) { - return Map.of( - "baseUrl", url, - "type", "HttpData", - "contentType", "application/json", - "authKey", "test-authkey", - "authCode", "test-authcode", - "proxyPath", "true", - "proxyQueryParams", "true" - ); - } - - @Test - @DisplayName("Verify E2E flow with Data Plane proxies and EDR") - void httpPullDataTransfer_withEdrAndProxy() { - - var eventsUrl = server.url(PLATO.backendProviderProxy().getPath()); - - var assetId = UUID.randomUUID().toString(); - - var dataAddress = dataAddress(eventsUrl.url().toString()); - - PLATO.createAsset(assetId, Map.of(), dataAddress); - - PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); - var accessPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.IS_ANY_OF, "test-group1")); - var contractPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.IS_ALL_OF, "test-group1", "test-group2")); - PLATO.createContractDefinition(assetId, "def-1", accessPolicy, contractPolicy); - - var callbacks = Json.createArrayBuilder() - .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.started"))) - .build(); - - // response to callback - server.enqueue(new MockResponse()); - - SOKRATES.edrs().negotiateEdr(PLATO, assetId, callbacks); - - var transferEvent = waitForTransferCompletion(); - - await().pollInterval(ASYNC_POLL_INTERVAL) - .atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> { - var edrCaches = SOKRATES.edrs().getEdrEntriesByAssetId(assetId); - assertThat(edrCaches).hasSize(1); - }); - - var body = "{\"response\": \"ok\"}"; - - server.enqueue(new MockResponse().setBody(body)); - var data = SOKRATES.data().pullProxyDataByAssetId(PLATO, assetId); - assertThat(data).isEqualTo(body); - - server.enqueue(new MockResponse().setBody(body)); - data = SOKRATES.data().pullProxyDataByTransferProcessId(PLATO, transferEvent.getPayload().getTransferProcessId()); - assertThat(data).isEqualTo(body); - } - - @Test - @DisplayName("Verify E2E flow with Data Plane proxies fails when EDR is not found") - void httpPullDataTransfer_withoutEdr() throws IOException { - - var eventsUrl = server.url(PLATO.backendProviderProxy().getPath()); - var assetId = UUID.randomUUID().toString(); - - PLATO.createAsset(assetId, Map.of(), dataAddress(eventsUrl.url().toString())); - - - PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); - var accessPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.NEQ, "forbidden-policy")); - var contractPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.EQ, "test-group1", "test-group2")); - PLATO.createContractDefinition(assetId, "def-1", accessPolicy, contractPolicy); - - - SOKRATES.data().pullProxyDataResponseByAssetId(PLATO, assetId) - .then() - .assertThat().statusCode(400); - - } - - @Test - @DisplayName("Verify E2E flow with Data Plane proxies and Two EDR") - void httpPullDataTransfer_shouldFailForAsset_withTwoEdrAndProxy() throws IOException { - - var eventsUrl = server.url(PLATO.backendProviderProxy().getPath()); - - var assetId = UUID.randomUUID().toString(); - - PLATO.createAsset(assetId, Map.of(), dataAddress(eventsUrl.url().toString())); - - PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); - var accessPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.IS_NONE_OF, "forbidden-policy")); - var contractPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.IS_ALL_OF, "test-group1", "test-group2")); - PLATO.createContractDefinition(assetId, "def-1", accessPolicy, contractPolicy); - - var callbacks = Json.createArrayBuilder() - .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.started"))) - .build(); - - // response to callback - server.enqueue(new MockResponse()); - server.enqueue(new MockResponse()); - - SOKRATES.edrs().negotiateEdr(PLATO, assetId, callbacks); - SOKRATES.edrs().negotiateEdr(PLATO, assetId, callbacks); - - var transferEvent1 = waitForTransferCompletion(); - var transferEvent2 = waitForTransferCompletion(); - - await().pollInterval(ASYNC_POLL_INTERVAL) - .atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> { - var edrCaches = SOKRATES.edrs().getEdrEntriesByAssetId(assetId); - assertThat(edrCaches).hasSize(2); - }); - - - var body = "{\"response\": \"ok\"}"; - - server.enqueue(new MockResponse().setBody(body)); - SOKRATES.data().pullProxyDataResponseByAssetId(PLATO, assetId).then() - .assertThat().statusCode(428); - - server.enqueue(new MockResponse().setBody(body)); - var data = SOKRATES.data().pullProxyDataByTransferProcessId(PLATO, transferEvent1.getPayload().getTransferProcessId()); - assertThat(data).isEqualTo(body); - - server.enqueue(new MockResponse().setBody(body)); - data = SOKRATES.data().pullProxyDataByTransferProcessId(PLATO, transferEvent2.getPayload().getTransferProcessId()); - assertThat(data).isEqualTo(body); - } - - @Test - @DisplayName("Verify E2E flow with Data Plane provider and EDR") - void httpPullDataTransfer_withEdrAndProviderDataPlaneProxy() throws IOException { - - var eventsUrl = server.url(PLATO.backendProviderProxy().getPath()); - var assetId = UUID.randomUUID().toString(); - PLATO.createAsset(assetId, Map.of(), dataAddress(eventsUrl.url().toString())); - - PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); - var accessPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.IS_ANY_OF, "test-group1")); - var contractPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.IS_ALL_OF, "test-group1", "test-group2")); - PLATO.createContractDefinition(assetId, "def-1", accessPolicy, contractPolicy); - - var callbacks = Json.createArrayBuilder() - .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.started"))) - .build(); - - // response to callback - server.enqueue(new MockResponse()); - - SOKRATES.edrs().negotiateEdr(PLATO, assetId, callbacks); - - var transferEvent = waitForTransferCompletion(); - - await().pollInterval(ASYNC_POLL_INTERVAL) - .atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> { - var edrCaches = SOKRATES.edrs().getEdrEntriesByAssetId(assetId); - assertThat(edrCaches).hasSize(1); - }); - - var body = "{\"response\": \"ok\"}"; - - server.enqueue(new MockResponse().setBody(body)); - var data = SOKRATES.data().pullProviderDataPlaneDataByAssetId(PLATO, assetId); - assertThat(data).isEqualTo(body); - - server.enqueue(new MockResponse().setBody(body)); - data = SOKRATES.data().pullProviderDataPlaneDataByTransferProcessId(PLATO, transferEvent.getPayload().getTransferProcessId()); - assertThat(data).isEqualTo(body); - } - - @Test - @DisplayName("Verify E2E flow with Data Plane provider and EDR") - void httpPullDataTransfer_withEdrAndProviderDataPlaneProxyAndCustomProperties() throws IOException { - - var eventsPath = PLATO.backendProviderProxy().getPath(); - var eventsUrl = server.url(eventsPath); - - var customUrl = server.url(CUSTOM_BASE_PATH); - - var body = "{\"response\": \"ok\"}"; - - server.setDispatcher(new Dispatcher() { - @NotNull - @Override - public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) throws InterruptedException { - var path = recordedRequest.getPath(); - if (PLATO.backendProviderProxy().getPath().equals(path)) { - return new MockResponse(); - } else if (CUSTOM_FULL_PATH.equals(path)) { - return new MockResponse().setBody(body); - } else { - return new MockResponse().setResponseCode(404); - } - } - }); - - var assetId = UUID.randomUUID().toString(); - PLATO.createAsset(assetId, Map.of(), dataAddress(customUrl.url().toString())); - - PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); - var accessPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.NEQ, "forbidden-policy")); - var contractPolicy = PLATO.createPolicyDefinition(bpnGroupPolicy(Operator.EQ, "test-group1", "test-group2")); - PLATO.createContractDefinition(assetId, "def-1", accessPolicy, contractPolicy); - - var callbacks = Json.createArrayBuilder() - .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.started"))) - .build(); - - SOKRATES.edrs().negotiateEdr(PLATO, assetId, callbacks); - - waitForTransferCompletion(); - - await().pollInterval(ASYNC_POLL_INTERVAL) - .atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> { - var edrCaches = SOKRATES.edrs().getEdrEntriesByAssetId(assetId); - assertThat(edrCaches).hasSize(1); - }); - - var data = SOKRATES.data().pullProviderDataPlaneDataByAssetIdAndCustomProperties(PLATO, assetId, CUSTOM_SUB_PATH, CUSTOM_QUERY_PARAMS); - assertThat(data).isEqualTo(body); - - } - - @BeforeEach - void setup() throws IOException { - server = new MockWebServer(); - server.start(PLATO.backendProviderProxy().getPort()); - } - - @AfterEach - void teardown() throws IOException { - server.shutdown(); - } - - private EventEnvelope waitForTransferCompletion() { - try { - var request = server.takeRequest(60, TimeUnit.SECONDS); - if (request != null) { - return mapper.readValue(request.getBody().inputStream(), new TypeReference<>() { - }); - } else { - throw new RuntimeException("Timeout exceeded waiting for events"); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/DataPlaneProxyInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/DataPlaneProxyInMemoryTest.java deleted file mode 100644 index 66f959807..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/DataPlaneProxyInMemoryTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.proxy; - -import com.nimbusds.jose.util.Base64; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.security.SecureRandom; - -@EndToEndTest -public class DataPlaneProxyInMemoryTest extends AbstractDataPlaneProxyTest { - - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - - @RegisterExtension - protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); - - @BeforeAll - static void prepare() { - var bytes = new byte[32]; - - new SecureRandom().nextBytes(bytes); - var value = Base64.encode(bytes).toString(); - var vault = SOKRATES_RUNTIME.getContext().getService(Vault.class); - vault.storeSecret("test-alias", value); - vault = PLATO_RUNTIME.getContext().getService(Vault.class); - vault.storeSecret("test-alias", value); - - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/DataPlaneProxyPostgresqlTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/DataPlaneProxyPostgresqlTest.java deleted file mode 100644 index 09cbc7db7..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/DataPlaneProxyPostgresqlTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.proxy; - -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; -import org.eclipse.tractusx.edc.lifecycle.PgParticipantRuntime; -import org.junit.jupiter.api.extension.RegisterExtension; - -@PostgresqlDbIntegrationTest -public class DataPlaneProxyPostgresqlTest extends AbstractDataPlaneProxyTest { - - @RegisterExtension - protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - @RegisterExtension - protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpConsumerPullWithProxyTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpConsumerPullWithProxyTest.java deleted file mode 100644 index 9f33897de..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpConsumerPullWithProxyTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.transfer; - -import jakarta.json.Json; -import jakarta.json.JsonObject; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.eclipse.edc.connector.transfer.spi.types.TransferProcessStates; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; -import static org.awaitility.pollinterval.FibonacciPollInterval.fibonacci; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.bnpPolicy; -import static org.eclipse.tractusx.edc.helpers.TransferProcessHelperFunctions.createProxyRequest; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_NAME; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_NAME; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_TIMEOUT; - -public abstract class AbstractHttpConsumerPullWithProxyTest { - - protected static final TxParticipant SOKRATES = TxParticipant.Builder.newInstance() - .name(SOKRATES_NAME) - .id(SOKRATES_BPN) - .build(); - - protected static final TxParticipant PLATO = TxParticipant.Builder.newInstance() - .name(PLATO_NAME) - .id(PLATO_BPN) - .build(); - - private MockWebServer server; - - @BeforeEach - void setup() throws IOException { - server = new MockWebServer(); - } - - @Test - void transferData_privateBackend() throws IOException, InterruptedException { - var assetId = "api-asset-1"; - var url = server.url("/mock/api"); - server.start(); - - var authCodeHeaderName = "test-authkey"; - var authCode = "test-authcode"; - - Map dataAddress = Map.of( - "baseUrl", url.toString(), - "type", "HttpData", - "contentType", "application/json", - "authKey", authCodeHeaderName, - "authCode", authCode - ); - - PLATO.createAsset(assetId, Map.of(), dataAddress); - - var accessPolicyId = PLATO.createPolicyDefinition(createAccessPolicy(SOKRATES.getBpn())); - var contractPolicyId = PLATO.createPolicyDefinition(createContractPolicy(SOKRATES.getBpn())); - PLATO.createContractDefinition(assetId, "def-1", accessPolicyId, contractPolicyId); - var transferProcessId = SOKRATES.requestAsset(PLATO, assetId, Json.createObjectBuilder().build(), createProxyRequest()); - - var contractAgreementId = new AtomicReference(); - var edr = new AtomicReference(); - - // wait until transfer process completes - await().pollInterval(fibonacci()) - .atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> { - var tpState = SOKRATES.getTransferProcessState(transferProcessId); - assertThat(tpState).isNotNull().isEqualTo(TransferProcessStates.STARTED.toString()); - }); - - // wait until EDC is available on the consumer side - server.enqueue(new MockResponse().setBody("test response").setResponseCode(200)); - await().pollInterval(fibonacci()) - .atMost(ASYNC_TIMEOUT) - .untilAsserted(() -> { - edr.set(SOKRATES.edrs().getDataReferenceFromBackend(transferProcessId)); - assertThat(edr).isNotNull(); - }); - - // pull data out of provider's backend service: - // Cons-DP -> Prov-DP -> Prov-backend - assertThat(SOKRATES.data().pullData(edr.get(), Map.of())).isEqualTo("test response"); - var rq = server.takeRequest(); - assertThat(rq.getHeader(authCodeHeaderName)).isEqualTo(authCode); - assertThat(rq.getHeader("Edc-Contract-Agreement-Id")).isEqualTo(edr.get().getContractId()); - assertThat(rq.getHeader("Edc-Bpn")).isEqualTo(SOKRATES.getBpn()); - assertThat(rq.getMethod()).isEqualToIgnoringCase("GET"); - } - - @AfterEach - void teardown() throws IOException { - server.shutdown(); - } - - protected JsonObject createAccessPolicy(String bpn) { - return bnpPolicy(bpn); - } - - protected JsonObject createContractPolicy(String bpn) { - return bnpPolicy(bpn); - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpProviderPushTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpProviderPushTest.java deleted file mode 100644 index 623d52256..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpProviderPushTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.transfer; - -import jakarta.json.JsonObject; -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.Map; -import java.util.UUID; - -import static jakarta.json.Json.createObjectBuilder; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; -import static org.eclipse.edc.connector.transfer.spi.types.TransferProcessStates.COMPLETED; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.bnpPolicy; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_NAME; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_NAME; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_TIMEOUT; - -public abstract class AbstractHttpProviderPushTest { - - protected static final TxParticipant SOKRATES = TxParticipant.Builder.newInstance() - .name(SOKRATES_NAME) - .id(SOKRATES_BPN) - .build(); - - protected static final TxParticipant PLATO = TxParticipant.Builder.newInstance() - .name(PLATO_NAME) - .id(PLATO_BPN) - .build(); - - private MockWebServer server; - - @BeforeEach - void setup() { - server = new MockWebServer(); - } - - @Test - void httpPushDataTransfer() throws IOException { - var assetId = UUID.randomUUID().toString(); - - var providerUrl = server.url("/mock/api/provider"); - var consumerUrl = server.url("/mock/api/consumer"); - - server.setDispatcher(new Dispatcher() { - @NotNull - @Override - public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) throws InterruptedException { - return switch (recordedRequest.getPath().split("\\?")[0]) { - case "/mock/api/provider" -> new MockResponse().setResponseCode(200); - case "/mock/api/consumer" -> new MockResponse().setResponseCode(200); - default -> new MockResponse().setResponseCode(404); - }; - } - }); - - server.start(); - - Map dataAddress = Map.of( - "name", "transfer-test", - "baseUrl", providerUrl.toString(), - "type", "HttpData", - "contentType", "application/json" - ); - - PLATO.createAsset(assetId, Map.of(), dataAddress); - var policyId = PLATO.createPolicyDefinition(bnpPolicy(SOKRATES.getBpn())); - PLATO.createContractDefinition(assetId, "def-1", policyId, policyId); - - var destination = httpDataAddress(consumerUrl.toString()); - - var transferProcessId = SOKRATES.requestAsset(PLATO, assetId, createObjectBuilder().build(), destination); - await().atMost(ASYNC_TIMEOUT).untilAsserted(() -> { - var state = SOKRATES.getTransferProcessState(transferProcessId); - assertThat(state).isEqualTo(COMPLETED.name()); - }); - } - - @AfterEach - void teardown() throws IOException { - server.shutdown(); - } - - private JsonObject httpDataAddress(String baseUrl) { - return createObjectBuilder() - .add(TYPE, EDC_NAMESPACE + "DataAddress") - .add(EDC_NAMESPACE + "type", "HttpData") - .add(EDC_NAMESPACE + "properties", createObjectBuilder() - .add(EDC_NAMESPACE + "baseUrl", baseUrl) - .build()) - .build(); - } - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/DimHttpPullTransferIntegrationTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/DimHttpPullTransferIntegrationTest.java new file mode 100644 index 000000000..1b8d48be9 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/DimHttpPullTransferIntegrationTest.java @@ -0,0 +1,101 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.tractusx.edc.lifecycle.DimParticipant; +import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; +import org.eclipse.tractusx.edc.tag.DimIntegrationTest; +import org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.model.HttpResponse; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Map; +import java.util.zip.GZIPOutputStream; + +import static org.eclipse.edc.util.io.Ports.getFreePort; +import static org.eclipse.tractusx.edc.helpers.DimHelper.configureParticipant; +import static org.eclipse.tractusx.edc.lifecycle.Runtimes.dimRuntime; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_NAME; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_NAME; +import static org.mockserver.model.HttpRequest.request; + +@DimIntegrationTest +@Disabled +public class DimHttpPullTransferIntegrationTest extends HttpConsumerPullBaseTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final Integer BDRS_PORT = getFreePort(); + private static final String BDRS_URL = "http://localhost:%s/api".formatted(BDRS_PORT); + protected static final DimParticipant SOKRATES = configureParticipant(SOKRATES_NAME, BDRS_URL); + protected static final DimParticipant PLATO = configureParticipant(PLATO_NAME, BDRS_URL); + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = dimRuntime(PLATO.getName(), PLATO.iatpConfiguration(SOKRATES)); + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = dimRuntime(SOKRATES.getName(), SOKRATES.iatpConfiguration(PLATO)); + private static ClientAndServer bdrsServer; + + @BeforeAll + static void beforeAll() { + bdrsServer = ClientAndServer.startClientAndServer(BDRS_PORT); + bdrsServer.when(request() + .withMethod("GET") + .withPath("/api/bpn-directory")) + .respond(HttpResponse.response() + .withHeader("Content-Encoding", "gzip") + .withBody(createGzipStream()) + .withStatusCode(200)); + + } + + private static byte[] createGzipStream() { + var data = Map.of(SOKRATES.getBpn(), SOKRATES.getDid(), + PLATO.getBpn(), PLATO.getDid()); + + var bas = new ByteArrayOutputStream(); + try (var gzip = new GZIPOutputStream(bas)) { + gzip.write(MAPPER.writeValueAsBytes(data)); + } catch (IOException e) { + throw new RuntimeException(e); + } + return bas.toByteArray(); + } + + @AfterAll + static void afterAll() { + bdrsServer.stop(); + } + + @Override + public TractusxParticipantBase plato() { + return PLATO; + } + + @Override + public TractusxParticipantBase sokrates() { + return SOKRATES; + } +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullWithProxyInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullWithProxyInMemoryTest.java deleted file mode 100644 index 5f3f0d791..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullWithProxyInMemoryTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.transfer; - -import com.nimbusds.jose.util.Base64; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.security.SecureRandom; - -@EndToEndTest -public class HttpConsumerPullWithProxyInMemoryTest extends AbstractHttpConsumerPullWithProxyTest { - - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - - @RegisterExtension - protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); - - @BeforeAll - static void prepare() { - var bytes = new byte[32]; - - new SecureRandom().nextBytes(bytes); - var value = Base64.encode(bytes).toString(); - var vault = SOKRATES_RUNTIME.getContext().getService(Vault.class); - vault.storeSecret("test-alias", value); - vault = PLATO_RUNTIME.getContext().getService(Vault.class); - vault.storeSecret("test-alias", value); - - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullWithProxyPostgresqlTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullWithProxyPostgresqlTest.java deleted file mode 100644 index a34768e88..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullWithProxyPostgresqlTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.transfer; - -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; -import org.eclipse.tractusx.edc.lifecycle.PgParticipantRuntime; -import org.junit.jupiter.api.extension.RegisterExtension; - -@PostgresqlDbIntegrationTest -public class HttpConsumerPullWithProxyPostgresqlTest extends AbstractHttpConsumerPullWithProxyTest { - - - @RegisterExtension - protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - @RegisterExtension - protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpProviderPushInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpProviderPushInMemoryTest.java deleted file mode 100644 index 037e189b7..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpProviderPushInMemoryTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.transfer; - -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.junit.jupiter.api.extension.RegisterExtension; - -@EndToEndTest -public class HttpProviderPushInMemoryTest extends AbstractHttpProviderPushTest { - - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - - @RegisterExtension - protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpProviderPushInPostgresqlTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpProviderPushInPostgresqlTest.java deleted file mode 100644 index 2c964aa5e..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpProviderPushInPostgresqlTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.transfer; - -import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; -import org.eclipse.tractusx.edc.lifecycle.PgParticipantRuntime; -import org.junit.jupiter.api.extension.RegisterExtension; - -@PostgresqlDbIntegrationTest -public class HttpProviderPushInPostgresqlTest extends AbstractHttpProviderPushTest { - - @RegisterExtension - protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.getConfiguration() - ); - @RegisterExtension - protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( - ":edc-tests:runtime:runtime-postgresql", - PLATO.getName(), - PLATO.getBpn(), - PLATO.getConfiguration() - ); -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/IatpHttpConsumerPullWithProxyInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/IatpHttpConsumerPullWithProxyInMemoryTest.java deleted file mode 100644 index 2c6b38a4b..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/IatpHttpConsumerPullWithProxyInMemoryTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.transfer; - -import jakarta.json.JsonObject; -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.iam.did.spi.resolution.DidResolverRegistry; -import org.eclipse.edc.identityhub.spi.ParticipantContextService; -import org.eclipse.edc.identityhub.spi.model.VerifiableCredentialResource; -import org.eclipse.edc.identityhub.spi.model.participant.KeyDescriptor; -import org.eclipse.edc.identityhub.spi.model.participant.ParticipantManifest; -import org.eclipse.edc.identityhub.spi.store.CredentialStore; -import org.eclipse.edc.jsonld.spi.JsonLd; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.did.DidExampleResolver; -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.eclipse.tractusx.edc.lifecycle.tx.iatp.DataspaceIssuer; -import org.eclipse.tractusx.edc.lifecycle.tx.iatp.IatpParticipant; -import org.eclipse.tractusx.edc.lifecycle.tx.iatp.SecureTokenService; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.TX_CREDENTIAL_NAMESPACE; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.frameworkPolicy; - -@EndToEndTest -// temporarily disabled waiting for an upstream fix -@Disabled -public class IatpHttpConsumerPullWithProxyInMemoryTest extends AbstractHttpConsumerPullWithProxyTest { - - protected static final DataspaceIssuer DATASPACE_ISSUER_PARTICIPANT = new DataspaceIssuer(); - protected static final SecureTokenService STS_PARTICIPANT = new SecureTokenService(); - - protected static final IatpParticipant PLATO_IATP = new IatpParticipant(PLATO, STS_PARTICIPANT.stsUri()); - - @RegisterExtension - protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:iatp:runtime-memory-iatp-ih", - PLATO.getName(), - PLATO.getBpn(), - PLATO_IATP.iatpConfiguration(SOKRATES) - ); - - protected static final IatpParticipant SOKRATES_IATP = new IatpParticipant(SOKRATES, STS_PARTICIPANT.stsUri()); - - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:iatp:runtime-memory-iatp-ih", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES_IATP.iatpConfiguration(PLATO) - ); - - @RegisterExtension - protected static final ParticipantRuntime STS_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:iatp:runtime-memory-sts", - STS_PARTICIPANT.getName(), - STS_PARTICIPANT.getBpn(), - STS_PARTICIPANT.stsConfiguration(SOKRATES_IATP, PLATO_IATP) - ); - - @BeforeAll - static void prepare() { - - // create the DIDs cache - var dids = new HashMap(); - dids.put(DATASPACE_ISSUER_PARTICIPANT.didUrl(), DATASPACE_ISSUER_PARTICIPANT.didDocument()); - dids.put(SOKRATES_IATP.didUrl(), SOKRATES_IATP.didDocument()); - dids.put(PLATO_IATP.didUrl(), PLATO_IATP.didDocument()); - - configureParticipant(SOKRATES_IATP, SOKRATES_RUNTIME, dids); - configureParticipant(PLATO_IATP, PLATO_RUNTIME, dids); - - } - - private static void configureParticipant(IatpParticipant participant, ParticipantRuntime runtime, Map dids) { - STS_RUNTIME.getContext().getService(Vault.class).storeSecret(participant.verificationId(), participant.privateKey()); - var participantContextService = runtime.getContext().getService(ParticipantContextService.class); - var vault = runtime.getContext().getService(Vault.class); - var didResolverRegistry = runtime.getContext().getService(DidResolverRegistry.class); - var didResolver = new DidExampleResolver(); - dids.forEach(didResolver::addCached); - didResolverRegistry.register(didResolver); - - var key = KeyDescriptor.Builder.newInstance() - .keyId(participant.verificationId()) - .publicKeyPem(participant.publicKey()) - .build(); - - var participantManifest = ParticipantManifest.Builder.newInstance() - .participantId(participant.getBpn()) - .did(participant.didUrl()) - .key(key) - .build(); - - participantContextService.createParticipantContext(participantManifest); - vault.storeSecret(participant.verificationId(), participant.privateKey()); - - storeCredentials(participant, runtime); - } - - private static void storeCredentials(IatpParticipant participant, ParticipantRuntime runtime) { - var credentialStore = runtime.getContext().getService(CredentialStore.class); - var jsonLd = runtime.getContext().getService(JsonLd.class); - issueCredentials(participant, jsonLd).forEach(credentialStore::create); - } - - private static List issueCredentials(IatpParticipant participant, JsonLd jsonLd) { - - if (participant.getBpn().startsWith("PLATO")) { - return List.of( - DATASPACE_ISSUER_PARTICIPANT.issueMembershipCredential(participant, jsonLd)); - } else { - return List.of( - DATASPACE_ISSUER_PARTICIPANT.issueMembershipCredential(participant, jsonLd), - DATASPACE_ISSUER_PARTICIPANT.issueFrameworkCredential(participant, jsonLd, "PcfCredential")); - } - - } - - @BeforeEach - void setup() throws IOException { - super.setup(); - } - - @Override - protected JsonObject createContractPolicy(String bpn) { - return frameworkPolicy(Map.of(TX_CREDENTIAL_NAMESPACE + "Membership", "active")); - } - -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/SsiHttpConsumerPullWithProxyInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/SsiHttpConsumerPullWithProxyInMemoryTest.java deleted file mode 100644 index ea0990363..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/SsiHttpConsumerPullWithProxyInMemoryTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.transfer; - -import jakarta.json.JsonObject; -import okhttp3.mockwebserver.MockWebServer; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; -import org.eclipse.tractusx.edc.token.KeycloakDispatcher; -import org.eclipse.tractusx.edc.token.MiwDispatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.io.IOException; -import java.util.Map; - -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.TX_NAMESPACE; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.frameworkPolicy; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; - -@EndToEndTest -public class SsiHttpConsumerPullWithProxyInMemoryTest extends AbstractHttpConsumerPullWithProxyTest { - - public static final String SUMMARY_VC_TEMPLATE = "summary-vc.json"; - @RegisterExtension - protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory-ssi", - SOKRATES.getName(), - SOKRATES.getBpn(), - SOKRATES.ssiConfiguration() - ); - @RegisterExtension - protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( - ":edc-tests:runtime:runtime-memory-ssi", - PLATO.getName(), - PLATO.getBpn(), - PLATO.ssiConfiguration() - ); - - private static MockWebServer sokratesOauthServer; - private static MockWebServer platoOauthServer; - private static MockWebServer miwPlatoServer; - private static MockWebServer miwSokratesServer; - - @BeforeAll - static void prepare() throws IOException { - miwSokratesServer = new MockWebServer(); - miwPlatoServer = new MockWebServer(); - sokratesOauthServer = new MockWebServer(); - platoOauthServer = new MockWebServer(); - - var credentialSubjectId = "did:web:example.com"; - - miwSokratesServer.start(SOKRATES.miwEndpoint().getPort()); - miwSokratesServer.setDispatcher(new MiwDispatcher(SOKRATES_BPN, SUMMARY_VC_TEMPLATE, credentialSubjectId, PLATO.getProtocolEndpoint().getUrl().toString())); - - miwPlatoServer.start(PLATO.miwEndpoint().getPort()); - miwPlatoServer.setDispatcher(new MiwDispatcher(PLATO_BPN, SUMMARY_VC_TEMPLATE, credentialSubjectId, SOKRATES.getProtocolEndpoint().getUrl().toString())); - - sokratesOauthServer.start(SOKRATES.authTokenEndpoint().getPort()); - sokratesOauthServer.setDispatcher(new KeycloakDispatcher()); - - platoOauthServer.start(PLATO.authTokenEndpoint().getPort()); - platoOauthServer.setDispatcher(new KeycloakDispatcher()); - } - - @AfterAll - static void unwind() throws IOException { - miwSokratesServer.shutdown(); - miwPlatoServer.shutdown(); - sokratesOauthServer.shutdown(); - platoOauthServer.shutdown(); - } - - @BeforeEach - void setup() throws IOException { - super.setup(); - - } - - @Override - protected JsonObject createAccessPolicy(String bpn) { - return frameworkPolicy(Map.of(TX_NAMESPACE + "Dismantler", "active")); - } -} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/MiwDispatcher.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/MiwDispatcher.java deleted file mode 100644 index 11bea53c9..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/MiwDispatcher.java +++ /dev/null @@ -1,142 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.token; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.crypto.RSASSASigner; -import com.nimbusds.jose.jwk.KeyUse; -import com.nimbusds.jose.jwk.RSAKey; -import com.nimbusds.jose.jwk.gen.RSAKeyGenerator; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.RecordedRequest; -import org.eclipse.edc.spi.types.TypeManager; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.security.PrivateKey; -import java.time.Instant; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; - -import static java.lang.String.format; - -public class MiwDispatcher extends Dispatcher { - - private static final TypeManager MAPPER = new TypeManager(); - - private final String audience; - - private final String credentialSubjectId; - - private final Map summaryVc; - - public MiwDispatcher(String bpn, String vcFile, String credentialSubjectId, String audience) { - this.audience = audience; - this.credentialSubjectId = credentialSubjectId; - var json = format(readVcContent(vcFile), bpn); - summaryVc = MAPPER.readValue(json, new TypeReference<>() { - }); - } - - @NotNull - @Override - public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) throws InterruptedException { - return switch (recordedRequest.getPath().split("\\?")[0]) { - case "/api/credentials" -> credentialResponse(); - case "/api/presentations" -> presentationResponse(); - case "/api/presentations/validation" -> presentationValidationResponse(); - default -> new MockResponse().setResponseCode(404); - }; - } - - private String readVcContent(String vcFile) { - var classloader = Thread.currentThread().getContextClassLoader(); - - try (var jsonStream = classloader.getResourceAsStream(vcFile)) { - Objects.requireNonNull(jsonStream); - return new String(jsonStream.readAllBytes(), StandardCharsets.UTF_8); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private MockResponse credentialResponse() { - return new MockResponse().setBody(MAPPER.writeValueAsString(Map.of("content", List.of(summaryVc)))); - } - - private MockResponse presentationResponse() { - try { - var jwt = createJwt(UUID.randomUUID().toString(), createClaims(Instant.now(), createVerifiablePresentationClaim()), testKey().toPrivateKey()); - return new MockResponse().setBody(MAPPER.writeValueAsString(Map.of("vp", jwt))); - } catch (JOSEException e) { - throw new RuntimeException(e); - } - } - - private Map createVerifiablePresentationClaim() { - var ctx = List.of("https://www.w3.org/2018/credentials/v1"); - var type = List.of("VerifiablePresentation"); - return Map.of( - "@context", ctx, - "type", type, - "verifiableCredential", List.of(summaryVc)); - } - - private MockResponse presentationValidationResponse() { - return new MockResponse().setBody(MAPPER.writeValueAsString(Map.of("valid", true))); - } - - private JWTClaimsSet createClaims(Instant exp, Map presentation) { - return new JWTClaimsSet.Builder() - .issuer(credentialSubjectId) - .claim("vp", presentation) - .audience(audience) - .expirationTime(Date.from(exp)) - .build(); - } - - private String createJwt(String publicKeyId, JWTClaimsSet claimsSet, PrivateKey pk) { - var header = new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(publicKeyId).build(); - try { - SignedJWT jwt = new SignedJWT(header, claimsSet); - jwt.sign(new RSASSASigner(pk)); - return jwt.serialize(); - } catch (JOSEException e) { - throw new AssertionError(e); - } - } - - private RSAKey testKey() throws JOSEException { - return new RSAKeyGenerator(2048) - .keyUse(KeyUse.SIGNATURE) // indicate the intended use of the key - .keyID(UUID.randomUUID().toString()) // give the key a unique ID - .generate(); - } -} diff --git a/spi/ssi-spi/build.gradle.kts b/edc-tests/edc-controlplane/catalog-tests/build.gradle.kts similarity index 73% rename from spi/ssi-spi/build.gradle.kts rename to edc-tests/edc-controlplane/catalog-tests/build.gradle.kts index 67ffc3c4a..d0de250c2 100644 --- a/spi/ssi-spi/build.gradle.kts +++ b/edc-tests/edc-controlplane/catalog-tests/build.gradle.kts @@ -23,12 +23,16 @@ plugins { } dependencies { - implementation(libs.edc.spi.core) - implementation(libs.edc.spi.jwt) - implementation(libs.jakartaJson) + testImplementation(testFixtures(project(":edc-tests:edc-controlplane:fixtures"))) - testFixturesImplementation(libs.jacksonJsonP) - testFixturesImplementation(libs.jackson.datatypeJsr310) - testFixturesImplementation(libs.titaniumJsonLd) - testFixturesImplementation(libs.jackson.datatypeJsr310) + testImplementation(libs.netty.mockserver) + testImplementation(libs.edc.junit) + testImplementation(libs.restAssured) + testImplementation(libs.awaitility) + testImplementation(libs.okhttp.mockwebserver) +} + +// do not publish +edcBuild { + publish.set(false) } diff --git a/edc-tests/edc-controlplane/catalog-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogTest.java b/edc-tests/edc-controlplane/catalog-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogTest.java new file mode 100644 index 000000000..14dba2c55 --- /dev/null +++ b/edc-tests/edc-controlplane/catalog-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogTest.java @@ -0,0 +1,235 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.catalog; + +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.junit.annotations.PostgresqlIntegrationTest; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.tractusx.edc.tests.participant.TransferParticipant; +import org.eclipse.tractusx.edc.tests.runtimes.ParticipantRuntime; +import org.eclipse.tractusx.edc.tests.runtimes.PgParticipantRuntime; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.util.Map; + +import static java.util.stream.IntStream.range; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.connector.controlplane.test.system.utils.PolicyFixtures.noConstraintPolicy; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_NAME; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_NAME; +import static org.eclipse.tractusx.edc.tests.helpers.CatalogHelperFunctions.getDatasetAssetId; +import static org.eclipse.tractusx.edc.tests.helpers.CatalogHelperFunctions.getDatasetPolicies; +import static org.eclipse.tractusx.edc.tests.helpers.PolicyHelperFunctions.BUSINESS_PARTNER_LEGACY_EVALUATION_KEY; +import static org.eclipse.tractusx.edc.tests.helpers.PolicyHelperFunctions.bnpPolicy; +import static org.eclipse.tractusx.edc.tests.helpers.PolicyHelperFunctions.bpnGroupPolicy; +import static org.eclipse.tractusx.edc.tests.helpers.PolicyHelperFunctions.frameworkPolicy; +import static org.eclipse.tractusx.edc.tests.helpers.QueryHelperFunctions.createQuery; +import static org.eclipse.tractusx.edc.tests.runtimes.Runtimes.memoryRuntime; +import static org.eclipse.tractusx.edc.tests.runtimes.Runtimes.pgRuntime; + +public class CatalogTest { + + protected static final TransferParticipant SOKRATES = TransferParticipant.Builder.newInstance() + .name(SOKRATES_NAME) + .id(SOKRATES_BPN) + .build(); + + + protected static final TransferParticipant PLATO = TransferParticipant.Builder.newInstance() + .name(PLATO_NAME) + .id(PLATO_BPN) + .build(); + + + abstract static class Tests { + @Test + @DisplayName("Plato gets catalog from Sokrates. No constraints.") + void requestCatalog_fulfillsPolicy_shouldReturnOffer() { + // arrange + SOKRATES.createAsset("test-asset"); + var ap = SOKRATES.createPolicyDefinition(noConstraintPolicy()); + var cp = SOKRATES.createPolicyDefinition(noConstraintPolicy()); + SOKRATES.createContractDefinition("test-asset", "test-def", ap, cp); + + // act + var catalog = PLATO.getCatalogDatasets(SOKRATES); + + // assert + assertThat(catalog).isNotEmpty() + .hasSize(1) + .allSatisfy(co -> { + assertThat(getDatasetAssetId(co)).isEqualTo("test-asset"); + }); + + } + + @Test + @DisplayName("Verify that Plato receives only the offers he is permitted to (using the legacy BPN validation)") + void requestCatalog_filteredByBpnLegacy_shouldReject() { + var onlyPlatoPolicy = bnpPolicy("BPN1", "BPN2", PLATO.getBpn()); + var onlyDiogenesPolicy = bnpPolicy("ARISTOTELES-BPN"); + + var onlyPlatoId = SOKRATES.createPolicyDefinition(onlyPlatoPolicy); + var onlyDiogenesId = SOKRATES.createPolicyDefinition(onlyDiogenesPolicy); + var noConstraintPolicyId = SOKRATES.createPolicyDefinition(noConstraintPolicy()); + + SOKRATES.createAsset("test-asset1"); + SOKRATES.createAsset("test-asset2"); + SOKRATES.createAsset("test-asset3"); + + SOKRATES.createContractDefinition("test-asset1", "def1", noConstraintPolicyId, noConstraintPolicyId); + SOKRATES.createContractDefinition("test-asset2", "def2", onlyPlatoId, noConstraintPolicyId); + SOKRATES.createContractDefinition("test-asset3", "def3", onlyDiogenesId, noConstraintPolicyId); + + + // act + var catalog = PLATO.getCatalogDatasets(SOKRATES); + assertThat(catalog).hasSize(2); + } + + + @Test + @DisplayName("Verify that Plato receives only the offers he is permitted to (using the legacy BPN validation)") + void requestCatalog_filteredByBpnLegacy_WithNamespace_shouldReject() { + + var onlyPlatoPolicy = bnpPolicy("BPN1", "BPN2", PLATO.getBpn()); + var onlyDiogenesPolicy = frameworkPolicy(Map.of(BUSINESS_PARTNER_LEGACY_EVALUATION_KEY, "ARISTOTELES-BPN")); + + var onlyPlatoId = SOKRATES.createPolicyDefinition(onlyPlatoPolicy); + var onlyDiogenesId = SOKRATES.createPolicyDefinition(onlyDiogenesPolicy); + var noConstraintPolicyId = SOKRATES.createPolicyDefinition(noConstraintPolicy()); + + SOKRATES.createAsset("test-asset1"); + SOKRATES.createAsset("test-asset2"); + SOKRATES.createAsset("test-asset3"); + + SOKRATES.createContractDefinition("test-asset1", "def1", noConstraintPolicyId, noConstraintPolicyId); + SOKRATES.createContractDefinition("test-asset2", "def2", onlyPlatoId, noConstraintPolicyId); + SOKRATES.createContractDefinition("test-asset3", "def3", onlyDiogenesId, noConstraintPolicyId); + + + // act + var catalog = PLATO.getCatalogDatasets(SOKRATES); + assertThat(catalog).hasSize(2); + } + + @Test + @DisplayName("Verify that Plato receives only the offers he is permitted to (using the new BPN validation)") + void requestCatalog_filteredByBpn_shouldReject() { + + var mustBeGreekPhilosopher = bpnGroupPolicy(Operator.IS_ANY_OF, "greek_customer", "philosopher"); + var mustBeGreekMathematician = bpnGroupPolicy(Operator.IS_ALL_OF, "greek_customer", "mathematician"); + + + SOKRATES.storeBusinessPartner(PLATO.getBpn(), "greek_customer", "philosopher"); + var philosopherId = SOKRATES.createPolicyDefinition(mustBeGreekPhilosopher); + var mathId = SOKRATES.createPolicyDefinition(mustBeGreekMathematician); + var noConstraintPolicyId = SOKRATES.createPolicyDefinition(noConstraintPolicy()); + + SOKRATES.createAsset("test-asset1"); + SOKRATES.createAsset("test-asset2"); + SOKRATES.createAsset("test-asset3"); + + SOKRATES.createContractDefinition("test-asset1", "def1", noConstraintPolicyId, noConstraintPolicyId); + SOKRATES.createContractDefinition("test-asset2", "def2", philosopherId, noConstraintPolicyId); + SOKRATES.createContractDefinition("test-asset3", "def3", mathId, noConstraintPolicyId); + + + // act + var catalog = PLATO.getCatalogDatasets(SOKRATES); + assertThat(catalog).hasSize(2); + } + + @Test + @DisplayName("Multiple ContractDefinitions exist for one Asset") + void requestCatalog_multipleOffersForAsset() { + SOKRATES.storeBusinessPartner(PLATO.getBpn(), "test-group"); + SOKRATES.createAsset("asset-1"); + var noConstraintId = SOKRATES.createPolicyDefinition(noConstraintPolicy()); + var groupConstraintId = SOKRATES.createPolicyDefinition(bpnGroupPolicy(Operator.IS_ANY_OF, "test-group")); + + SOKRATES.createContractDefinition("asset-1", "def1", noConstraintId, noConstraintId); + SOKRATES.createContractDefinition("asset-1", "def2", groupConstraintId, noConstraintId); + + var catalog = PLATO.getCatalogDatasets(SOKRATES); + assertThat(catalog).hasSize(1) + .allSatisfy(cd -> { + assertThat(getDatasetAssetId(cd)).isEqualTo("asset-1"); + assertThat(getDatasetPolicies(cd)).hasSize(2); + }); + } + + @Test + @DisplayName("Catalog with 1000 offers") + void requestCatalog_of1000Assets_shouldContainAll() { + var policy = bpnGroupPolicy(Operator.IS_NONE_OF, "test-group1", "test-group2"); + var policyId = SOKRATES.createPolicyDefinition(policy); + var noConstraintId = SOKRATES.createPolicyDefinition(noConstraintPolicy()); + SOKRATES.storeBusinessPartner(PLATO.getBpn(), "test-group-3"); + + range(0, 1000) + .forEach(i -> { + var assetId = "asset-" + i; + SOKRATES.createAsset(assetId); + SOKRATES.createContractDefinition(assetId, "def-" + i, policyId, noConstraintId); + }); + + // request all at once + var dataset = PLATO.getCatalogDatasets(SOKRATES, createQuery(1000, 0)); + assertThat(dataset).hasSize(1000); + + // request in chunks + var o2 = PLATO.getCatalogDatasets(SOKRATES, createQuery(500, 0)); + var o3 = PLATO.getCatalogDatasets(SOKRATES, createQuery(500, 500)); + assertThat(o2).doesNotContainAnyElementsOf(o3); + + } + } + + @Nested + @EndToEndTest + class InMemory extends Tests { + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = memoryRuntime(SOKRATES.getName(), SOKRATES.getBpn(), SOKRATES.getConfiguration()); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = memoryRuntime(PLATO.getName(), PLATO.getBpn(), PLATO.getConfiguration()); + + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests { + + @RegisterExtension + protected static final PgParticipantRuntime SOKRATES_RUNTIME = pgRuntime(SOKRATES.getName(), SOKRATES.getBpn(), SOKRATES.getConfiguration()); + + @RegisterExtension + protected static final PgParticipantRuntime PLATO_RUNTIME = pgRuntime(PLATO.getName(), PLATO.getBpn(), PLATO.getConfiguration()); + + } + +} diff --git a/edc-tests/edc-controlplane/edr-api-tests/build.gradle.kts b/edc-tests/edc-controlplane/edr-api-tests/build.gradle.kts new file mode 100644 index 000000000..d0de250c2 --- /dev/null +++ b/edc-tests/edc-controlplane/edr-api-tests/build.gradle.kts @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 { + `java-library` + `java-test-fixtures` +} + +dependencies { + testImplementation(testFixtures(project(":edc-tests:edc-controlplane:fixtures"))) + + testImplementation(libs.netty.mockserver) + testImplementation(libs.edc.junit) + testImplementation(libs.restAssured) + testImplementation(libs.awaitility) + testImplementation(libs.okhttp.mockwebserver) +} + +// do not publish +edcBuild { + publish.set(false) +} diff --git a/edc-tests/edc-controlplane/edr-api-tests/src/test/java/org/eclipse/tractusx/edc/tests/edrv2/EdrCacheApiEndToEndTest.java b/edc-tests/edc-controlplane/edr-api-tests/src/test/java/org/eclipse/tractusx/edc/tests/edrv2/EdrCacheApiEndToEndTest.java new file mode 100644 index 000000000..938a0dea4 --- /dev/null +++ b/edc-tests/edc-controlplane/edr-api-tests/src/test/java/org/eclipse/tractusx/edc/tests/edrv2/EdrCacheApiEndToEndTest.java @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.tests.edrv2; + + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import jakarta.json.JsonObject; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; +import org.eclipse.tractusx.edc.tests.participant.TransferParticipant; +import org.eclipse.tractusx.edc.tests.runtimes.ParticipantRuntime; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockserver.client.MockServerClient; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.verify.VerificationTimes; + +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.util.io.Ports.getFreePort; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_EXPIRES_IN; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_AUDIENCE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_ENDPOINT; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.EDR_PROPERTY_REFRESH_TOKEN; +import static org.mockserver.integration.ClientAndServer.startClientAndServer; +import static org.mockserver.matchers.Times.exactly; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; +import static org.mockserver.model.StringBody.exact; + +/** + * This End-To-End test spins up a consumer control plane and verifies that the EDR Cache API + * performs as expected. + * The provider data plane is mocked with a {@link ClientAndServer}. + */ +@EndToEndTest +public class EdrCacheApiEndToEndTest { + protected static final TransferParticipant SOKRATES = TransferParticipant.Builder.newInstance() + .name("sokrates") + .id("BPN00001") + .build(); + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + SOKRATES.getName(), + SOKRATES.getId(), + with(SOKRATES.getConfiguration(), Map.of("edc.iam.issuer.id", "did:web:sokrates"))); + private final ObjectMapper mapper = new ObjectMapper(); + private String refreshEndpoint; + private String refreshAudience; + private ClientAndServer mockedRefreshApi; + private ECKey providerSigningKey; + + protected static Map with(Map original, Map additional) { + var newMap = new HashMap<>(original); + newMap.putAll(additional); + return newMap; + } + + @BeforeEach + void setup() throws JOSEException { + providerSigningKey = new ECKeyGenerator(Curve.P_256).keyID("did:web:provider#key-1").generate(); + var port = getFreePort(); + refreshEndpoint = "http://localhost:%s/refresh".formatted(port); + refreshAudience = "did:web:sokrates"; + mockedRefreshApi = startClientAndServer(port); + } + + @AfterEach + void teardown() { + mockedRefreshApi.stop(); + } + + @DisplayName("Verify HTTP 200 response and body when refreshing succeeds") + @Test + void getEdrWithRefresh_success() { + + try (var client = new MockServerClient("localhost", mockedRefreshApi.getPort())) { + // mock the provider dataplane's refresh endpoint + client.when(request() + .withMethod("POST") + .withPath("/refresh/token") + .withBody(exact("")), + exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody(tokenResponseBody()) + ); + + storeEdr("test-id", true); + var edr = SOKRATES.edrs().getEdrWithRefresh("test-id", true) + .statusCode(200) + .extract().body().as(JsonObject.class); + assertThat(edr).isNotNull(); + + // assert the correct endpoint was called + client.verify( + request() + .withQueryStringParameter("grant_type", "refresh_token") + .withMethod("POST") + .withPath("/refresh/token"), + VerificationTimes.exactly(1)); + + } + } + + @DisplayName("Verify the refresh endpoint is not called when token not yet expired") + @Test + void getEdrWithRefresh_notExpired_shouldNotCallEndpoint() { + + try (var client = new MockServerClient("localhost", mockedRefreshApi.getPort())) { + // mock the provider dataplane's refresh endpoint + + storeEdr("test-id", false); + var edr = SOKRATES.edrs().getEdrWithRefresh("test-id", true) + .statusCode(200) + .extract().body().as(JsonObject.class); + assertThat(edr).isNotNull(); + + // assert the correct endpoint was called + client.verify( + request() + .withQueryStringParameter("grant_type", "refresh_token") + .withMethod("POST") + .withPath("/refresh/token"), + VerificationTimes.never()); + } + } + + @DisplayName("Verify the refresh endpoint is not called when auto_refresh=false") + @Test + void getEdrWithRefresh_whenNotAutorefresh_shouldNotCallEndpoint() { + + try (var client = new MockServerClient("localhost", mockedRefreshApi.getPort())) { + // mock the provider dataplane's refresh endpoint + + storeEdr("test-id", true); + var edr = SOKRATES.edrs() + .getEdrWithRefresh("test-id", false) + .statusCode(200) + .extract().body().as(JsonObject.class); + assertThat(edr).isNotNull(); + + // assert the correct endpoint was called + client.verify( + request() + .withQueryStringParameter("grant_type", "refresh_token") + .withMethod("POST") + .withPath("/refresh/token"), + VerificationTimes.never()); + } + } + + @DisplayName("Verify HTTP 403 response when refreshing the token is not allowed") + @Test + void getEdrWithRefresh_unauthorized() { + + try (var client = new MockServerClient("localhost", mockedRefreshApi.getPort())) { + // mock the provider dataplane's refresh endpoint + client.when(request() + .withMethod("POST") + .withPath("/refresh/token") + .withBody(exact("")), + exactly(1)) + .respond(response() + .withStatusCode(401) + .withBody("unauthorized") + ); + + storeEdr("test-id", true); + SOKRATES.edrs().getEdrWithRefresh("test-id", true) + .statusCode(403); + + // assert the correct endpoint was called + client.verify( + request() + .withQueryStringParameter("grant_type", "refresh_token") + .withMethod("POST") + .withPath("/refresh/token"), + VerificationTimes.exactly(1)); + } + } + + @Test + void refreshEdr() { + try (var client = new MockServerClient("localhost", mockedRefreshApi.getPort())) { + // mock the provider dataplane's refresh endpoint + client.when(request() + .withMethod("POST") + .withPath("/refresh/token") + .withBody(exact("")), + exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody(tokenResponseBody()) + ); + + storeEdr("test-id", true); + var edr = SOKRATES.edrs().refreshEdr("test-id") + .statusCode(200) + .extract().body().as(JsonObject.class); + assertThat(edr).isNotNull(); + + // assert the correct endpoint was called + client.verify( + request() + .withQueryStringParameter("grant_type", "refresh_token") + .withMethod("POST") + .withPath("/refresh/token"), + VerificationTimes.exactly(1)); + + } + } + + @Test + void refreshEdr_whenNotFound() { + var edr = SOKRATES.edrs().refreshEdr("does-not-exist") + .statusCode(404); + } + + @Test + void refreshEdr_whenNotAuthorized() { + try (var client = new MockServerClient("localhost", mockedRefreshApi.getPort())) { + // mock the provider dataplane's refresh endpoint + client.when(request() + .withMethod("POST") + .withPath("/refresh/token") + .withBody(exact("")), + exactly(1)) + .respond(response() + .withStatusCode(401) + .withBody("unauthorized") + ); + + storeEdr("test-id", true); + SOKRATES.edrs().refreshEdr("test-id") + .statusCode(403); + + // assert the correct endpoint was called + client.verify( + request() + .withQueryStringParameter("grant_type", "refresh_token") + .withMethod("POST") + .withPath("/refresh/token"), + VerificationTimes.exactly(1)); + } + } + + private String tokenResponseBody() { + var claims = new JWTClaimsSet.Builder().claim("iss", "did:web:provider").build(); + + var accessToken = createJwt(providerSigningKey, claims); + var refreshToken = createJwt(providerSigningKey, new JWTClaimsSet.Builder().build()); + var response = new TokenResponse(accessToken, refreshToken, 300L, "bearer"); + try { + return mapper.writeValueAsString(response); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + private void storeEdr(String transferProcessId, boolean isExpired) { + var claims = new JWTClaimsSet.Builder().claim("iss", "did:web:provider").build(); + var store = SOKRATES_RUNTIME.getService(EndpointDataReferenceStore.class); + var edr = DataAddress.Builder.newInstance() + .type("test-type") + .property(EDC_NAMESPACE + "authorization", createJwt(providerSigningKey, claims)) + .property(EDC_NAMESPACE + "authType", "bearer") + .property(EDR_PROPERTY_REFRESH_TOKEN, createJwt(providerSigningKey, new JWTClaimsSet.Builder().build())) + .property(EDR_PROPERTY_EXPIRES_IN, "300") + .property(EDR_PROPERTY_REFRESH_ENDPOINT, refreshEndpoint) + .property(EDR_PROPERTY_REFRESH_AUDIENCE, refreshAudience) + .build(); + var entry = EndpointDataReferenceEntry.Builder.newInstance() + .clock(isExpired ? // defaults to an expired token + Clock.fixed(Instant.now().minusSeconds(3600), ZoneId.systemDefault()) : + Clock.systemUTC()) + .agreementId("test-agreement") + .assetId("test-asset") + .transferProcessId(transferProcessId) + .providerId("test-provider") + .contractNegotiationId("test-negotiation") + .build(); + store.save(entry, edr).orElseThrow(f -> new AssertionError(f.getFailureDetail())); + } + + + private String createJwt(ECKey signerKey, JWTClaimsSet claims) { + var header = new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(signerKey.getKeyID()).build(); + var jwt = new SignedJWT(header, claims); + try { + jwt.sign(new ECDSASigner(signerKey)); + return jwt.serialize(); + } catch (JOSEException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/edc-tests/edc-controlplane/edr-api-tests/src/test/java/org/eclipse/tractusx/edc/tests/edrv2/NegotiateEdrTest.java b/edc-tests/edc-controlplane/edr-api-tests/src/test/java/org/eclipse/tractusx/edc/tests/edrv2/NegotiateEdrTest.java new file mode 100644 index 000000000..fa91ed005 --- /dev/null +++ b/edc-tests/edc-controlplane/edr-api-tests/src/test/java/org/eclipse/tractusx/edc/tests/edrv2/NegotiateEdrTest.java @@ -0,0 +1,204 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.edrv2; + +import jakarta.json.Json; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationAgreed; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationFinalized; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationInitiated; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationRequested; +import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationVerified; +import org.eclipse.edc.connector.controlplane.transfer.spi.event.TransferProcessInitiated; +import org.eclipse.edc.connector.controlplane.transfer.spi.event.TransferProcessProvisioned; +import org.eclipse.edc.connector.controlplane.transfer.spi.event.TransferProcessRequested; +import org.eclipse.edc.connector.controlplane.transfer.spi.event.TransferProcessStarted; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.junit.annotations.PostgresqlIntegrationTest; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.tractusx.edc.tests.helpers.EdrNegotiationHelperFunctions; +import org.eclipse.tractusx.edc.tests.helpers.PolicyHelperFunctions; +import org.eclipse.tractusx.edc.tests.participant.TransferParticipant; +import org.eclipse.tractusx.edc.tests.runtimes.ParticipantRuntime; +import org.eclipse.tractusx.edc.tests.runtimes.PgParticipantRuntime; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_NAME; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_NAME; +import static org.eclipse.tractusx.edc.tests.helpers.EdrNegotiationHelperFunctions.createEvent; +import static org.eclipse.tractusx.edc.tests.helpers.Functions.waitForEvent; +import static org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase.ASYNC_POLL_INTERVAL; +import static org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase.ASYNC_TIMEOUT; +import static org.eclipse.tractusx.edc.tests.runtimes.Runtimes.memoryRuntime; +import static org.eclipse.tractusx.edc.tests.runtimes.Runtimes.pgRuntime; + +public class NegotiateEdrTest { + + protected static final TransferParticipant SOKRATES = TransferParticipant.Builder.newInstance() + .name(SOKRATES_NAME) + .id(SOKRATES_BPN) + .build(); + + protected static final TransferParticipant PLATO = TransferParticipant.Builder.newInstance() + .name(PLATO_NAME) + .id(PLATO_BPN) + .build(); + + + abstract static class Tests { + + MockWebServer server; + + @BeforeEach + void setup() { + server = new MockWebServer(); + } + + @Test + @DisplayName("Verify that the callbacks are invoked when negotiating an EDR") + void negotiateEdr_shouldInvokeCallbacks() throws IOException { + + var expectedEvents = List.of( + createEvent(ContractNegotiationInitiated.class), + createEvent(ContractNegotiationRequested.class), + createEvent(ContractNegotiationAgreed.class), + createEvent(ContractNegotiationFinalized.class), + createEvent(ContractNegotiationVerified.class), + createEvent(TransferProcessInitiated.class), + createEvent(TransferProcessProvisioned.class), + createEvent(TransferProcessRequested.class), + createEvent(TransferProcessStarted.class)); + + var assetId = "api-asset-1"; + var url = server.url("/mock/api"); + server.start(); + + var authCodeHeaderName = "test-authkey"; + var authCode = "test-authcode"; + Map dataAddress = Map.of( + "name", "transfer-test", + "baseUrl", url.toString(), + "type", "HttpData", + "contentType", "application/json", + "authKey", authCodeHeaderName, + "authCode", authCode + ); + + PLATO.createAsset(assetId, Map.of(), dataAddress); + + PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); + var accessPolicy = PLATO.createPolicyDefinition(PolicyHelperFunctions.bpnGroupPolicy(Operator.IS_NONE_OF, "forbidden-policy")); + var contractPolicy = PLATO.createPolicyDefinition(PolicyHelperFunctions.bpnGroupPolicy(Operator.IS_ALL_OF, "test-group1", "test-group2")); + PLATO.createContractDefinition(assetId, "def-1", accessPolicy, contractPolicy); + + + expectedEvents.forEach(event -> server.enqueue(new MockResponse())); + + var callbacks = Json.createArrayBuilder() + .add(EdrNegotiationHelperFunctions.createCallback(url.toString(), true, Set.of("contract.negotiation", "transfer.process"))) + .build(); + + var contractNegotiationId = SOKRATES.edrs().negotiateEdr(PLATO, assetId, callbacks); + + var events = expectedEvents.stream() + .map(receivedEvent -> waitForEvent(server)) + .collect(Collectors.toList()); + + + await().pollInterval(ASYNC_POLL_INTERVAL) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var edrCaches = SOKRATES.edrs().getEdrEntriesByAssetId(assetId); + assertThat(edrCaches).hasSize(1); + }); + + assertThat(expectedEvents).usingRecursiveFieldByFieldElementComparator().containsAll(events); + + var edrCaches = SOKRATES.edrs().getEdrEntriesByAssetId(assetId); + + assertThat(edrCaches).hasSize(1); + + assertThat(SOKRATES.edrs().getEdrEntriesByContractNegotiationId(contractNegotiationId)).hasSize(1); + + assertThat(edrCaches).hasSize(1); + + var transferProcessId = edrCaches.get(0).asJsonObject().getString("transferProcessId"); + var cnId = edrCaches.get(0).asJsonObject().getString("contractNegotiationId"); + var agreementId = edrCaches.get(0).asJsonObject().getString("agreementId"); + + assertThat(cnId).isEqualTo(contractNegotiationId); + assertThat(SOKRATES.edrs().getEdrEntriesByAgreementId(agreementId)).hasSize(1); + + + var edr = SOKRATES.edrs().getEdr(transferProcessId); + + assertThat(edr.getJsonString("type").getString()).isEqualTo("https://w3id.org/idsa/v4.1/HTTP"); + assertThat(edr.getJsonString("endpoint").getString()).isNotNull(); + assertThat(edr.getJsonString("endpointType").getString()).isEqualTo(edr.getJsonString("type").getString()); + assertThat(edr.getJsonString("authorization").getString()).isNotNull(); + + } + + @AfterEach + void teardown() throws IOException { + server.shutdown(); + } + } + + @Nested + @EndToEndTest + class InMemory extends Tests { + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = memoryRuntime(SOKRATES.getName(), SOKRATES.getBpn(), SOKRATES.getConfiguration()); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = memoryRuntime(PLATO.getName(), PLATO.getBpn(), PLATO.getConfiguration()); + + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests { + + @RegisterExtension + protected static final PgParticipantRuntime SOKRATES_RUNTIME = pgRuntime(SOKRATES.getName(), SOKRATES.getBpn(), SOKRATES.getConfiguration()); + + @RegisterExtension + protected static final PgParticipantRuntime PLATO_RUNTIME = pgRuntime(PLATO.getName(), PLATO.getBpn(), PLATO.getConfiguration()); + + } +} diff --git a/edc-tests/edc-controlplane/fixtures/build.gradle.kts b/edc-tests/edc-controlplane/fixtures/build.gradle.kts new file mode 100644 index 000000000..2297b33c6 --- /dev/null +++ b/edc-tests/edc-controlplane/fixtures/build.gradle.kts @@ -0,0 +1,65 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 { + `java-library` + `java-test-fixtures` +} + +dependencies { + // api modules that the test classes may need + testFixturesApi(project(":spi:edr-spi")) + testFixturesApi(project(":edc-extensions:edr:edr-api-v2")) + testFixturesApi(project(":spi:core-spi")) + testFixturesApi(project(":spi:tokenrefresh-spi")) + testFixturesApi(project(":spi:bdrs-client-spi")) + + testFixturesApi(libs.edc.spi.core) + testFixturesApi(libs.edc.spi.policy) + testFixturesApi(libs.edc.spi.contract) + testFixturesApi(testFixtures(libs.edc.api.management.test.fixtures)) + testFixturesApi(libs.edc.spi.edrstore) + testFixturesApi(libs.edc.lib.cryptocommon) + testFixturesApi(libs.edc.lib.boot) + + // api modules for some test utils + testFixturesApi(libs.netty.mockserver) + testFixturesApi(libs.edc.junit) + testFixturesApi(libs.restAssured) + testFixturesApi(libs.awaitility) + + testFixturesImplementation(libs.edc.identity.trust.sts.embedded) + testFixturesImplementation(libs.edc.core.token) + testFixturesImplementation(libs.edc.spi.identity.did) + testFixturesImplementation(testFixtures(libs.edc.sql.core)) + testFixturesImplementation(libs.okhttp.mockwebserver) + testFixturesImplementation(libs.testcontainers.postgres) + + testCompileOnly(project(":edc-tests:runtime:runtime-memory")) + + testFixturesImplementation(libs.assertj) + testFixturesImplementation(libs.junit.jupiter.api) + testFixturesImplementation(project(":edc-extensions:bpn-validation:bpn-validation-spi")) + +} + +// do not publish +edcBuild { + publish.set(false) +} diff --git a/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/IdentityParticipant.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/IdentityParticipant.java new file mode 100644 index 000000000..879e690f6 --- /dev/null +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/IdentityParticipant.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.tests; + +import com.nimbusds.jose.jwk.JWK; +import org.eclipse.edc.connector.controlplane.test.system.utils.Participant; +import org.eclipse.edc.security.token.jwt.CryptoConverter; + +import java.security.KeyPair; + +import static org.eclipse.tractusx.edc.tests.helpers.Functions.generateKeyPair; + +public abstract class IdentityParticipant extends Participant { + private final KeyPair keyPair; + private JWK keyPairJwk; + + public IdentityParticipant() { + keyPair = generateKeyPair(); + } + + public KeyPair getKeyPair() { + return keyPair; + } + + public JWK getKeyPairAsJwk() { + if (keyPairJwk == null) { + var jwk = CryptoConverter.createJwk(keyPair).toJSONObject(); + jwk.put("kid", getFullKeyId()); + keyPairJwk = CryptoConverter.create(jwk); + } + return keyPairJwk; + } + + public String getPrivateKeyAsString() { + return CryptoConverter.createJwk(keyPair).toJSONString(); + } + + public String getPublicKeyAsString() { + return CryptoConverter.createJwk(keyPair).toJSONString(); + } + + public String getPrivateKeyAlias() { + return "private." + getFullKeyId(); + } + + public abstract String getFullKeyId(); + + public String getKeyId() { + return "key-1"; + } +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/MockBpnIdentityService.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/MockBpnIdentityService.java similarity index 98% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/MockBpnIdentityService.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/MockBpnIdentityService.java index caf733a0b..c95d7297a 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/MockBpnIdentityService.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/MockBpnIdentityService.java @@ -17,7 +17,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.token; +package org.eclipse.tractusx.edc.tests; import org.eclipse.edc.spi.iam.ClaimToken; import org.eclipse.edc.spi.iam.IdentityService; diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/TestCommon.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/ParticipantAwareTest.java similarity index 77% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/TestCommon.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/ParticipantAwareTest.java index 486044a9a..26cddc343 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/TestCommon.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/ParticipantAwareTest.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -19,11 +19,11 @@ package org.eclipse.tractusx.edc.tests; -import java.time.Duration; +import org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase; -import static java.time.Duration.ofSeconds; +public interface ParticipantAwareTest { -public interface TestCommon { - Duration ASYNC_TIMEOUT = ofSeconds(60); - Duration ASYNC_POLL_INTERVAL = ofSeconds(1); + TractusxParticipantBase plato(); + + TractusxParticipantBase sokrates(); } diff --git a/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/ParticipantDataApi.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/ParticipantDataApi.java new file mode 100644 index 000000000..68b6049f7 --- /dev/null +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/ParticipantDataApi.java @@ -0,0 +1,57 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests; + +import jakarta.json.JsonObject; +import org.eclipse.edc.spi.types.domain.DataAddress; + +import java.util.Map; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * E2E test helper for fetching the data + */ +public class ParticipantDataApi { + + /** + * Pull the data with an {@link DataAddress} + * + * @param edr The edr + * @param queryParams additional params + * @return the data + */ + public String pullData(JsonObject edr, Map queryParams) { + + var endpoint = edr.getString("endpoint"); + var token = edr.getString("authorization"); + var response = given() + .baseUri(endpoint) + .header("Authorization", token) + .queryParams(queryParams) + .when() + .get(); + assertThat(response.statusCode()).isBetween(200, 300); + return response.body().asString(); + } + +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/ParticipantEdrApi.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/ParticipantEdrApi.java similarity index 57% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/ParticipantEdrApi.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/ParticipantEdrApi.java index 18f55c0d3..a8aec8b06 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/ParticipantEdrApi.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/ParticipantEdrApi.java @@ -17,37 +17,39 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.lifecycle.tx; +package org.eclipse.tractusx.edc.tests; import io.restassured.response.ValidatableResponse; import io.restassured.specification.RequestSpecification; +import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonObject; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.edc.test.system.utils.Participant; +import org.eclipse.edc.connector.controlplane.test.system.utils.Participant; +import org.eclipse.tractusx.edc.tests.participant.TransferParticipant; -import java.net.URI; -import java.util.concurrent.atomic.AtomicReference; - -import static io.restassured.RestAssured.given; import static io.restassured.http.ContentType.JSON; +import static jakarta.json.Json.createObjectBuilder; import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; -import static org.eclipse.tractusx.edc.helpers.CatalogHelperFunctions.getDatasetContractId; -import static org.eclipse.tractusx.edc.helpers.CatalogHelperFunctions.getDatasetFirstPolicy; -import static org.eclipse.tractusx.edc.helpers.EdrNegotiationHelperFunctions.createEdrNegotiationRequest; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VOCAB; +import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_ASSIGNER_ATTRIBUTE; +import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_TARGET_ATTRIBUTE; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.tractusx.edc.tests.helpers.CatalogHelperFunctions.getDatasetContractId; +import static org.eclipse.tractusx.edc.tests.helpers.CatalogHelperFunctions.getDatasetFirstPolicy; +import static org.eclipse.tractusx.edc.tests.helpers.EdrNegotiationHelperFunctions.createEdrNegotiationRequest; /** * E2E test helper for the EDR APIs */ public class ParticipantEdrApi { - private final TxParticipant participant; - private final URI edrBackend; + private final Participant participant; - public ParticipantEdrApi(TxParticipant participant, Participant.Endpoint managementEndpoint, URI edrBackend) { + public ParticipantEdrApi(Participant participant) { this.participant = participant; - this.edrBackend = edrBackend; } /** @@ -65,25 +67,13 @@ public JsonObject getEdr(String transferProcessId) { } /** - * Get the cached EDR for a transfer process cached in a backend + * Get the cached EDR for a transfer process as {@link ValidatableResponse} * * @param transferProcessId The transfer process id - * @return The EDR + * @return The {@link ValidatableResponse} */ - public EndpointDataReference getDataReferenceFromBackend(String transferProcessId) { - var dataReference = new AtomicReference(); - - var result = given() - .when() - .get(edrBackend + "/{id}", transferProcessId) - .then() - .statusCode(200) - .extract() - .body() - .as(EndpointDataReference.class); - dataReference.set(result); - - return dataReference.get(); + public ValidatableResponse getEdrRequest(String transferProcessId) { + return getEdrWithRefresh(transferProcessId, false); } /** @@ -92,11 +82,24 @@ public EndpointDataReference getDataReferenceFromBackend(String transferProcessI * @param transferProcessId The transfer process id * @return The {@link ValidatableResponse} */ - public ValidatableResponse getEdrRequest(String transferProcessId) { + public ValidatableResponse getEdrWithRefresh(String transferProcessId, boolean autoRefresh) { return baseEdrRequest() .when() - .get("/edrs/{id}", transferProcessId) - .then(); + .get("/v2/edrs/{id}/dataaddress?auto_refresh={auto_refresh}", transferProcessId, autoRefresh) + .then() + .log().ifError(); + + } + + /** + * Triggers the explicit renewal of an EDR identified by {@code transferProcessId} + */ + public ValidatableResponse refreshEdr(String transferProcessId) { + return baseEdrRequest() + .when() + .post("/v2/edrs/{id}/refresh", transferProcessId) + .then() + .log().ifError(); } /** @@ -107,11 +110,14 @@ public ValidatableResponse getEdrRequest(String transferProcessId) { * @param callbacks The callbacks * @return The contract negotiation id */ - public String negotiateEdr(TxParticipant other, String assetId, JsonArray callbacks) { + public String negotiateEdr(TransferParticipant other, String assetId, JsonArray callbacks) { var dataset = participant.getDatasetForAsset(other, assetId); assertThat(dataset).withFailMessage("Catalog received from " + other.getName() + " was empty!").isNotEmpty(); - var policy = getDatasetFirstPolicy(dataset); + var policy = createObjectBuilder(getDatasetFirstPolicy(dataset)) + .add(ODRL_TARGET_ATTRIBUTE, createObjectBuilder().add(ID, dataset.get(ID))) + .add(ODRL_ASSIGNER_ATTRIBUTE, createObjectBuilder().add(ID, other.getBpn())) + .build(); var contractId = getDatasetContractId(dataset); var requestBody = createEdrNegotiationRequest(other.getProtocolEndpoint().getUrl().toString(), other.getBpn(), contractId.toString(), contractId.assetIdPart(), policy, callbacks); @@ -120,7 +126,7 @@ public String negotiateEdr(TxParticipant other, String assetId, JsonArray callba var response = baseEdrRequest() .when() .body(requestBody) - .post("/edrs") + .post("/v2/edrs") .then(); var body = response.extract().body().asString(); @@ -136,9 +142,11 @@ public String negotiateEdr(TxParticipant other, String assetId, JsonArray callba * @return The EDRs */ public JsonArray getEdrEntriesByContractNegotiationId(String contractNegotiationId) { + var query = createQuery("contractNegotiationId", "=", contractNegotiationId); return baseEdrRequest() .when() - .get("/edrs?contractNegotiationId={contractNegotiationId}", contractNegotiationId) + .body(query) + .post("/v2/edrs/request") .then() .statusCode(200) .extract() @@ -153,9 +161,11 @@ public JsonArray getEdrEntriesByContractNegotiationId(String contractNegotiation * @return The EDRs */ public JsonArray getEdrEntriesByAgreementId(String agreementId) { + var query = createQuery("agreementId", "=", agreementId); return baseEdrRequest() .when() - .get("/edrs?agreementId={agreementId}", agreementId) + .body(query) + .post("/v2/edrs/request") .then() .statusCode(200) .extract() @@ -170,9 +180,11 @@ public JsonArray getEdrEntriesByAgreementId(String agreementId) { * @return The EDRs */ public JsonArray getEdrEntriesByAssetId(String assetId) { + var query = createQuery("assetId", "=", assetId); return baseEdrRequest() .when() - .get("/edrs?assetId={assetId}", assetId) + .body(query) + .post("/v2/edrs/request") .then() .statusCode(200) .extract() @@ -180,6 +192,22 @@ public JsonArray getEdrEntriesByAssetId(String assetId) { .as(JsonArray.class); } + /** + * Creates a query spec as JSON object that can be passed into the new EDR-V2 API (/request). Not yet used + */ + private String createQuery(String leftOp, String op, String rightOp) { + return Json.createObjectBuilder() + .add(CONTEXT, Json.createObjectBuilder().add(VOCAB, EDC_NAMESPACE).build()) + .add(TYPE, "QuerySpec") + .add("filterExpression", Json.createObjectBuilder() + .add("operandLeft", leftOp) + .add("operator", op) + .add("operandRight", rightOp) + .build()) + .build() + .toString(); + } + private RequestSpecification baseEdrRequest() { return participant.getManagementEndpoint().baseRequest().contentType(JSON); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/TestRuntimeConfiguration.java similarity index 92% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/TestRuntimeConfiguration.java index 861796680..db5f1178f 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/TestRuntimeConfiguration.java @@ -17,15 +17,15 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.lifecycle; +package org.eclipse.tractusx.edc.tests; public class TestRuntimeConfiguration { - + public static final String BPN_SUFFIX = "-BPN"; public static final String SOKRATES_NAME = "SOKRATES"; public static final String SOKRATES_BPN = SOKRATES_NAME + BPN_SUFFIX; public static final String PLATO_NAME = "PLATO"; public static final String PLATO_BPN = PLATO_NAME + BPN_SUFFIX; - static final String DB_SCHEMA_NAME = "testschema"; + public static final String DB_SCHEMA_NAME = "testschema"; } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/CatalogHelperFunctions.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/CatalogHelperFunctions.java similarity index 84% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/CatalogHelperFunctions.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/CatalogHelperFunctions.java index 56bf62478..2533b7959 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/CatalogHelperFunctions.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/CatalogHelperFunctions.java @@ -1,5 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -15,20 +15,20 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + */ -package org.eclipse.tractusx.edc.helpers; +package org.eclipse.tractusx.edc.tests.helpers; import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.JsonValue; -import org.eclipse.edc.connector.contract.spi.ContractId; +import org.eclipse.edc.connector.controlplane.contract.spi.ContractId; -import static org.eclipse.edc.catalog.spi.CatalogRequest.CATALOG_REQUEST_QUERY_SPEC; +import static org.eclipse.edc.connector.controlplane.catalog.spi.CatalogRequest.CATALOG_REQUEST_QUERY_SPEC; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_POLICY_ATTRIBUTE; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; public class CatalogHelperFunctions { diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/EdrNegotiationHelperFunctions.java similarity index 74% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/EdrNegotiationHelperFunctions.java index 0d69075fd..425331fd9 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/EdrNegotiationHelperFunctions.java @@ -1,5 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -15,38 +15,35 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + */ -package org.eclipse.tractusx.edc.helpers; +package org.eclipse.tractusx.edc.tests.helpers; import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonArrayBuilder; import jakarta.json.JsonObject; +import org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractRequest; import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.spi.event.Event; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto; +import org.eclipse.edc.spi.monitor.ConsoleMonitor; import java.util.Set; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -import static org.mockito.Mockito.mock; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; public class EdrNegotiationHelperFunctions { - private static final JsonLd JSON_LD = new TitaniumJsonLd(mock(Monitor.class)); + private static final JsonLd JSON_LD = new TitaniumJsonLd(new ConsoleMonitor()); public static JsonObject createEdrNegotiationRequest(String connectorAddress, String providerId, String offerId, String assetId, JsonObject policy, JsonArray callbacks) { return Json.createObjectBuilder() - .add(TYPE, NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE) + .add(TYPE, ContractRequest.CONTRACT_REQUEST_TYPE) .add(EDC_NAMESPACE + "counterPartyId", providerId) - .add(EDC_NAMESPACE + "providerId", providerId) .add(EDC_NAMESPACE + "counterPartyAddress", connectorAddress) - .add(EDC_NAMESPACE + "protocol", DATASPACE_PROTOCOL_HTTP) + .add(EDC_NAMESPACE + "protocol", "dataspace-protocol-http") .add(EDC_NAMESPACE + "offer", Json.createObjectBuilder() .add(EDC_NAMESPACE + "offerId", offerId) .add(EDC_NAMESPACE + "assetId", assetId) @@ -68,6 +65,7 @@ public static JsonObject createCallback(String url, boolean transactional, Set ReceivedEvent createEvent(Class klass) { return ReceivedEvent.Builder.newInstance().type(klass.getSimpleName()).build(); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/TestFunctions.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/Functions.java similarity index 62% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/TestFunctions.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/Functions.java index 64a99c5eb..11b7b0409 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/TestFunctions.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/Functions.java @@ -1,5 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -15,18 +15,22 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + */ -package org.eclipse.tractusx.edc.tests.edr; +package org.eclipse.tractusx.edc.tests.helpers; import com.fasterxml.jackson.databind.ObjectMapper; import okhttp3.mockwebserver.MockWebServer; -import org.eclipse.tractusx.edc.helpers.ReceivedEvent; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.spec.ECGenParameterSpec; import java.util.concurrent.TimeUnit; -public class TestFunctions { - +public class Functions { private static final ObjectMapper MAPPER = new ObjectMapper(); @@ -42,4 +46,14 @@ public static ReceivedEvent waitForEvent(MockWebServer server) { throw new RuntimeException(e); } } + + public static KeyPair generateKeyPair() { + try { + KeyPairGenerator gen = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider()); + gen.initialize(new ECGenParameterSpec("secp256r1")); + return gen.generateKeyPair(); + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + throw new RuntimeException(e); + } + } } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/PolicyHelperFunctions.java similarity index 85% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/PolicyHelperFunctions.java index 5a16efe0b..946723094 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/PolicyHelperFunctions.java @@ -17,7 +17,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.helpers; +package org.eclipse.tractusx.edc.tests.helpers; import com.fasterxml.jackson.databind.ObjectMapper; @@ -25,7 +25,7 @@ import jakarta.json.JsonArrayBuilder; import jakarta.json.JsonObject; import jakarta.json.JsonObjectBuilder; -import org.eclipse.edc.connector.policy.spi.PolicyDefinition; +import org.eclipse.edc.connector.controlplane.policy.spi.PolicyDefinition; import org.eclipse.edc.jsonld.util.JacksonJsonLd; import org.eclipse.edc.policy.model.AtomicConstraint; import org.eclipse.edc.policy.model.Operator; @@ -43,12 +43,11 @@ import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_CONSTRAINT_TYPE; import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_LOGICAL_CONSTRAINT_TYPE; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; public class PolicyHelperFunctions { public static final String TX_NAMESPACE = "https://w3id.org/tractusx/v0.0.1/ns/"; - public static final String TX_CREDENTIAL_NAMESPACE = "https://w3id.org/tractusx/credentials/v0.0.1/ns/"; private static final String ODRL_JSONLD = "http://www.w3.org/ns/odrl.jsonld"; private static final String BUSINESS_PARTNER_EVALUATION_KEY = "BusinessPartnerNumber"; @@ -58,40 +57,47 @@ public class PolicyHelperFunctions { private static final ObjectMapper MAPPER = JacksonJsonLd.createObjectMapper(); - public static JsonObject bpnGroupPolicy(Operator operator, String... allowedGroups) { return bpnGroupPolicy(operator.getOdrlRepresentation(), allowedGroups); } - private static JsonObject bpnGroupPolicy(String operator, String... allowedGroups) { + /** + * Creates a {@link PolicyDefinition} using the given ID, that contains equality constraints for each of the given BusinessPartnerNumbers: + * each BPN is converted into an {@link AtomicConstraint} {@code BusinessPartnerNumber EQ [BPN]}. + */ + public static JsonObject frameworkPolicy(String id, Map permissions) { + return policyDefinitionBuilder(frameworkPolicy(permissions)) + .add(ID, id) + .build(); + } - var groupConstraint = atomicConstraint(BUSINESS_PARTNER_CONSTRAINT_KEY, operator, Arrays.asList(allowedGroups)); + public static JsonObject frameworkPolicy(Map permissions) { + return Json.createObjectBuilder() + .add(CONTEXT, ODRL_JSONLD) + .add(TYPE, "Set") + .add("permission", Json.createArrayBuilder() + .add(frameworkPermission(permissions))) + .build(); + } + + public static JsonObject frameworkPolicy(String leftOperand, Operator operator, Object rightOperand) { + var constraint = atomicConstraint(leftOperand, operator.getOdrlRepresentation(), rightOperand); var permission = Json.createObjectBuilder() .add("action", "use") .add("constraint", Json.createObjectBuilder() .add(TYPE, ODRL_LOGICAL_CONSTRAINT_TYPE) - .add("or", groupConstraint) + .add("or", constraint) .build()) .build(); return Json.createObjectBuilder() .add(CONTEXT, ODRL_JSONLD) - .add("permission", permission) - .build(); - } - - /** - * Creates a {@link PolicyDefinition} using the given ID, that contains equality constraints for each of the given BusinessPartnerNumbers: - * each BPN is converted into an {@link AtomicConstraint} {@code BusinessPartnerNumber EQ [BPN]}. - */ - public static JsonObject frameworkPolicy(String id, Map permissions) { - return policyDefinitionBuilder(frameworkPolicy(permissions)) - .add(ID, id) + .add(TYPE, "Set") + .add("permission", Json.createArrayBuilder().add(permission)) .build(); } - /** * Creates a {@link PolicyDefinition} using the given ID, that contains equality constraints for each of the given BusinessPartnerNumbers: * each BPN is converted into an {@link AtomicConstraint} {@code BusinessPartnerNumber EQ [BPN]}. @@ -105,16 +111,6 @@ public static JsonObject frameworkTemplatePolicy(String id, String frameworkKind } } - - private static String fetchFrameworkTemplate() { - try (var stream = PolicyHelperFunctions.class.getClassLoader().getResourceAsStream("framework-policy.json")) { - return new String(stream.readAllBytes(), StandardCharsets.UTF_8); - } catch (IOException e) { - throw new RuntimeException(e); - } - - } - public static JsonObjectBuilder policyDefinitionBuilder() { return Json.createObjectBuilder() .add(TYPE, EDC_NAMESPACE + "PolicyDefinitionDto"); @@ -128,11 +124,41 @@ public static JsonObjectBuilder policyDefinitionBuilder(JsonObject policy) { public static JsonObject bnpPolicy(String... bnps) { return Json.createObjectBuilder() .add(CONTEXT, ODRL_JSONLD) + .add(TYPE, "Set") .add("permission", Json.createArrayBuilder() .add(permission(bnps))) .build(); } + + private static JsonObject bpnGroupPolicy(String operator, String... allowedGroups) { + + var groupConstraint = atomicConstraint(BUSINESS_PARTNER_CONSTRAINT_KEY, operator, Arrays.asList(allowedGroups)); + + var permission = Json.createObjectBuilder() + .add("action", "use") + .add("constraint", Json.createObjectBuilder() + .add(TYPE, ODRL_LOGICAL_CONSTRAINT_TYPE) + .add("or", groupConstraint) + .build()) + .build(); + + return Json.createObjectBuilder() + .add(CONTEXT, ODRL_JSONLD) + .add(TYPE, "Set") + .add("permission", permission) + .build(); + } + + private static String fetchFrameworkTemplate() { + try (var stream = PolicyHelperFunctions.class.getClassLoader().getResourceAsStream("framework-policy.json")) { + return new String(stream.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + private static JsonObject permission(String... bpns) { var bpnConstraints = Stream.of(bpns) @@ -148,14 +174,6 @@ private static JsonObject permission(String... bpns) { .build(); } - public static JsonObject frameworkPolicy(Map permissions) { - return Json.createObjectBuilder() - .add(CONTEXT, ODRL_JSONLD) - .add("permission", Json.createArrayBuilder() - .add(frameworkPermission(permissions))) - .build(); - } - private static JsonObject frameworkPermission(Map permissions) { var constraints = permissions.entrySet().stream() @@ -177,8 +195,8 @@ private static JsonObject atomicConstraint(String leftOperand, String operator, .add("leftOperand", leftOperand) .add("operator", operator); - if (rightOperand instanceof Collection) { - builder.add("rightOperand", ((Collection) rightOperand).stream().map(Object::toString).collect(Collectors.joining(","))); + if (rightOperand instanceof Collection coll) { + builder.add("rightOperand", coll.stream().map(Object::toString).collect(Collectors.joining(","))); } else { builder.add("rightOperand", rightOperand.toString()); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/QueryHelperFunctions.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/QueryHelperFunctions.java similarity index 97% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/QueryHelperFunctions.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/QueryHelperFunctions.java index 3c7ab0347..f55667839 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/QueryHelperFunctions.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/QueryHelperFunctions.java @@ -17,7 +17,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.helpers; +package org.eclipse.tractusx.edc.tests.helpers; import jakarta.json.Json; import jakarta.json.JsonObject; diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ReceivedEvent.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/ReceivedEvent.java similarity index 97% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ReceivedEvent.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/ReceivedEvent.java index 2699b702c..4cf374222 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ReceivedEvent.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/ReceivedEvent.java @@ -17,7 +17,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.helpers; +package org.eclipse.tractusx.edc.tests.helpers; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/TransferProcessHelperFunctions.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/TransferProcessHelperFunctions.java similarity index 74% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/TransferProcessHelperFunctions.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/TransferProcessHelperFunctions.java index 9f9c35340..82cafae36 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/TransferProcessHelperFunctions.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/helpers/TransferProcessHelperFunctions.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -17,15 +17,15 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.helpers; +package org.eclipse.tractusx.edc.tests.helpers; import jakarta.json.Json; import jakarta.json.JsonObject; +import static jakarta.json.Json.createObjectBuilder; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; public class TransferProcessHelperFunctions { @@ -35,7 +35,7 @@ public static JsonObject createTransferRequest(String dataRequestId, String coun .add(ID, dataRequestId) .add(EDC_NAMESPACE + "connectorId", "connectorId") .add(EDC_NAMESPACE + "dataDestination", destination) - .add(EDC_NAMESPACE + "protocol", DATASPACE_PROTOCOL_HTTP) + .add(EDC_NAMESPACE + "protocol", "dataspace-protocol-http") .add(EDC_NAMESPACE + "assetId", assetId) .add(EDC_NAMESPACE + "contractId", contractId) .add(EDC_NAMESPACE + "counterPartyAddress", counterPartyAddress) @@ -44,7 +44,6 @@ public static JsonObject createTransferRequest(String dataRequestId, String coun } - public static JsonObject createProxyRequest() { return Json.createObjectBuilder() .add(TYPE, EDC_NAMESPACE + "DataAddress") @@ -52,4 +51,14 @@ public static JsonObject createProxyRequest() { .build(); } + + public static JsonObject httpDataAddress(String baseUrl) { + return createObjectBuilder() + .add(TYPE, EDC_NAMESPACE + "DataAddress") + .add(EDC_NAMESPACE + "type", "HttpData") + .add(EDC_NAMESPACE + "properties", createObjectBuilder() + .add(EDC_NAMESPACE + "baseUrl", baseUrl) + .build()) + .build(); + } } diff --git a/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/participant/TractusxIatpParticipantBase.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/participant/TractusxIatpParticipantBase.java new file mode 100644 index 000000000..175a8fa2a --- /dev/null +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/participant/TractusxIatpParticipantBase.java @@ -0,0 +1,107 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.participant; + +import java.net.URI; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * Specialized version of {@link TractusxParticipantBase} with IATP configurations + */ +public abstract class TractusxIatpParticipantBase extends TractusxParticipantBase { + + protected URI stsUri; + protected String stsClientId; + protected String stsClientSecret; + protected String trustedIssuer; + + public Map iatpConfiguration(TractusxIatpParticipantBase... others) { + var iatpConfiguration = new HashMap<>(getConfiguration()) { + { + + put("edc.iam.sts.oauth.token.url", stsUri + "/token"); + put("edc.iam.sts.oauth.client.id", stsClientId); + put("edc.iam.sts.oauth.client.secret.alias", "client_secret_alias"); + put("edc.ih.iam.id", getBpn()); + put("tx.vault.seed.secrets", "client_secret_alias:%s".formatted(stsClientSecret)); + put("edc.ih.iam.publickey.alias", getFullKeyId()); + put("edc.agent.identity.key", "client_id"); + put("edc.iam.trusted-issuer.issuer.id", trustedIssuer); + put("edc.transfer.proxy.token.signer.privatekey.alias", getPrivateKeyAlias()); + put("edc.transfer.proxy.token.verifier.publickey.alias", getFullKeyId()); + } + }; + + Stream.concat(Stream.of(this), Arrays.stream(others)).forEach(p -> { + var prefix = "tx.iam.iatp.audiences.%s".formatted(p.getName().toLowerCase()); + iatpConfiguration.put("%s.from".formatted(prefix), p.getBpn()); + iatpConfiguration.put("%s.to".formatted(prefix), p.getDid()); + }); + return iatpConfiguration; + } + + public static class Builder

> extends TractusxParticipantBase.Builder { + + protected Builder(P participant) { + super(participant); + } + + public B stsUri(URI stsUri) { + participant.stsUri = stsUri; + return self(); + } + + public B stsClientId(String stsClientId) { + participant.stsClientId = stsClientId; + return self(); + } + + public B stsClientSecret(String stsClientSecret) { + participant.stsClientSecret = stsClientSecret; + return self(); + } + + public B trustedIssuer(String trustedIssuer) { + participant.trustedIssuer = trustedIssuer; + return self(); + } + + @Override + public TractusxIatpParticipantBase build() { + super.build(); + Objects.requireNonNull(participant.stsUri, "STS URI should not be null"); + Objects.requireNonNull(participant.trustedIssuer, "Trusted issuer cannot be null"); + + if (participant.stsClientId == null) { + participant.stsClientId = participant.id; + } + + if (participant.stsClientSecret == null) { + participant.stsClientSecret = "clientSecret"; + } + return participant; + } + } + +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/TxParticipant.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/participant/TractusxParticipantBase.java similarity index 51% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/TxParticipant.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/participant/TractusxParticipantBase.java index 5072d5079..6977e33a1 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/TxParticipant.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/participant/TractusxParticipantBase.java @@ -17,118 +17,52 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.lifecycle.tx; +package org.eclipse.tractusx.edc.tests.participant; -import com.fasterxml.jackson.annotation.JsonCreator; -import io.restassured.specification.RequestSpecification; import jakarta.json.Json; -import jakarta.json.JsonObject; -import org.eclipse.edc.test.system.utils.Participant; +import org.eclipse.edc.connector.controlplane.test.system.utils.Participant; +import org.eclipse.tractusx.edc.tests.IdentityParticipant; +import org.eclipse.tractusx.edc.tests.ParticipantDataApi; +import org.eclipse.tractusx.edc.tests.ParticipantEdrApi; +import org.jetbrains.annotations.NotNull; import java.net.URI; +import java.time.Duration; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import static io.restassured.http.ContentType.JSON; +import static jakarta.json.Json.createObjectBuilder; +import static java.time.Duration.ofSeconds; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.TX_NAMESPACE; -import static org.eclipse.tractusx.edc.tests.TestCommon.ASYNC_TIMEOUT; +import static org.eclipse.edc.util.io.Ports.getFreePort; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; /** - * Implementation for TX of a {@link Participant} + * Base class for doing E2E tests with participants. */ -public class TxParticipant extends Participant { +public abstract class TractusxParticipantBase extends IdentityParticipant { public static final String API_KEY = "testkey"; - public static final String PROXY_SUBPATH = "proxy/aas/request"; + public static final Duration ASYNC_TIMEOUT = ofSeconds(60); + public static final Duration ASYNC_POLL_INTERVAL = ofSeconds(1); private final URI controlPlaneDefault = URI.create("http://localhost:" + getFreePort()); private final URI controlPlaneControl = URI.create("http://localhost:" + getFreePort() + "/control"); - private final URI gateway = URI.create("http://localhost:" + getFreePort() + "/api/gateway"); private final URI backendProviderProxy = URI.create("http://localhost:" + getFreePort() + "/events"); private final URI dataPlaneProxy = URI.create("http://localhost:" + getFreePort()); private final URI dataPlanePublic = URI.create("http://localhost:" + getFreePort() + "/public"); - private final URI miwUri = URI.create("http://localhost:" + getFreePort()); - private final URI oauthTokenUri = URI.create("http://localhost:" + getFreePort()); - private final URI backend = URI.create("http://localhost:" + controlPlaneDefault.getPort() + "/api/consumer/datareference"); - private ParticipantEdrApi edrs; - private ParticipantDataApi data; - /** - * Creates an asset with the given ID and props using the participant's Data Management API - */ + protected ParticipantEdrApi edrs; + protected ParticipantDataApi data; + protected String did; + public void createAsset(String id) { createAsset(id, new HashMap<>(), Map.of("type", "test-type")); } - /** - * Stores BPN groups - */ - public void storeBusinessPartner(String bpn, String... groups) { - var body = Json.createObjectBuilder() - .add(ID, bpn) - .add(TX_NAMESPACE + "groups", Json.createArrayBuilder(Arrays.asList(groups))) - .build(); - managementEndpoint.baseRequest() - .contentType(JSON) - .body(body) - .when() - .post("/business-partner-groups") - .then() - .statusCode(204); - } - - /** - * The BPN/ID of the participant - * - * @return The bpn - */ - public String getBpn() { - return id; - } - - - /** - * Returns the client API for interacting with EDRs - */ - public ParticipantEdrApi edrs() { - return edrs; - } - - /** - * Returns the client API for fetching data - */ - public ParticipantDataApi data() { - return data; - } - - /** - * Creates a policy definition - */ - public String createPolicy(JsonObject policyDefinition) { - return managementRequest() - .contentType(JSON) - .body(policyDefinition) - .when() - .post("/v2/policydefinitions") - .then() - .log().ifError() - .statusCode(200) - .contentType(JSON) - .extract() - .path("@id"); - } - - /** - * Get current error if any of a contract negotiation. - * - * @param negotiationId contract negotiation id - * @return error of the contract negotiation. - */ - public String getContractNegotiationError(String negotiationId) { - return getContractNegotiationField(negotiationId, "errorDetail"); + return getId(); } /** @@ -151,10 +85,8 @@ public Map getConfiguration() { put("edc.api.auth.key", "testkey"); put("web.http.public.path", "/api/public"); put("web.http.public.port", String.valueOf(dataPlanePublic.getPort())); - put("web.http.gateway.path", gateway.getPath()); - put("web.http.gateway.port", String.valueOf(gateway.getPort())); - put("edc.transfer.proxy.token.signer.privatekey.alias", "private-key"); - put("edc.transfer.proxy.token.verifier.publickey.alias", "public-key"); + put("edc.transfer.proxy.token.signer.privatekey.alias", getPrivateKeyAlias()); + put("edc.transfer.proxy.token.verifier.publickey.alias", getFullKeyId()); put("edc.transfer.send.retry.limit", "1"); put("edc.transfer.send.retry.base-delay.ms", "100"); put("tx.dpf.consumer.proxy.port", String.valueOf(dataPlaneProxy.getPort())); @@ -162,122 +94,86 @@ public Map getConfiguration() { put("edc.dataplane.selector.httpplane.url", controlPlaneControl.toString()); put("edc.dataplane.selector.httpplane.sourcetypes", "HttpData"); put("edc.dataplane.selector.httpplane.destinationtypes", "HttpProxy"); - put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + dataPlanePublic.getPort() + "/api/public\"}"); + put("edc.dataplane.selector.httpplane.transfertypes", "HttpProxy-PULL"); + put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + dataPlanePublic.getPort() + "/api/public/v2\"}"); put("edc.receiver.http.dynamic.endpoint", "http://localhost:" + controlPlaneDefault.getPort() + "/api/consumer/datareference"); put("tractusx.businesspartnervalidation.log.agreement.validation", "true"); put("edc.agent.identity.key", "BusinessPartnerNumber"); - put("edc.data.encryption.keys.alias", "test-alias"); put("tx.dpf.proxy.gateway.aas.proxied.path", backendProviderProxy.toString()); put("tx.dpf.proxy.gateway.aas.authorization.type", "none"); + put("edc.iam.issuer.id", getDid()); + put("edc.dataplane.api.public.baseurl", "http://localhost:%d/api/public/v2/data".formatted(dataPlanePublic.getPort())); } }; } /** - * Returns the SSI configuration - */ - public Map ssiConfiguration() { - var ssiConfiguration = new HashMap() { - { - put("tx.ssi.miw.url", miwUri.toString()); - put("tx.ssi.oauth.token.url", oauthTokenUri.toString()); - put("tx.ssi.oauth.client.id", "client_id"); - put("tx.ssi.oauth.client.secret.alias", "client_secret_alias"); - put("tx.ssi.miw.authority.id", "authorityId"); - put("tx.ssi.miw.authority.issuer", "did:web:example.com"); - put("tx.vault.seed.secrets", "client_secret_alias:client_secret"); - put("tx.ssi.endpoint.audience", protocolEndpoint.getUrl().toString()); - } - }; - var baseConfiguration = getConfiguration(); - ssiConfiguration.putAll(baseConfiguration); - return ssiConfiguration; - } - - /** - * Returns the renewal configuration - */ - public Map renewalConfiguration() { - return renewalConfiguration("10"); - } - - /** - * Returns the renewal configuration - */ - public Map renewalConfiguration(String retention) { - var renewalConfig = new HashMap() { - { - put("edc.edr.state-machine.expiring-duration", "10"); - put("edc.edr.state-machine.expired-retention", retention); - put("edc.transfer.proxy.token.validity.seconds", "15"); - } - }; - var baseConfiguration = getConfiguration(); - renewalConfig.putAll(baseConfiguration); - - return renewalConfig; - } - - /** - * Returns the MIW endpoint - */ - public URI miwEndpoint() { - return miwUri; - } - - /** - * Returns the OAuth2 token endpoint + * Returns the client api for fetching EDRs */ - public URI authTokenEndpoint() { - return oauthTokenUri; + public ParticipantEdrApi edrs() { + return edrs; } /** - * Returns the Gateway endpoint + * Returns the client API for fetching data */ - public URI gatewayEndpoint() { - return gateway; + public ParticipantDataApi data() { + return data; } /** - * Returns the Consumer data plane proxy endpoint + * Stores BPN groups */ - public URI dataPlaneProxy() { - return dataPlaneProxy; + public void storeBusinessPartner(String bpn, String... groups) { + var body = createObjectBuilder() + .add(ID, bpn) + .add(TX_NAMESPACE + "groups", Json.createArrayBuilder(Arrays.asList(groups))) + .build(); + managementEndpoint.baseRequest() + .contentType(JSON) + .body(body) + .when() + .post("/business-partner-groups") + .then() + .statusCode(204); } - /** - * Returns the provider gateway backend endpoint - */ - public URI backendProviderProxy() { - return backendProviderProxy; + @Override + public String getFullKeyId() { + return getDid() + "#" + getKeyId(); } - protected RequestSpecification managementRequest() { - return managementEndpoint.baseRequest(); + @NotNull + public String getDid() { + return did; } - public static final class Builder extends Participant.Builder { + public static class Builder

> extends Participant.Builder { - private Builder() { - super(new TxParticipant()); + protected Builder(P participant) { + super(participant); } - @JsonCreator - public static Builder newInstance() { - return new Builder(); + public B did(String did) { + this.participant.did = did; + return self(); } @Override - public TxParticipant build() { + public TractusxParticipantBase build() { + if (participant.did == null) { + participant.did = "did:web:" + participant.name.toLowerCase(); + } + super.managementEndpoint(new Endpoint(URI.create("http://localhost:" + getFreePort() + "/api/management"), Map.of("x-api-key", API_KEY))); super.protocolEndpoint(new Endpoint(URI.create("http://localhost:" + getFreePort() + "/protocol"))); super.timeout(ASYNC_TIMEOUT); super.build(); - this.participant.edrs = new ParticipantEdrApi(participant, participant.managementEndpoint, participant.backend); - this.participant.data = new ParticipantDataApi(participant); + this.participant.edrs = new ParticipantEdrApi(participant); + this.participant.data = new ParticipantDataApi(); return participant; } } + } diff --git a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/BaseCredentialEvaluationFunction.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/participant/TransferParticipant.java similarity index 59% rename from edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/BaseCredentialEvaluationFunction.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/participant/TransferParticipant.java index de45f5d0a..725d018f1 100644 --- a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/BaseCredentialEvaluationFunction.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/participant/TransferParticipant.java @@ -17,20 +17,27 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iatp.policy; +package org.eclipse.tractusx.edc.tests.participant; -import org.eclipse.edc.policy.engine.spi.AtomicConstraintFunction; -import org.eclipse.edc.policy.model.Permission; +/** + * Extension of {@link TractusxParticipantBase} with Transfer specific configuration + */ +public class TransferParticipant extends TractusxParticipantBase { -import java.util.Map; + public static class Builder extends TractusxParticipantBase.Builder { -public abstract class BaseCredentialEvaluationFunction implements AtomicConstraintFunction { + protected Builder() { + super(new TransferParticipant()); + } - protected T getClaim(Class type, String postfix, Map claims) { - return claims.entrySet().stream().filter(e -> e.getKey().endsWith(postfix)) - .findFirst() - .map(Map.Entry::getValue) - .map(type::cast) - .orElse(null); + public static Builder newInstance() { + return new Builder(); + } + + @Override + public TransferParticipant build() { + super.build(); + return participant; + } } } diff --git a/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/BeforeInitCallback.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/BeforeInitCallback.java new file mode 100644 index 000000000..1fe184957 --- /dev/null +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/BeforeInitCallback.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.tests.runtimes; + +import org.eclipse.edc.junit.extensions.EdcExtension; + +/** + * Callback invoked before the runtime boots with extensions of {@link EdcExtension}. This will allow injecting + * custom mock services directly in the tests rather than extending the {@link EdcExtension} with custom mocks + */ +@FunctionalInterface +public interface BeforeInitCallback { + + void beforeInit(EdcExtension runtime); +} diff --git a/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/DataWiper.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/DataWiper.java new file mode 100644 index 000000000..12e66c0ff --- /dev/null +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/DataWiper.java @@ -0,0 +1,84 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.runtimes; + +import org.eclipse.edc.connector.controlplane.asset.spi.index.AssetIndex; +import org.eclipse.edc.connector.controlplane.contract.spi.offer.store.ContractDefinitionStore; +import org.eclipse.edc.connector.controlplane.policy.spi.store.PolicyDefinitionStore; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; + +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_BPN; + +/** + * Helper class to delete all objects from a runtime's data stores. + */ +public class DataWiper { + + private final ServiceExtensionContext context; + + public DataWiper(ServiceExtensionContext context) { + this.context = context; + } + + public void clearPersistence() { + clearAssetIndex(); + clearPolicies(); + clearContractDefinitions(); + clearEdrCache(); + clearBusinessPartnerStore(); + } + + public void clearBusinessPartnerStore() { + var bps = context.getService(BusinessPartnerStore.class); + bps.delete(SOKRATES_BPN); + bps.delete(PLATO_BPN); + } + + public void clearContractDefinitions() { + var cds = context.getService(ContractDefinitionStore.class); + cds.findAll(QuerySpec.max()).forEach(cd -> cds.deleteById(cd.getId())); + } + + public void clearPolicies() { + var ps = context.getService(PolicyDefinitionStore.class); + // must .collect() here, otherwise we'll get a ConcurrentModificationException + ps.findAll(QuerySpec.max()).toList().forEach(p -> ps.delete(p.getId())); + } + + public void clearAssetIndex() { + var index = context.getService(AssetIndex.class); + index.queryAssets(QuerySpec.max()).forEach(asset -> index.deleteById(asset.getId())); + } + + public void clearEdrCache() { + var edrCache = context.getService(EndpointDataReferenceStore.class); + edrCache.query(QuerySpec.max()).getContent().forEach(entry -> { + try { + edrCache.delete(entry.getTransferProcessId()); + } catch (Exception e) { + context.getMonitor().warning("Failed to clean up the cache", e); + } + }); + } +} diff --git a/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/KeyPool.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/KeyPool.java new file mode 100644 index 000000000..184c3314a --- /dev/null +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/KeyPool.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.tests.runtimes; + +import java.security.KeyPair; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class KeyPool { + private static final Map KEYS = new ConcurrentHashMap<>(); + + public static void register(String keyId, KeyPair keypair) { + KEYS.put(keyId, keypair); + } + + public static KeyPair forId(String keyId) { + return KEYS.get(keyId); + } +} diff --git a/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/ParticipantRuntime.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/ParticipantRuntime.java new file mode 100644 index 000000000..9207153fe --- /dev/null +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/ParticipantRuntime.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.tests.runtimes; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import org.eclipse.edc.boot.system.injection.InjectionContainer; +import org.eclipse.edc.iam.did.spi.resolution.DidPublicKeyResolver; +import org.eclipse.edc.iam.identitytrust.spi.SecureTokenService; +import org.eclipse.edc.iam.identitytrust.sts.embedded.EmbeddedSecureTokenService; +import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; +import org.eclipse.edc.spi.iam.AudienceResolver; +import org.eclipse.edc.spi.iam.IdentityService; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.domain.message.RemoteMessage; +import org.eclipse.edc.token.JwtGenerationService; +import org.eclipse.tractusx.edc.spi.identity.mapper.BdrsClient; +import org.eclipse.tractusx.edc.tests.MockBpnIdentityService; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.time.Clock; +import java.time.Duration; +import java.util.List; +import java.util.Map; + + +public class ParticipantRuntime extends EdcRuntimeExtension implements BeforeAllCallback, AfterAllCallback { + + private final Map properties; + private final ECKey runtimeKeyPair; + private DataWiper wiper; + + public ParticipantRuntime(String moduleName, String runtimeName, String bpn, Map properties) { + this(moduleName, runtimeName, bpn, properties, null); + } + + public ParticipantRuntime(String moduleName, String runtimeName, String bpn, Map properties, BeforeInitCallback beforeInitCallback) { + super(moduleName, runtimeName, properties); + this.properties = properties; + this.registerServiceMock(IdentityService.class, new MockBpnIdentityService(bpn)); + this.registerServiceMock(AudienceResolver.class, RemoteMessage::getCounterPartyAddress); + this.registerServiceMock(BdrsClient.class, (s) -> s); + var kid = properties.get("edc.iam.issuer.id") + "#key-1"; + try { + runtimeKeyPair = new ECKeyGenerator(Curve.P_256).keyID(kid).generate(); + KeyPool.register(kid, runtimeKeyPair.toKeyPair()); + var privateKey = runtimeKeyPair.toPrivateKey(); + + registerServiceMock(SecureTokenService.class, new EmbeddedSecureTokenService(new JwtGenerationService(), () -> privateKey, () -> kid, Clock.systemUTC(), Duration.ofMinutes(10).toMillis())); + registerServiceMock(DidPublicKeyResolver.class, keyId -> Result.success(KeyPool.forId(keyId).getPublic())); + } catch (JOSEException e) { + throw new RuntimeException(e); + } + + if (beforeInitCallback != null) { + beforeInitCallback.beforeInit(this); + } + } + + @Override + public void beforeTestExecution(ExtensionContext extensionContext) { + //do nothing - we only want to start the runtime once + wiper.clearPersistence(); + } + + @Override + public void afterTestExecution(ExtensionContext context) { + } + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + //only run this once + super.beforeTestExecution(context); + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + super.afterTestExecution(context); + } + + @Override + protected void bootExtensions(ServiceExtensionContext context, List> serviceExtensions) { + super.bootExtensions(context, serviceExtensions); + wiper = new DataWiper(context); + registerConsumerPullKeys(runtimeKeyPair); + } + + private void registerConsumerPullKeys(ECKey ecKey) { + var privateAlias = properties.get("edc.transfer.proxy.token.signer.privatekey.alias"); + var publicAlias = properties.get("edc.transfer.proxy.token.verifier.publickey.alias"); + + if (privateAlias != null && publicAlias != null) { + var vault = getContext().getService(Vault.class); + vault.storeSecret(privateAlias, ecKey.toJSONString()); + vault.storeSecret(publicAlias, ecKey.toPublicJWK().toJSONString()); + } + } + +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/PgParticipantRuntime.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/PgParticipantRuntime.java similarity index 78% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/PgParticipantRuntime.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/PgParticipantRuntime.java index 4682bab19..7e37030ac 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/PgParticipantRuntime.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/PgParticipantRuntime.java @@ -17,33 +17,37 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.lifecycle; +package org.eclipse.tractusx.edc.tests.runtimes; -import org.eclipse.edc.connector.core.vault.InMemoryVault; +import org.eclipse.edc.boot.system.injection.InjectionContainer; +import org.eclipse.edc.boot.vault.InMemoryVault; +import org.eclipse.edc.spi.monitor.ConsoleMonitor; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.spi.security.Vault; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.injection.InjectionContainer; import org.eclipse.edc.sql.testfixtures.PostgresqlLocalInstance; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.testcontainers.containers.PostgreSQLContainer; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Stream; import static java.lang.String.format; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.DB_SCHEMA_NAME; -import static org.mockito.Mockito.mock; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.DB_SCHEMA_NAME; -public class PgParticipantRuntime extends ParticipantRuntime { +public class PgParticipantRuntime extends ParticipantRuntime implements AfterAllCallback, BeforeAllCallback { private static final String POSTGRES_IMAGE_NAME = "postgres:14.2"; private static final String USER = "postgres"; private static final String PASSWORD = "password"; + private static final List DATASOURCES = List.of("asset", "contractdefinition", + "contractnegotiation", "policy", "transferprocess", "bpn", + "policy-monitor", "edr", "dataplane", "accesstokendata"); private final String dbName; public PostgreSQLContainer postgreSqlContainer; @@ -86,14 +90,13 @@ public Map postgresqlConfiguration(String name) { var jdbcUrl = jdbcUrl(name); return new HashMap<>() { { - Stream.of("asset", "contractdefinition", "contractnegotiation", "policy", "transferprocess", "edr", "bpn", "policy-monitor") - .forEach(context -> { - var group = "edc.datasource." + context; - put(group + ".name", context); - put(group + ".url", jdbcUrl); - put(group + ".user", USER); - put(group + ".password", PASSWORD); - }); + DATASOURCES.forEach(context -> { + var group = "edc.datasource." + context; + put(group + ".name", context); + put(group + ".url", jdbcUrl); + put(group + ".user", USER); + put(group + ".password", PASSWORD); + }); // use non-default schema name to test usage of non-default schema put("org.eclipse.tractusx.edc.postgresql.migration.schema", DB_SCHEMA_NAME); } @@ -109,7 +112,7 @@ public String baseJdbcUrl() { } protected void mockVault() { - this.registerServiceMock(Vault.class, new InMemoryVaultOverride(mock(Monitor.class))); + this.registerServiceMock(Vault.class, new InMemoryVaultOverride(new ConsoleMonitor())); } private static class InMemoryVaultOverride extends InMemoryVault { diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/IatpFrameworkAgreementHttpConsumerPullWithProxyInMemoryTest.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/Runtimes.java similarity index 56% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/IatpFrameworkAgreementHttpConsumerPullWithProxyInMemoryTest.java rename to edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/Runtimes.java index 4691907cd..a033ed718 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/IatpFrameworkAgreementHttpConsumerPullWithProxyInMemoryTest.java +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/runtimes/Runtimes.java @@ -17,25 +17,21 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.tests.transfer; - -import jakarta.json.JsonObject; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.junit.jupiter.api.Disabled; +package org.eclipse.tractusx.edc.tests.runtimes; import java.util.Map; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.TX_CREDENTIAL_NAMESPACE; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.frameworkPolicy; +public interface Runtimes { -@EndToEndTest -// temporarily disabled waiting for an upstream fix -@Disabled -public class IatpFrameworkAgreementHttpConsumerPullWithProxyInMemoryTest extends IatpHttpConsumerPullWithProxyInMemoryTest { + static ParticipantRuntime memoryRuntime(String runtimeName, String bpn, Map properties) { + return memoryRuntime(runtimeName, bpn, properties, null); + } - @Override - protected JsonObject createContractPolicy(String bpn) { - return frameworkPolicy(Map.of(TX_CREDENTIAL_NAMESPACE + "FrameworkAgreement.pcf", "active")); + static ParticipantRuntime memoryRuntime(String runtimeName, String bpn, Map properties, BeforeInitCallback callback) { + return new ParticipantRuntime(":edc-tests:runtime:runtime-memory", runtimeName, bpn, properties, callback); } + static PgParticipantRuntime pgRuntime(String runtimeName, String bpn, Map properties) { + return new PgParticipantRuntime(":edc-tests:runtime:runtime-postgresql", runtimeName, bpn, properties); + } } diff --git a/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullBaseTest.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullBaseTest.java new file mode 100644 index 000000000..9a35f7f2e --- /dev/null +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullBaseTest.java @@ -0,0 +1,124 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcessStates; +import org.eclipse.tractusx.edc.tests.ParticipantAwareTest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.verify.VerificationTimes; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.awaitility.pollinterval.FibonacciPollInterval.fibonacci; +import static org.eclipse.edc.util.io.Ports.getFreePort; +import static org.eclipse.tractusx.edc.tests.helpers.PolicyHelperFunctions.bnpPolicy; +import static org.eclipse.tractusx.edc.tests.helpers.TransferProcessHelperFunctions.createProxyRequest; +import static org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase.ASYNC_TIMEOUT; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +/** + * Base tests for Http PULL scenario + */ +public abstract class HttpConsumerPullBaseTest implements ParticipantAwareTest { + + public static final String MOCK_BACKEND_REMOTE_HOST = "localhost"; + public static final String MOCK_BACKEND_PATH = "/mock/api"; + protected ClientAndServer server; + + protected String privateBackendUrl; + + + @BeforeEach + void setup() { + server = ClientAndServer.startClientAndServer(MOCK_BACKEND_REMOTE_HOST, getFreePort()); + privateBackendUrl = "http://%s:%d%s".formatted(MOCK_BACKEND_REMOTE_HOST, server.getPort(), MOCK_BACKEND_PATH); + } + + @Test + void transferData_privateBackend() { + var assetId = "api-asset-1"; + + + Map dataAddress = Map.of( + "baseUrl", privateBackendUrl, + "type", "HttpData", + "contentType", "application/json" + ); + + plato().createAsset(assetId, Map.of(), dataAddress); + + var accessPolicyId = plato().createPolicyDefinition(createAccessPolicy(sokrates().getBpn())); + var contractPolicyId = plato().createPolicyDefinition(createContractPolicy(sokrates().getBpn())); + plato().createContractDefinition(assetId, "def-1", accessPolicyId, contractPolicyId); + var transferProcessId = sokrates().requestAsset(plato(), assetId, Json.createObjectBuilder().build(), createProxyRequest(), "HttpData-PULL"); + + var edr = new AtomicReference(); + + // wait until transfer process completes + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var tpState = sokrates().getTransferProcessState(transferProcessId); + assertThat(tpState).isNotNull().isEqualTo(TransferProcessStates.STARTED.toString()); + }); + + // wait until EDC is available on the consumer side + server.when(request().withMethod("GET").withPath(MOCK_BACKEND_PATH)).respond(response().withStatusCode(200).withBody("test response")); + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + edr.set(sokrates().edrs().getEdr(transferProcessId)); + assertThat(edr).isNotNull(); + }); + + // pull data out of provider's backend service: + // Prov-DP -> Prov-backend + assertThat(sokrates().data().pullData(edr.get(), Map.of())).isEqualTo("test response"); + + server.verify(request() + .withPath(MOCK_BACKEND_PATH) + .withHeader("Edc-Contract-Agreement-Id") + .withHeader("Edc-Bpn", sokrates().getBpn()) + .withMethod("GET"), VerificationTimes.exactly(1)); + + } + + @AfterEach + void teardown() { + server.stop(); + } + + protected JsonObject createAccessPolicy(String bpn) { + return bnpPolicy(bpn); + } + + protected JsonObject createContractPolicy(String bpn) { + return bnpPolicy(bpn); + } +} diff --git a/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/transfer/ProviderPushBaseTest.java b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/transfer/ProviderPushBaseTest.java new file mode 100644 index 000000000..fdb03f995 --- /dev/null +++ b/edc-tests/edc-controlplane/fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/transfer/ProviderPushBaseTest.java @@ -0,0 +1,105 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer; + +import jakarta.json.JsonObject; +import org.eclipse.tractusx.edc.tests.ParticipantAwareTest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.model.HttpResponse; + +import java.util.Map; +import java.util.UUID; + +import static jakarta.json.Json.createObjectBuilder; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcessStates.COMPLETED; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.util.io.Ports.getFreePort; +import static org.eclipse.tractusx.edc.tests.helpers.PolicyHelperFunctions.bnpPolicy; +import static org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase.ASYNC_TIMEOUT; +import static org.mockserver.model.HttpRequest.request; + +/** + * Base tests for Provider PUSH scenario + */ +public abstract class ProviderPushBaseTest implements ParticipantAwareTest { + + public static final String MOCK_BACKEND_REMOTE_HOST = "localhost"; + + private ClientAndServer server; + + @BeforeEach + void setup() { + server = ClientAndServer.startClientAndServer(MOCK_BACKEND_REMOTE_HOST, getFreePort()); + } + + @Test + void httpPushDataTransfer() { + var assetId = UUID.randomUUID().toString(); + + var providerUrl = "http://%s:%d%s".formatted(MOCK_BACKEND_REMOTE_HOST, server.getPort(), "/mock/api/provider"); + var consumerUrl = "http://%s:%d%s".formatted(MOCK_BACKEND_REMOTE_HOST, server.getPort(), "/mock/api/consumer"); + + server.when(request().withPath("/mock/api/provider")) + .respond(HttpResponse.response().withStatusCode(200)); + server.when(request().withPath("/mock/api/consumer")) + .respond(HttpResponse.response().withStatusCode(200)); + + Map dataAddress = Map.of( + "name", "transfer-test", + "baseUrl", providerUrl, + "type", "HttpData", + "contentType", "application/json" + ); + + plato().createAsset(assetId, Map.of(), dataAddress); + var policyId = plato().createPolicyDefinition(bnpPolicy(sokrates().getBpn())); + plato().createContractDefinition(assetId, "def-1", policyId, policyId); + + var destination = httpDataAddress(consumerUrl); + + var transferProcessId = sokrates().requestAsset(plato(), assetId, createObjectBuilder().build(), destination, "HttpData-PUSH"); + await().atMost(ASYNC_TIMEOUT).untilAsserted(() -> { + var state = sokrates().getTransferProcessState(transferProcessId); + assertThat(state).isEqualTo(COMPLETED.name()); + }); + } + + @AfterEach + void teardown() { + server.stop(); + } + + private JsonObject httpDataAddress(String baseUrl) { + return createObjectBuilder() + .add(TYPE, EDC_NAMESPACE + "DataAddress") + .add(EDC_NAMESPACE + "type", "HttpData") + .add(EDC_NAMESPACE + "properties", createObjectBuilder() + .add(EDC_NAMESPACE + "baseUrl", baseUrl) + .build()) + .build(); + } + +} diff --git a/edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/build.gradle.kts b/edc-tests/edc-controlplane/iatp-tests/build.gradle.kts similarity index 62% rename from edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/build.gradle.kts rename to edc-tests/edc-controlplane/iatp-tests/build.gradle.kts index 7c4d4d89b..23f1f0231 100644 --- a/edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/build.gradle.kts +++ b/edc-tests/edc-controlplane/iatp-tests/build.gradle.kts @@ -19,26 +19,28 @@ plugins { `java-library` + `java-test-fixtures` } dependencies { + testImplementation(testFixtures(project(":edc-tests:edc-controlplane:fixtures"))) + testImplementation(libs.edc.identity.trust.sts.embedded) + testImplementation(libs.edc.ih.did) + testImplementation(libs.edc.ih.spi) + testImplementation(libs.edc.ih.spi.store) testImplementation(libs.edc.junit) + testImplementation(libs.edc.core.token) + testImplementation(libs.edc.identity.vc.ldp) + testImplementation(libs.edc.lib.jws2020) + // 3rd party libs + testImplementation(libs.netty.mockserver) testImplementation(libs.restAssured) + testImplementation(libs.awaitility) testImplementation(libs.okhttp.mockwebserver) - - // test runtime config - testImplementation(libs.edc.config.filesystem) - testImplementation(libs.edc.dpf.http) - testImplementation(libs.edc.auth.tokenbased) - testRuntimeOnly(libs.edc.dpf.core) - testRuntimeOnly(libs.edc.controlplane.apiclient) - testImplementation(project(":spi:edr-spi")) - testImplementation(project(":core:edr-cache-core")) - testImplementation(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-consumer-api")) - testImplementation(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-api")) - testImplementation(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-core")) - + testImplementation(libs.bouncyCastle.bcpkixJdk18on) } - - +// do not publish +edcBuild { + publish.set(false) +} diff --git a/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractIatpConsumerPullTest.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractIatpConsumerPullTest.java new file mode 100644 index 000000000..ef0446bd1 --- /dev/null +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractIatpConsumerPullTest.java @@ -0,0 +1,228 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcessStates; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase; +import org.eclipse.tractusx.edc.tests.transfer.iatp.harness.DataspaceIssuer; +import org.eclipse.tractusx.edc.tests.transfer.iatp.harness.IatpParticipant; +import org.eclipse.tractusx.edc.tests.transfer.iatp.harness.StsParticipant; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.mockserver.verify.VerificationTimes; + +import java.io.IOException; +import java.net.URI; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.awaitility.pollinterval.FibonacciPollInterval.fibonacci; +import static org.eclipse.edc.util.io.Ports.getFreePort; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_CREDENTIAL_NS; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.CX_POLICY_NS; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_NAME; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_NAME; +import static org.eclipse.tractusx.edc.tests.helpers.PolicyHelperFunctions.frameworkPolicy; +import static org.eclipse.tractusx.edc.tests.helpers.TransferProcessHelperFunctions.createProxyRequest; +import static org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase.ASYNC_TIMEOUT; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +public abstract class AbstractIatpConsumerPullTest extends HttpConsumerPullBaseTest { + + protected static final URI DIM_URI = URI.create("http://localhost:" + getFreePort()); + protected static final DataspaceIssuer DATASPACE_ISSUER_PARTICIPANT = new DataspaceIssuer(); + protected static final StsParticipant STS = StsParticipant.Builder.newInstance() + .id("STS") + .name("STS") + .build(); + protected static final IatpParticipant SOKRATES = IatpParticipant.Builder.newInstance() + .name(SOKRATES_NAME) + .id(SOKRATES_BPN) + .stsUri(STS.stsUri()) + .stsClientId(SOKRATES_BPN) + .stsClientSecret("client_secret") + .trustedIssuer(DATASPACE_ISSUER_PARTICIPANT.didUrl()) + .dimUri(DIM_URI) + .did(did(SOKRATES_NAME)) + .build(); + protected static final IatpParticipant PLATO = IatpParticipant.Builder.newInstance() + .name(PLATO_NAME) + .id(PLATO_BPN) + .stsUri(STS.stsUri()) + .stsClientId(PLATO_BPN) + .stsClientSecret("client_secret") + .trustedIssuer(DATASPACE_ISSUER_PARTICIPANT.didUrl()) + .dimUri(DIM_URI) + .did(did(PLATO_NAME)) + .build(); + + private static String did(String name) { + return "did:example:" + name.toLowerCase(); + } + + + @Override + public TractusxParticipantBase plato() { + return PLATO; + } + + @Override + public TractusxParticipantBase sokrates() { + return SOKRATES; + } + + + @DisplayName("Contract policy is fulfilled") + @ParameterizedTest(name = "{1}") + @ArgumentsSource(ValidContractPolicyProvider.class) + void transferData_whenContractPolicyFulfilled(JsonObject contractPolicy, String description) { + var assetId = "api-asset-1"; + + var authCodeHeaderName = "test-authkey"; + var authCode = "test-authcode"; + + Map dataAddress = Map.of( + "baseUrl", privateBackendUrl, + "type", "HttpData", + "contentType", "application/json", + "authKey", authCodeHeaderName, + "authCode", authCode + ); + + PLATO.createAsset(assetId, Map.of(), dataAddress); + + var accessPolicyId = PLATO.createPolicyDefinition(createAccessPolicy(SOKRATES.getBpn())); + var contractPolicyId = PLATO.createPolicyDefinition(contractPolicy); + PLATO.createContractDefinition(assetId, "def-1", accessPolicyId, contractPolicyId); + var transferProcessId = SOKRATES.requestAsset(PLATO, assetId, Json.createObjectBuilder().build(), createProxyRequest(), "HttpData-PULL"); + + var edr = new AtomicReference(); + + // wait until transfer process completes + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var tpState = SOKRATES.getTransferProcessState(transferProcessId); + assertThat(tpState).isNotNull().isEqualTo(TransferProcessStates.STARTED.toString()); + }); + + // wait until EDC is available on the consumer side + server.when(request().withMethod("GET").withPath(MOCK_BACKEND_PATH)).respond(response().withStatusCode(200).withBody("test response")); + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + edr.set(SOKRATES.edrs().getEdr(transferProcessId)); + assertThat(edr).isNotNull(); + }); + + // pull data out of provider's backend service: + // Prov-DP -> Prov-backend + assertThat(sokrates().data().pullData(edr.get(), Map.of())).isEqualTo("test response"); + + server.verify(request() + .withPath(MOCK_BACKEND_PATH) + .withHeader("Edc-Contract-Agreement-Id") + .withHeader("Edc-Bpn", sokrates().getBpn()) + .withMethod("GET"), VerificationTimes.exactly(1)); + } + + @DisplayName("Contract policy is NOT fulfilled") + @ParameterizedTest(name = "{1}") + @ArgumentsSource(InvalidContractPolicyProvider.class) + void transferData_whenContractPolicyNotFulfilled(JsonObject contractPolicy, String description) throws IOException { + var assetId = "api-asset-1"; + + var authCodeHeaderName = "test-authkey"; + var authCode = "test-authcode"; + + Map dataAddress = Map.of( + "baseUrl", privateBackendUrl, + "type", "HttpData", + "contentType", "application/json", + "authKey", authCodeHeaderName, + "authCode", authCode + ); + + PLATO.createAsset(assetId, Map.of(), dataAddress); + + var accessPolicyId = PLATO.createPolicyDefinition(createAccessPolicy(SOKRATES.getBpn())); + var contractPolicyId = PLATO.createPolicyDefinition(contractPolicy); + PLATO.createContractDefinition(assetId, "def-1", accessPolicyId, contractPolicyId); + var negotiationId = SOKRATES.initContractNegotiation(PLATO, assetId); + + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var contractNegotiationState = SOKRATES.getContractNegotiationState(negotiationId); + assertThat(contractNegotiationState).isEqualTo("TERMINATED"); + }); + } + + @Override + protected JsonObject createContractPolicy(String bpn) { + return frameworkPolicy(Map.of(CX_CREDENTIAL_NS + "Membership", "active")); + } + + private static class ValidContractPolicyProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) { + return Stream.of( + Arguments.of(frameworkPolicy(Map.of(CX_POLICY_NS + "Membership", "active")), "MembershipCredential"), + Arguments.of(frameworkPolicy(Map.of(CX_POLICY_NS + "FrameworkAgreement.pcf", "active")), "PCF Use Case (legacy notation)"), + Arguments.of(frameworkPolicy(Map.of(CX_POLICY_NS + "FrameworkAgreement", "pcf")), "PCF Use Case (new notation)"), + Arguments.of(frameworkPolicy(Map.of(CX_POLICY_NS + "Dismantler", "active")), "Dismantler Credential"), + Arguments.of(frameworkPolicy(Map.of(CX_POLICY_NS + "Dismantler.activityType", "vehicleDismantle")), "Dismantler Cred (activity type)"), + Arguments.of(frameworkPolicy(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_ANY_OF, List.of("Moskvich", "Tatra")), "Dismantler allowedBrands (IS_ANY_OF, one intersects)"), + Arguments.of(frameworkPolicy(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.EQ, List.of("Moskvich", "Lada")), "Dismantler allowedBrands (EQ, exact match)"), + Arguments.of(frameworkPolicy(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_NONE_OF, List.of("Yugo", "Tatra")), "Dismantler allowedBrands (IS_NONE_OF, no intersect)"), + Arguments.of(frameworkPolicy(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IN, List.of("Moskvich", "Tatra", "Yugo", "Lada")), "Dismantler allowedBrands (IN, fully contained)") + ); + } + } + + private static class InvalidContractPolicyProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) { + return Stream.of( + Arguments.of(frameworkPolicy(Map.of(CX_POLICY_NS + "FrameworkAgreement.sustainability", "active")), "Sustainability Use Case (legacy notation)"), + Arguments.of(frameworkPolicy(Map.of(CX_POLICY_NS + "FrameworkAgreement", "traceability")), "Traceability Use Case (new notation)"), + Arguments.of(frameworkPolicy(Map.of(CX_POLICY_NS + "Dismantler.activityType", "vehicleScrap")), "Dismantler activityType does not match"), + Arguments.of(frameworkPolicy(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.NEQ, List.of("Moskvich", "Lada")), "Dismantler allowedBrands (NEQ, but is equal)"), + Arguments.of(frameworkPolicy(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IS_NONE_OF, List.of("Yugo", "Lada")), "Dismantler allowedBrands (IS_NONE_OF, but is one contains)"), + Arguments.of(frameworkPolicy(CX_POLICY_NS + "Dismantler.allowedBrands", Operator.IN, List.of("Moskvich", "Tatra", "Yugo")), "Dismantler allowedBrands (IN, but not subset)") + ); + } + } + +} diff --git a/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/DimHttpConsumerPullTest.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/DimHttpConsumerPullTest.java new file mode 100644 index 000000000..90b9c2e4d --- /dev/null +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/DimHttpConsumerPullTest.java @@ -0,0 +1,105 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer; + +import okhttp3.mockwebserver.MockWebServer; +import org.eclipse.edc.iam.did.spi.document.DidDocument; +import org.eclipse.edc.iam.identitytrust.sts.embedded.EmbeddedSecureTokenService; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.token.JwtGenerationService; +import org.eclipse.edc.token.spi.TokenGenerationService; +import org.eclipse.tractusx.edc.tests.transfer.iatp.dispatchers.DimDispatcher; +import org.eclipse.tractusx.edc.tests.transfer.iatp.dispatchers.KeycloakDispatcher; +import org.eclipse.tractusx.edc.tests.transfer.iatp.harness.IatpParticipant; +import org.eclipse.tractusx.edc.tests.transfer.iatp.runtime.IatpParticipantRuntime; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.io.IOException; +import java.security.PrivateKey; +import java.time.Clock; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +import static org.eclipse.tractusx.edc.tests.transfer.iatp.harness.IatpHelperFunctions.configureParticipant; +import static org.eclipse.tractusx.edc.tests.transfer.iatp.runtime.Runtimes.dimRuntime; + +@EndToEndTest +public class DimHttpConsumerPullTest extends AbstractIatpConsumerPullTest { + + + @RegisterExtension + protected static final IatpParticipantRuntime SOKRATES_RUNTIME = dimRuntime(SOKRATES.getName(), SOKRATES.iatpConfiguration(PLATO), SOKRATES.getKeyPair()); + + @RegisterExtension + protected static final IatpParticipantRuntime PLATO_RUNTIME = dimRuntime(PLATO.getName(), PLATO.iatpConfiguration(SOKRATES), PLATO.getKeyPair()); + private static MockWebServer oauthServer; + private static MockWebServer dimDispatcher; + + @BeforeAll + static void prepare() throws IOException { + + var tokenGeneration = new JwtGenerationService(); + + var generatorServices = Map.of( + SOKRATES.getDid(), tokenServiceFor(tokenGeneration, SOKRATES), + PLATO.getDid(), tokenServiceFor(tokenGeneration, PLATO)); + + oauthServer = new MockWebServer(); + oauthServer.start(STS.stsUri().getPort()); + oauthServer.setDispatcher(new KeycloakDispatcher(STS.stsUri().getPath() + "/token")); + + dimDispatcher = new MockWebServer(); + dimDispatcher.start(DIM_URI.getPort()); + dimDispatcher.setDispatcher(new DimDispatcher(generatorServices)); + + // create the DIDs cache + var dids = new HashMap(); + dids.put(DATASPACE_ISSUER_PARTICIPANT.didUrl(), DATASPACE_ISSUER_PARTICIPANT.didDocument()); + dids.put(SOKRATES.getDid(), SOKRATES.getDidDocument()); + dids.put(PLATO.getDid(), PLATO.getDidDocument()); + + configureParticipant(DATASPACE_ISSUER_PARTICIPANT, SOKRATES, SOKRATES_RUNTIME, dids, null); + configureParticipant(DATASPACE_ISSUER_PARTICIPANT, PLATO, PLATO_RUNTIME, dids, null); + + } + + @AfterAll + static void unwind() throws IOException { + oauthServer.shutdown(); + dimDispatcher.shutdown(); + } + + private static EmbeddedSecureTokenService tokenServiceFor(TokenGenerationService tokenGenerationService, IatpParticipant iatpDimParticipant) { + return new EmbeddedSecureTokenService(tokenGenerationService, privateKeySupplier(iatpDimParticipant), publicIdSupplier(iatpDimParticipant), Clock.systemUTC(), 60 * 60); + } + + private static Supplier privateKeySupplier(IatpParticipant participant) { + return () -> participant.getKeyPair().getPrivate(); + } + + private static Supplier publicIdSupplier(IatpParticipant participant) { + return participant::verificationId; + } + + +} diff --git a/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/StsHttpConsumerPullTest.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/StsHttpConsumerPullTest.java new file mode 100644 index 000000000..4ec46af14 --- /dev/null +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/StsHttpConsumerPullTest.java @@ -0,0 +1,61 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer; + +import org.eclipse.edc.iam.did.spi.document.DidDocument; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.tractusx.edc.tests.transfer.iatp.runtime.IatpParticipantRuntime; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.util.HashMap; + +import static org.eclipse.tractusx.edc.tests.transfer.iatp.harness.IatpHelperFunctions.configureParticipant; +import static org.eclipse.tractusx.edc.tests.transfer.iatp.runtime.Runtimes.iatpRuntime; +import static org.eclipse.tractusx.edc.tests.transfer.iatp.runtime.Runtimes.stsRuntime; + +@EndToEndTest +public class StsHttpConsumerPullTest extends AbstractIatpConsumerPullTest { + + + @RegisterExtension + protected static final IatpParticipantRuntime SOKRATES_RUNTIME = iatpRuntime(SOKRATES.getName(), SOKRATES.iatpConfiguration(PLATO), SOKRATES.getKeyPair()); + + @RegisterExtension + protected static final IatpParticipantRuntime PLATO_RUNTIME = iatpRuntime(PLATO.getName(), PLATO.iatpConfiguration(SOKRATES), PLATO.getKeyPair()); + + @RegisterExtension + protected static final IatpParticipantRuntime STS_RUNTIME = stsRuntime(STS.getName(), STS.stsConfiguration(SOKRATES, PLATO), STS.getKeyPair()); + + @BeforeAll + static void prepare() { + + // create the DIDs cache + var dids = new HashMap(); + dids.put(DATASPACE_ISSUER_PARTICIPANT.didUrl(), DATASPACE_ISSUER_PARTICIPANT.didDocument()); + dids.put(SOKRATES.getDid(), SOKRATES.getDidDocument()); + dids.put(PLATO.getDid(), PLATO.getDidDocument()); + + configureParticipant(DATASPACE_ISSUER_PARTICIPANT, SOKRATES, SOKRATES_RUNTIME, dids, STS_RUNTIME); + configureParticipant(DATASPACE_ISSUER_PARTICIPANT, PLATO, PLATO_RUNTIME, dids, STS_RUNTIME); + + } + +} diff --git a/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/dispatchers/DimDispatcher.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/dispatchers/DimDispatcher.java new file mode 100644 index 000000000..be30b6022 --- /dev/null +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/dispatchers/DimDispatcher.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.tests.transfer.iatp.dispatchers; + +import okhttp3.mockwebserver.Dispatcher; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.RecordedRequest; +import org.eclipse.edc.iam.identitytrust.sts.embedded.EmbeddedSecureTokenService; +import org.eclipse.edc.spi.iam.TokenRepresentation; +import org.eclipse.edc.spi.types.TypeManager; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.eclipse.edc.iam.identitytrust.spi.SelfIssuedTokenConstants.PRESENTATION_TOKEN_CLAIM; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SUBJECT; + +/** + * Mock service for DIM interaction. Underlying it uses the {@link EmbeddedSecureTokenService} for generating SI tokens + */ +public class DimDispatcher extends Dispatcher { + + private static final TypeManager MAPPER = new TypeManager(); + private final String path; + private final Map secureTokenServices; + + public DimDispatcher(String path, Map secureTokenServices) { + this.path = path; + this.secureTokenServices = secureTokenServices; + } + + public DimDispatcher(Map secureTokenServices) { + this("/", secureTokenServices); + } + + @NotNull + @Override + @SuppressWarnings({ "unchecked" }) + public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) { + if (recordedRequest.getPath().split("\\?")[0].equals(path)) { + + var body = MAPPER.readValue(recordedRequest.getBody().readByteArray(), Map.class); + + var grant = Optional.ofNullable(body.get("grantAccess")) + .map((payload) -> grantAccessHandler((Map) payload)); + + var sign = Optional.ofNullable(body.get("signToken")) + .map((payload) -> signTokenHandler((Map) payload)); + + return grant.or(() -> sign).orElse(new MockResponse().setResponseCode(404)); + } + return new MockResponse().setResponseCode(404); + } + + @SuppressWarnings("unchecked") + private MockResponse grantAccessHandler(Map params) { + var issuer = params.get("consumerDid").toString(); + var audience = params.get("providerDid").toString(); + Collection scopes = (Collection) params.get("credentialTypes"); + var scope = scopes.stream().map("org.eclipse.tractusx.vc.type:%s:read"::formatted).collect(Collectors.joining(" ")); + var claims = Map.of(ISSUER, issuer, SUBJECT, issuer, AUDIENCE, audience); + + var sts = secureTokenServices.get(issuer); + var token = sts.createToken(claims, scope) + .map(TokenRepresentation::getToken) + .orElseThrow(failure -> new RuntimeException(failure.getFailureDetail())); + + return new MockResponse().setBody(MAPPER.writeValueAsString(Map.of("jwt", token))); + } + + private MockResponse signTokenHandler(Map params) { + var subject = params.get("subject").toString(); + var accessToken = params.get("token").toString(); + var audience = params.get("audience").toString(); + var issuer = params.get("issuer").toString(); + + var claims = Map.of( + ISSUER, issuer, + SUBJECT, subject, + AUDIENCE, audience, + PRESENTATION_TOKEN_CLAIM, accessToken); + + var sts = secureTokenServices.get(issuer); + var token = sts.createToken(claims, null) + .map(TokenRepresentation::getToken) + .orElseThrow(failure -> new RuntimeException(failure.getFailureDetail())); + + return new MockResponse().setBody(MAPPER.writeValueAsString(Map.of("jwt", token))); + } + + private MockResponse createTokenResponse() { + return new MockResponse().setBody(MAPPER.writeValueAsString(Map.of("jwt", "token"))); + } + +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/KeycloakDispatcher.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/dispatchers/KeycloakDispatcher.java similarity index 78% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/KeycloakDispatcher.java rename to edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/dispatchers/KeycloakDispatcher.java index 53240d078..6861f3a30 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/KeycloakDispatcher.java +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/dispatchers/KeycloakDispatcher.java @@ -1,5 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -15,9 +15,9 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + */ -package org.eclipse.tractusx.edc.token; +package org.eclipse.tractusx.edc.tests.transfer.iatp.dispatchers; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; @@ -30,15 +30,20 @@ public class KeycloakDispatcher extends Dispatcher { private static final TypeManager MAPPER = new TypeManager(); - - public KeycloakDispatcher() { + private final String path; + + public KeycloakDispatcher(String path) { + this.path = path; + } + public KeycloakDispatcher() { + this("/"); } @NotNull @Override - public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) throws InterruptedException { - if (recordedRequest.getPath().split("\\?")[0].equals("/")) { + public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) { + if (recordedRequest.getPath().split("\\?")[0].equals(path)) { return createTokenResponse(); } return new MockResponse().setResponseCode(404); diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/iatp/DataspaceIssuer.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/DataspaceIssuer.java similarity index 62% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/iatp/DataspaceIssuer.java rename to edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/DataspaceIssuer.java index 887e53211..6a03cd8eb 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/iatp/DataspaceIssuer.java +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/DataspaceIssuer.java @@ -17,57 +17,51 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.lifecycle.tx.iatp; +package org.eclipse.tractusx.edc.tests.transfer.iatp.harness; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jose.jwk.Curve; -import com.nimbusds.jose.jwk.ECKey; import com.nimbusds.jose.jwk.JWK; +import jakarta.json.Json; import jakarta.json.JsonObject; import org.eclipse.edc.iam.did.spi.document.DidDocument; import org.eclipse.edc.iam.did.spi.document.VerificationMethod; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.CredentialFormat; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.CredentialSubject; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.Issuer; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredentialContainer; import org.eclipse.edc.identityhub.spi.model.VerifiableCredentialResource; -import org.eclipse.edc.identitytrust.model.CredentialFormat; -import org.eclipse.edc.identitytrust.model.CredentialSubject; -import org.eclipse.edc.identitytrust.model.Issuer; -import org.eclipse.edc.identitytrust.model.VerifiableCredential; -import org.eclipse.edc.identitytrust.model.VerifiableCredentialContainer; import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.security.signature.jws2020.JwkMethod; import org.eclipse.edc.security.signature.jws2020.JwsSignature2020Suite; import org.eclipse.edc.verifiablecredentials.linkeddata.LdpIssuer; +import org.eclipse.tractusx.edc.tests.IdentityParticipant; import java.net.URI; -import java.security.KeyPair; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; import java.time.Instant; import java.util.List; import java.util.Map; import java.util.function.Supplier; import static org.eclipse.edc.jsonld.util.JacksonJsonLd.createObjectMapper; -import static org.eclipse.tractusx.edc.helpers.IatpHelperFunctions.createVc; -import static org.eclipse.tractusx.edc.helpers.IatpHelperFunctions.frameworkAgreementSubject; -import static org.eclipse.tractusx.edc.helpers.IatpHelperFunctions.generateKeyPair; -import static org.eclipse.tractusx.edc.helpers.IatpHelperFunctions.membershipSubject; +import static org.eclipse.tractusx.edc.tests.transfer.iatp.harness.IatpHelperFunctions.createVc; +import static org.eclipse.tractusx.edc.tests.transfer.iatp.harness.IatpHelperFunctions.frameworkAgreementSubject; +import static org.eclipse.tractusx.edc.tests.transfer.iatp.harness.IatpHelperFunctions.membershipSubject; import static org.mockito.Mockito.mock; /** * Dataspace issuer configurations */ -public class DataspaceIssuer { +public class DataspaceIssuer extends IdentityParticipant { public static final String DATASPACE_ISSUER = "did:example:dataspace_issuer"; private static final ObjectMapper MAPPER = createObjectMapper(); private static final String KEY_ID = "#key1"; private final JwsSignature2020Suite jws2020suite = new JwsSignature2020Suite(MAPPER); private final DidDocument didDocument; - private final KeyPair keyPair; public DataspaceIssuer() { - keyPair = generateKeyPair(); didDocument = generateDidDocument(); } @@ -80,10 +74,10 @@ public DidDocument didDocument() { } public String verificationId() { - return DATASPACE_ISSUER + KEY_ID; + return DATASPACE_ISSUER + "#" + getKeyId(); } - public VerifiableCredentialResource issueCredential(IatpParticipant participant, JsonLd jsonLd, String type, Supplier credentialSubjectSupplier, Supplier subjectSupplier) { + public VerifiableCredentialResource issueCredential(String did, String bpn, JsonLd jsonLd, String type, Supplier credentialSubjectSupplier, Supplier subjectSupplier) { var credential = VerifiableCredential.Builder.newInstance() .type(type) .credentialSubject(credentialSubjectSupplier.get()) @@ -94,28 +88,48 @@ public VerifiableCredentialResource issueCredential(IatpParticipant participant, var rawVc = createLdpVc(jsonLd, type, subjectSupplier); return VerifiableCredentialResource.Builder.newInstance() .issuerId(didUrl()) - .holderId(participant.getBpn()) + .participantId(did) + .holderId(bpn) .credential(new VerifiableCredentialContainer(rawVc, CredentialFormat.JSON_LD, credential)) .build(); } - public VerifiableCredentialResource issueMembershipCredential(IatpParticipant participant, JsonLd jsonLd) { - return issueCredential(participant, jsonLd, "MembershipCredential", () -> CredentialSubject.Builder.newInstance() - .claim("holderIdentifier", participant.getBpn()) + public VerifiableCredentialResource issueMembershipCredential(String did, String bpn, JsonLd jsonLd) { + return issueCredential(did, bpn, jsonLd, "MembershipCredential", () -> CredentialSubject.Builder.newInstance() + .claim("holderIdentifier", bpn) .build(), - () -> membershipSubject(participant.didUrl(), participant.getBpn())); + () -> membershipSubject(did, bpn)); } - public VerifiableCredentialResource issueFrameworkCredential(IatpParticipant participant, JsonLd jsonLd, String type) { - return issueCredential(participant, jsonLd, "UseCaseFrameworkCondition", () -> CredentialSubject.Builder.newInstance() - .claim("holderIdentifier", participant.getBpn()) - .claim("useCaseType", type) + public VerifiableCredentialResource issueDismantlerCredential(String did, String bpn, JsonLd jsonLd) { + return issueCredential(did, bpn, jsonLd, "DismantlerCredential", () -> CredentialSubject.Builder.newInstance() + .claim("holderIdentifier", bpn) + .claim("activityType", "vehicleDismantle") + .claim("allowedVehicleBrands", List.of("Moskvich", "Lada")) .build(), - () -> frameworkAgreementSubject(participant.didUrl(), participant.getBpn(), type)); + () -> Json.createObjectBuilder() + .add("type", "DismantlerCredential") + .add("holderIdentifier", bpn) + .add("activityType", "vehicleDismantle") + .add("allowedVehicleBrands", Json.createArrayBuilder().add("Moskvich").add("Lada").build()) + .add("id", did) + .build()); + } + + public VerifiableCredentialResource issueFrameworkCredential(String did, String bpn, JsonLd jsonLd, String credentialType) { + return issueCredential(did, bpn, jsonLd, credentialType, () -> CredentialSubject.Builder.newInstance() + .claim("holderIdentifier", bpn) + .build(), + () -> frameworkAgreementSubject(did, bpn, credentialType)); } + @Override + public String getFullKeyId() { + return verificationId(); + } + private String createLdpVc(JsonLd jsonLd, String type, Supplier subjectSupplier) { var issuer = LdpIssuer.Builder.newInstance() .jsonLd(jsonLd) @@ -127,7 +141,7 @@ private String createLdpVc(JsonLd jsonLd, String type, Supplier subj .verificationMethod(new JwkMethod(URI.create(verificationId()), null, null, null)) .purpose(URI.create("https://w3id.org/security#assertionMethod")); - var key = new ECKey.Builder(Curve.P_256, (ECPublicKey) keyPair.getPublic()).privateKey(keyPair.getPrivate()).build(); + var key = getKeyPairAsJwk(); var vc = createVc(didUrl(), type, subjectSupplier); var result = issuer.signDocument(vc, createKeyPair(key, verificationId()), proofOptions).orElseThrow(err -> new RuntimeException(err.getFailureDetail())); @@ -146,15 +160,13 @@ private com.apicatalog.ld.signature.key.KeyPair createKeyPair(JWK jwk, String id private DidDocument generateDidDocument() { - var ecKey = new ECKey.Builder(Curve.P_256, (ECPublicKey) keyPair.getPublic()) - .privateKey((ECPrivateKey) keyPair.getPrivate()) - .build(); + var jwk = getKeyPairAsJwk(); var verificationMethod = VerificationMethod.Builder.newInstance() .id(verificationId()) .controller(didUrl()) .type("JsonWebKey2020") - .publicKeyJwk(ecKey.toPublicJWK().toJSONObject()) + .publicKeyJwk(jwk.toPublicJWK().toJSONObject()) .build(); return DidDocument.Builder.newInstance() diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/did/DidExampleResolver.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/DidExampleResolver.java similarity index 96% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/did/DidExampleResolver.java rename to edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/DidExampleResolver.java index 34e7952b4..c9297a456 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/did/DidExampleResolver.java +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/DidExampleResolver.java @@ -17,7 +17,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.did; +package org.eclipse.tractusx.edc.tests.transfer.iatp.harness; import org.eclipse.edc.iam.did.spi.document.DidDocument; import org.eclipse.edc.iam.did.spi.resolution.DidResolver; @@ -35,9 +35,6 @@ public class DidExampleResolver implements DidResolver { private final Map cache = new HashMap<>(); - public DidExampleResolver() { - } - @Override public @NotNull String getMethod() { return "example"; diff --git a/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/IatpHelperFunctions.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/IatpHelperFunctions.java new file mode 100644 index 000000000..36485819d --- /dev/null +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/IatpHelperFunctions.java @@ -0,0 +1,131 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer.iatp.harness; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import org.eclipse.edc.iam.did.spi.document.DidDocument; +import org.eclipse.edc.iam.did.spi.resolution.DidResolverRegistry; +import org.eclipse.edc.identityhub.spi.ParticipantContextService; +import org.eclipse.edc.identityhub.spi.model.VerifiableCredentialResource; +import org.eclipse.edc.identityhub.spi.model.participant.KeyDescriptor; +import org.eclipse.edc.identityhub.spi.model.participant.ParticipantManifest; +import org.eclipse.edc.identityhub.spi.store.CredentialStore; +import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.junit.extensions.EdcExtension; +import org.eclipse.edc.spi.security.Vault; + +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class IatpHelperFunctions { + public static JsonObject createVc(String issuer, String type, Supplier subjectSupplier) { + return Json.createObjectBuilder() + .add("@context", context()) + .add("type", types(type)) + .add("credentialSubject", subjectSupplier.get()) + .add("issuer", issuer) + .add("issuanceDate", Instant.now().toString()) + .build(); + } + + public static JsonObject membershipSubject(String did, String id) { + return Json.createObjectBuilder() + .add("type", "MembershipCredential") + .add("holderIdentifier", id) + .add("id", did) + .build(); + + } + + public static JsonObject frameworkAgreementSubject(String did, String id, String type) { + return Json.createObjectBuilder() + .add("type", type) + .add("holderIdentifier", id) + .add("contractVersion", "1.0.0") + .add("contractTemplate", "https://public.catena-x.org/contracts/traceabilty.v1.pdf") + .add("id", did) + .build(); + + } + + private static JsonArray types(String type) { + return Json.createArrayBuilder() + .add("VerifiableCredential") + .add(type) + .build(); + } + + private static JsonArray context() { + return Json.createArrayBuilder() + .add("https://www.w3.org/2018/credentials/v1") + .add("https://w3id.org/security/suites/jws-2020/v1") + .add("https://w3id.org/catenax/credentials") + .build(); + } + + public static void configureParticipant(DataspaceIssuer issuer, IatpParticipant participant, EdcExtension runtime, Map didDocs, EdcExtension stsRuntime) { + + if (stsRuntime != null) { + stsRuntime.getContext().getService(Vault.class).storeSecret(participant.verificationId(), participant.getPrivateKeyAsString()); + } + var participantContextService = runtime.getContext().getService(ParticipantContextService.class); + var vault = runtime.getContext().getService(Vault.class); + var didResolverRegistry = runtime.getContext().getService(DidResolverRegistry.class); + var didResolver = new DidExampleResolver(); + didDocs.forEach(didResolver::addCached); + didResolverRegistry.register(didResolver); + + var participantKey = participant.getKeyPairAsJwk(); + var key = KeyDescriptor.Builder.newInstance() + .keyId(participant.getKeyId()) + .publicKeyJwk(participantKey.toPublicJWK().toJSONObject()) + .privateKeyAlias(participant.getPrivateKeyAlias()) + .build(); + + var participantManifest = ParticipantManifest.Builder.newInstance() + .participantId(participant.getDid()) + .did(participant.getDid()) + .key(key) + .build(); + + participantContextService.createParticipantContext(participantManifest); + vault.storeSecret(participant.getPrivateKeyAlias(), participant.getPrivateKeyAsString()); + + storeCredentials(issuer, participant, runtime); + } + + private static void storeCredentials(DataspaceIssuer issuer, IatpParticipant participant, EdcExtension runtime) { + var credentialStore = runtime.getContext().getService(CredentialStore.class); + var jsonLd = runtime.getContext().getService(JsonLd.class); + issueCredentials(issuer, participant, jsonLd).forEach(credentialStore::create); + } + + private static List issueCredentials(DataspaceIssuer issuer, IatpParticipant participant, JsonLd jsonLd) { + return List.of( + issuer.issueMembershipCredential(participant.getDid(), participant.getBpn(), jsonLd), + issuer.issueDismantlerCredential(participant.getDid(), participant.getBpn(), jsonLd), + issuer.issueFrameworkCredential(participant.getDid(), participant.getBpn(), jsonLd, "PcfCredential")); + + } +} diff --git a/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/IatpParticipant.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/IatpParticipant.java new file mode 100644 index 000000000..b4edf7502 --- /dev/null +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/IatpParticipant.java @@ -0,0 +1,112 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer.iatp.harness; + +import org.eclipse.edc.iam.did.spi.document.DidDocument; +import org.eclipse.edc.iam.did.spi.document.Service; +import org.eclipse.edc.iam.did.spi.document.VerificationMethod; +import org.eclipse.tractusx.edc.tests.participant.TractusxIatpParticipantBase; + +import java.net.URI; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.eclipse.edc.util.io.Ports.getFreePort; + +public class IatpParticipant extends TractusxIatpParticipantBase { + + protected final URI csService = URI.create("http://localhost:" + getFreePort() + "/api/resolution"); + protected URI dimUri; + + private DidDocument didDocument; + + public DidDocument getDidDocument() { + return didDocument; + } + + public String verificationId() { + return did + "#" + getKeyId(); + } + + @Override + public Map getConfiguration() { + var cfg = new HashMap<>(super.getConfiguration()); + cfg.put("web.http.resolution.port", String.valueOf(csService.getPort())); + cfg.put("web.http.resolution.path", csService.getPath()); + if (dimUri != null) { + cfg.put("edc.iam.sts.dim.url", dimUri.toString()); + } + return cfg; + } + + public static class Builder extends TractusxIatpParticipantBase.Builder { + + protected Builder() { + super(new IatpParticipant()); + } + + public static Builder newInstance() { + return new Builder(); + } + + @Override + public IatpParticipant build() { + super.build(); + participant.didDocument = generateDidDocument(); + return participant; + } + + public Builder dimUri(URI dimUri) { + participant.dimUri = dimUri; + return self(); + } + + private DidDocument generateDidDocument() { + var service = new Service(); + service.setId("#credential-service"); + service.setType("CredentialService"); + service.setServiceEndpoint(participant.csService + "/v1/participants/" + toBase64(participant.did)); + + var ecKey = participant.getKeyPairAsJwk(); + + var verificationMethod = VerificationMethod.Builder.newInstance() + .id(participant.verificationId()) + .controller(participant.did) + .type("JsonWebKey2020") + .publicKeyJwk(ecKey.toPublicJWK().toJSONObject()) + .build(); + + return DidDocument.Builder.newInstance() + .id(participant.did) + .service(List.of(service)) + .authentication(List.of("#key1")) + .verificationMethod(List.of(verificationMethod)) + .build(); + } + + + private String toBase64(String s) { + return Base64.getUrlEncoder().encodeToString(s.getBytes()); + } + + } +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/iatp/SecureTokenService.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/StsParticipant.java similarity index 62% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/iatp/SecureTokenService.java rename to edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/StsParticipant.java index 1b29f83b7..3671cb60e 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/tx/iatp/SecureTokenService.java +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/harness/StsParticipant.java @@ -17,9 +17,10 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.lifecycle.tx.iatp; +package org.eclipse.tractusx.edc.tests.transfer.iatp.harness; -import org.eclipse.tractusx.edc.lifecycle.tx.TxParticipant; + +import org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase; import java.net.URI; import java.util.Arrays; @@ -28,55 +29,65 @@ import java.util.UUID; import static java.lang.String.format; -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; +import static org.eclipse.edc.util.io.Ports.getFreePort; /** * STS configurations */ -public class SecureTokenService { +public class StsParticipant extends TractusxParticipantBase { protected final URI stsUri = URI.create("http://localhost:" + getFreePort() + "/api/v1/sts"); - protected final TxParticipant stsParticipant = TxParticipant.Builder.newInstance() - .name("STS") - .id("STS") - .build(); + + private StsParticipant() { + } public Map stsConfiguration(IatpParticipant... participants) { - var stsConfiguration = new HashMap() { - { + var stsConfiguration = new HashMap<>(super.getConfiguration()); - put("web.http.sts.port", String.valueOf(stsUri.getPort())); - put("web.http.sts.path", stsUri.getPath()); - put("edc.dataplane.token.validation.endpoint", ""); - put("tx.vault.seed.secrets", "client_secret_alias:client_secret"); - } - }; + stsConfiguration.put("web.http.sts.port", String.valueOf(stsUri.getPort())); + stsConfiguration.put("web.http.sts.path", stsUri.getPath()); + stsConfiguration.put("edc.dataplane.token.validation.endpoint", ""); + stsConfiguration.put("tx.vault.seed.secrets", "client_secret_alias:client_secret"); Arrays.stream(participants).forEach(participant -> { var prefix = format("edc.iam.sts.clients.%s", participant.getName().toLowerCase()); stsConfiguration.put(prefix + ".name", participant.getName()); stsConfiguration.put(prefix + ".id", UUID.randomUUID().toString()); stsConfiguration.put(prefix + ".client_id", participant.getBpn()); - stsConfiguration.put(prefix + ".did", participant.didUrl()); + stsConfiguration.put(prefix + ".did", participant.getDid()); stsConfiguration.put(prefix + ".secret.alias", "client_secret_alias"); stsConfiguration.put(prefix + ".private-key.alias", participant.verificationId()); stsConfiguration.put(prefix + ".public-key.reference", participant.verificationId()); }); - var baseConfiguration = stsParticipant.getConfiguration(); - stsConfiguration.putAll(baseConfiguration); return stsConfiguration; } - public String getBpn() { - return stsParticipant.getBpn(); - } - public String getName() { - return stsParticipant.getName(); + @Override + public String getFullKeyId() { + return "sts-" + getKeyId(); } + public URI stsUri() { return stsUri; } + + public static class Builder extends TractusxParticipantBase.Builder { + + protected Builder() { + super(new StsParticipant()); + } + + public static Builder newInstance() { + return new Builder(); + } + + @Override + public StsParticipant build() { + super.build(); + return participant; + } + } } diff --git a/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/runtime/IatpParticipantRuntime.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/runtime/IatpParticipantRuntime.java new file mode 100644 index 000000000..b94368687 --- /dev/null +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/runtime/IatpParticipantRuntime.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.tests.transfer.iatp.runtime; + +import com.nimbusds.jose.jwk.JWK; +import org.eclipse.edc.boot.system.injection.InjectionContainer; +import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; +import org.eclipse.edc.security.token.jwt.CryptoConverter; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.tractusx.edc.spi.identity.mapper.BdrsClient; +import org.eclipse.tractusx.edc.tests.runtimes.DataWiper; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.security.KeyPair; +import java.util.List; +import java.util.Map; + + +public class IatpParticipantRuntime extends EdcRuntimeExtension implements BeforeAllCallback, AfterAllCallback { + + private final Map properties; + private final JWK runtimeKeyPair; + private DataWiper wiper; + + public IatpParticipantRuntime(String moduleName, String runtimeName, Map properties, KeyPair runtimeKeypair) { + super(moduleName, runtimeName, properties); + this.properties = properties; + runtimeKeyPair = CryptoConverter.createJwk(runtimeKeypair); + this.registerServiceMock(BdrsClient.class, (s) -> s); + } + + @Override + public void beforeTestExecution(ExtensionContext extensionContext) { + //do nothing - we only want to start the runtime once + wiper.clearPersistence(); + } + + @Override + public void afterTestExecution(ExtensionContext context) { + } + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + //only run this once + super.beforeTestExecution(context); + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + super.afterTestExecution(context); + } + + @Override + protected void bootExtensions(ServiceExtensionContext context, List> serviceExtensions) { + super.bootExtensions(context, serviceExtensions); + wiper = new DataWiper(context); + registerConsumerPullKeys(runtimeKeyPair); + } + + private void registerConsumerPullKeys(JWK ecKey) { + var privateAlias = properties.get("edc.transfer.proxy.token.signer.privatekey.alias"); + var publicAlias = properties.get("edc.transfer.proxy.token.verifier.publickey.alias"); + + if (privateAlias != null && publicAlias != null) { + var vault = getContext().getService(Vault.class); + vault.storeSecret(privateAlias, ecKey.toJSONString()); + vault.storeSecret(publicAlias, ecKey.toPublicJWK().toJSONString()); + } + } + +} diff --git a/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/runtime/Runtimes.java b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/runtime/Runtimes.java new file mode 100644 index 000000000..8dcce0fdc --- /dev/null +++ b/edc-tests/edc-controlplane/iatp-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/iatp/runtime/Runtimes.java @@ -0,0 +1,49 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer.iatp.runtime; + +import java.security.KeyPair; +import java.util.Map; + +public interface Runtimes { + + static IatpParticipantRuntime dimRuntime(String name, Map properties, KeyPair keyPair) { + return new IatpParticipantRuntime(":edc-tests:runtime:iatp:runtime-memory-iatp-dim-ih", + name, + properties, + keyPair); + } + + static IatpParticipantRuntime iatpRuntime(String name, Map properties, KeyPair keyPair) { + return new IatpParticipantRuntime(":edc-tests:runtime:iatp:runtime-memory-iatp-ih", + name, + properties, + keyPair); + } + + static IatpParticipantRuntime stsRuntime(String name, Map properties, KeyPair keyPair) { + return new IatpParticipantRuntime( + ":edc-tests:runtime:iatp:runtime-memory-sts", + name, + properties, + keyPair); + } + +} diff --git a/edc-tests/edc-controlplane/policy-tests/build.gradle.kts b/edc-tests/edc-controlplane/policy-tests/build.gradle.kts new file mode 100644 index 000000000..d0de250c2 --- /dev/null +++ b/edc-tests/edc-controlplane/policy-tests/build.gradle.kts @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 { + `java-library` + `java-test-fixtures` +} + +dependencies { + testImplementation(testFixtures(project(":edc-tests:edc-controlplane:fixtures"))) + + testImplementation(libs.netty.mockserver) + testImplementation(libs.edc.junit) + testImplementation(libs.restAssured) + testImplementation(libs.awaitility) + testImplementation(libs.okhttp.mockwebserver) +} + +// do not publish +edcBuild { + publish.set(false) +} diff --git a/edc-tests/edc-controlplane/policy-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/PolicyMonitorEndToEndTest.java b/edc-tests/edc-controlplane/policy-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/PolicyMonitorEndToEndTest.java new file mode 100644 index 000000000..373a10ca1 --- /dev/null +++ b/edc-tests/edc-controlplane/policy-tests/src/test/java/org/eclipse/tractusx/edc/tests/policy/PolicyMonitorEndToEndTest.java @@ -0,0 +1,119 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.policy; + +import jakarta.json.Json; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.junit.annotations.PostgresqlIntegrationTest; +import org.eclipse.tractusx.edc.tests.participant.TransferParticipant; +import org.eclipse.tractusx.edc.tests.runtimes.ParticipantRuntime; +import org.eclipse.tractusx.edc.tests.runtimes.PgParticipantRuntime; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractNegotiationStates.TERMINATED; +import static org.eclipse.edc.connector.controlplane.test.system.utils.PolicyFixtures.inForceDatePolicy; +import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcessStates.STARTED; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_NAME; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_NAME; +import static org.eclipse.tractusx.edc.tests.helpers.TransferProcessHelperFunctions.createProxyRequest; +import static org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase.ASYNC_TIMEOUT; +import static org.eclipse.tractusx.edc.tests.runtimes.Runtimes.memoryRuntime; +import static org.eclipse.tractusx.edc.tests.runtimes.Runtimes.pgRuntime; + +public class PolicyMonitorEndToEndTest { + + protected static final TransferParticipant SOKRATES = TransferParticipant.Builder.newInstance() + .name(SOKRATES_NAME) + .id(SOKRATES_BPN) + .build(); + + + protected static final TransferParticipant PLATO = TransferParticipant.Builder.newInstance() + .name(PLATO_NAME) + .id(PLATO_BPN) + .build(); + + abstract static class Tests { + + @Test + void shouldTerminateTransfer_whenPolicyExpires() { + var assetId = UUID.randomUUID().toString(); + + Map dataAddress = Map.of( + "name", "transfer-test", + "baseUrl", "http://localhost:8080", + "type", "HttpData", + "contentType", "application/json", + "proxyQueryParams", "true" + ); + PLATO.createAsset(assetId, Map.of(), dataAddress); + + var policy = inForceDatePolicy("gteq", "contractAgreement+0s", "lteq", "contractAgreement+10s"); + var policyId = PLATO.createPolicyDefinition(policy); + PLATO.createContractDefinition(assetId, UUID.randomUUID().toString(), policyId, policyId); + + + var transferProcessId = SOKRATES.requestAsset(PLATO, assetId, Json.createObjectBuilder().build(), createProxyRequest(), "HttpData-PULL"); + await().atMost(ASYNC_TIMEOUT).untilAsserted(() -> { + var state = SOKRATES.getTransferProcessState(transferProcessId); + assertThat(state).isEqualTo(STARTED.name()); + }); + + await().atMost(ASYNC_TIMEOUT).untilAsserted(() -> { + var state = SOKRATES.getTransferProcessState(transferProcessId); + assertThat(state).isEqualTo(TERMINATED.name()); + }); + } + + } + + @Nested + @EndToEndTest + class InMemory extends Tests { + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = memoryRuntime(SOKRATES.getName(), SOKRATES.getBpn(), SOKRATES.getConfiguration()); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = memoryRuntime(PLATO.getName(), PLATO.getBpn(), PLATO.getConfiguration()); + + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests { + + @RegisterExtension + protected static final PgParticipantRuntime SOKRATES_RUNTIME = pgRuntime(SOKRATES.getName(), SOKRATES.getBpn(), SOKRATES.getConfiguration()); + + @RegisterExtension + protected static final PgParticipantRuntime PLATO_RUNTIME = pgRuntime(PLATO.getName(), PLATO.getBpn(), PLATO.getConfiguration()); + + } +} diff --git a/edc-tests/edc-controlplane/transfer-tests/build.gradle.kts b/edc-tests/edc-controlplane/transfer-tests/build.gradle.kts new file mode 100644 index 000000000..ddb7e022c --- /dev/null +++ b/edc-tests/edc-controlplane/transfer-tests/build.gradle.kts @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 { + `java-library` + `java-test-fixtures` +} + +dependencies { + testImplementation(project(":spi:bdrs-client-spi")) + testImplementation(testFixtures(project(":edc-tests:edc-controlplane:fixtures"))) + + testImplementation(libs.netty.mockserver) + testImplementation(libs.edc.junit) + testImplementation(libs.restAssured) + testImplementation(libs.awaitility) +} + +// do not publish +edcBuild { + publish.set(false) +} diff --git a/edc-tests/edc-controlplane/transfer-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/TransferPullEndToEndTest.java b/edc-tests/edc-controlplane/transfer-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/TransferPullEndToEndTest.java new file mode 100644 index 000000000..ddd2a983f --- /dev/null +++ b/edc-tests/edc-controlplane/transfer-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/TransferPullEndToEndTest.java @@ -0,0 +1,86 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer; + +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.junit.annotations.PostgresqlIntegrationTest; +import org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase; +import org.eclipse.tractusx.edc.tests.participant.TransferParticipant; +import org.eclipse.tractusx.edc.tests.runtimes.ParticipantRuntime; +import org.eclipse.tractusx.edc.tests.runtimes.PgParticipantRuntime; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.extension.RegisterExtension; + +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_NAME; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_NAME; +import static org.eclipse.tractusx.edc.tests.runtimes.Runtimes.memoryRuntime; +import static org.eclipse.tractusx.edc.tests.runtimes.Runtimes.pgRuntime; + +public class TransferPullEndToEndTest { + + abstract static class Tests extends HttpConsumerPullBaseTest { + + protected static final TransferParticipant SOKRATES = TransferParticipant.Builder.newInstance() + .name(SOKRATES_NAME) + .id(SOKRATES_BPN) + .build(); + protected static final TransferParticipant PLATO = TransferParticipant.Builder.newInstance() + .name(PLATO_NAME) + .id(PLATO_BPN) + .build(); + + @Override + public TractusxParticipantBase plato() { + return PLATO; + } + + @Override + public TractusxParticipantBase sokrates() { + return SOKRATES; + } + + } + + @Nested + @EndToEndTest + class InMemory extends Tests { + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = memoryRuntime(SOKRATES.getName(), SOKRATES.getBpn(), SOKRATES.getConfiguration()); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = memoryRuntime(PLATO.getName(), PLATO.getBpn(), PLATO.getConfiguration()); + + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests { + + @RegisterExtension + protected static final PgParticipantRuntime SOKRATES_RUNTIME = pgRuntime(SOKRATES.getName(), SOKRATES.getBpn(), SOKRATES.getConfiguration()); + + @RegisterExtension + protected static final PgParticipantRuntime PLATO_RUNTIME = pgRuntime(PLATO.getName(), PLATO.getBpn(), PLATO.getConfiguration()); + + } +} \ No newline at end of file diff --git a/edc-tests/edc-controlplane/transfer-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/TransferPushEndToEndTest.java b/edc-tests/edc-controlplane/transfer-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/TransferPushEndToEndTest.java new file mode 100644 index 000000000..ad9d34e71 --- /dev/null +++ b/edc-tests/edc-controlplane/transfer-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/TransferPushEndToEndTest.java @@ -0,0 +1,86 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer; + +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.junit.annotations.PostgresqlIntegrationTest; +import org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase; +import org.eclipse.tractusx.edc.tests.participant.TransferParticipant; +import org.eclipse.tractusx.edc.tests.runtimes.ParticipantRuntime; +import org.eclipse.tractusx.edc.tests.runtimes.PgParticipantRuntime; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.extension.RegisterExtension; + +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_NAME; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_NAME; +import static org.eclipse.tractusx.edc.tests.runtimes.Runtimes.memoryRuntime; +import static org.eclipse.tractusx.edc.tests.runtimes.Runtimes.pgRuntime; + +public class TransferPushEndToEndTest { + + abstract static class Tests extends ProviderPushBaseTest { + + protected static final TransferParticipant SOKRATES = TransferParticipant.Builder.newInstance() + .name(SOKRATES_NAME) + .id(SOKRATES_BPN) + .build(); + protected static final TransferParticipant PLATO = TransferParticipant.Builder.newInstance() + .name(PLATO_NAME) + .id(PLATO_BPN) + .build(); + + @Override + public TractusxParticipantBase plato() { + return PLATO; + } + + @Override + public TractusxParticipantBase sokrates() { + return SOKRATES; + } + + } + + @Nested + @EndToEndTest + class InMemory extends Tests { + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = memoryRuntime(SOKRATES.getName(), SOKRATES.getBpn(), SOKRATES.getConfiguration()); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = memoryRuntime(PLATO.getName(), PLATO.getBpn(), PLATO.getConfiguration()); + + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests { + + @RegisterExtension + protected static final PgParticipantRuntime SOKRATES_RUNTIME = pgRuntime(SOKRATES.getName(), SOKRATES.getBpn(), SOKRATES.getConfiguration()); + + @RegisterExtension + protected static final PgParticipantRuntime PLATO_RUNTIME = pgRuntime(PLATO.getName(), PLATO.getBpn(), PLATO.getConfiguration()); + + } +} \ No newline at end of file diff --git a/edc-tests/edc-controlplane/transfer-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/TransferWithTokenRefreshTest.java b/edc-tests/edc-controlplane/transfer-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/TransferWithTokenRefreshTest.java new file mode 100644 index 000000000..8867a59e9 --- /dev/null +++ b/edc-tests/edc-controlplane/transfer-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/TransferWithTokenRefreshTest.java @@ -0,0 +1,254 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.tests.transfer; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcessStates; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.junit.extensions.EdcExtension; +import org.eclipse.tractusx.edc.spi.identity.mapper.BdrsClient; +import org.eclipse.tractusx.edc.tests.participant.TransferParticipant; +import org.eclipse.tractusx.edc.tests.runtimes.ParticipantRuntime; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.verify.VerificationTimes; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.awaitility.pollinterval.FibonacciPollInterval.fibonacci; +import static org.eclipse.edc.util.io.Ports.getFreePort; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.PLATO_NAME; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_BPN; +import static org.eclipse.tractusx.edc.tests.TestRuntimeConfiguration.SOKRATES_NAME; +import static org.eclipse.tractusx.edc.tests.helpers.PolicyHelperFunctions.bnpPolicy; +import static org.eclipse.tractusx.edc.tests.helpers.TransferProcessHelperFunctions.createProxyRequest; +import static org.eclipse.tractusx.edc.tests.participant.TractusxParticipantBase.ASYNC_TIMEOUT; +import static org.eclipse.tractusx.edc.tests.runtimes.Runtimes.memoryRuntime; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +/** + * this test uses in-mem runtimes to negotiate and perform a data transfer, but the EDR expires before the consumer has a + * chance to obtain the data. + * The test asserts that the automatic token refresh mechanism renews the token, and the transfer can be completed afterward. + */ +@EndToEndTest +public class TransferWithTokenRefreshTest { + + public static final String MOCK_BACKEND_REMOTE_HOST = "localhost"; + public static final String MOCK_BACKEND_PATH = "/mock/api"; + protected static final TransferParticipant SOKRATES = TransferParticipant.Builder.newInstance() + .name(SOKRATES_NAME) + .id(SOKRATES_BPN) + .build(); + protected static final TransferParticipant PLATO = TransferParticipant.Builder.newInstance() + .name(PLATO_NAME) + .id(PLATO_BPN) + .build(); + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = memoryRuntime(SOKRATES.getName(), SOKRATES.getBpn(), SOKRATES.getConfiguration()); + private static final Long VERY_SHORT_TOKEN_EXPIRY = 3L; + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = memoryRuntime(PLATO.getName(), PLATO.getBpn(), forConfig(PLATO.getConfiguration()), TransferWithTokenRefreshTest::platoInitiator); + protected ClientAndServer server; + private String privateBackendUrl; + + + private static Map forConfig(Map originalConfig) { + var newConfig = new HashMap<>(originalConfig); + newConfig.put("edc.dataplane.token.expiry", String.valueOf(VERY_SHORT_TOKEN_EXPIRY)); + newConfig.put("edc.dataplane.token.expiry.tolerance", "0"); + return newConfig; + } + + private static void platoInitiator(EdcExtension runtime) { + runtime.registerServiceMock(BdrsClient.class, (c) -> SOKRATES.getDid()); + } + + @BeforeEach + void setup() { + server = ClientAndServer.startClientAndServer(MOCK_BACKEND_REMOTE_HOST, getFreePort()); + privateBackendUrl = "http://%s:%d%s".formatted(MOCK_BACKEND_REMOTE_HOST, server.getPort(), MOCK_BACKEND_PATH); + } + + @Test + void transferData_withExpiredEdr_shouldReturn4xx() { + var assetId = "api-asset-1"; + + Map dataAddress = Map.of( + "baseUrl", privateBackendUrl, + "type", "HttpData", + "contentType", "application/json" + ); + + PLATO.createAsset(assetId, Map.of(), dataAddress); + + var accessPolicyId = PLATO.createPolicyDefinition(createAccessPolicy(SOKRATES.getBpn())); + var contractPolicyId = PLATO.createPolicyDefinition(createContractPolicy(SOKRATES.getBpn())); + PLATO.createContractDefinition(assetId, "def-1", accessPolicyId, contractPolicyId); + var transferProcessId = SOKRATES.requestAsset(PLATO, assetId, Json.createObjectBuilder().build(), createProxyRequest(), "HttpData-PULL"); + + var edr = new AtomicReference(); + + // wait until transfer process completes + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var tpState = SOKRATES.getTransferProcessState(transferProcessId); + assertThat(tpState).isNotNull().isEqualTo(TransferProcessStates.STARTED.toString()); + }); + + // wait until EDC is available on the consumer side + server.when(request().withMethod("GET").withPath(MOCK_BACKEND_PATH)).respond(response().withStatusCode(200).withBody("test response")); + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + edr.set(SOKRATES.edrs().getEdr(transferProcessId)); + assertThat(edr).isNotNull(); + }); + + + // wait until the EDR expires + await().pollDelay(Duration.ofSeconds(VERY_SHORT_TOKEN_EXPIRY + 1)) + .pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var endpoint = edr.get().getString("endpoint"); + var token = edr.get().getString("authorization"); + given() + .baseUri(endpoint) + .header("Authorization", token) + .when() + .get() + .then() + .statusCode(403); + }); + + // assert the data has not been fetched + server.verify(request().withPath(MOCK_BACKEND_PATH), VerificationTimes.never()); + + // renew EDR explicitly + var renewedEdr = SOKRATES.edrs().refreshEdr(transferProcessId) + .statusCode(200) + .extract().body() + .as(JsonObject.class); + + // make sure the consumer has now been able to fetch the data. + var data = SOKRATES.data().pullData(renewedEdr, Map.of()); + assertThat(data).isNotNull().isEqualTo("test response"); + + server.verify(request().withPath(MOCK_BACKEND_PATH), VerificationTimes.exactly(1)); + } + + @Test + void transferData_withAutomaticRefresh() { + var assetId = "api-asset-1"; + + Map dataAddress = Map.of( + "baseUrl", privateBackendUrl, + "type", "HttpData", + "contentType", "application/json" + ); + + PLATO.createAsset(assetId, Map.of(), dataAddress); + + var accessPolicyId = PLATO.createPolicyDefinition(createAccessPolicy(SOKRATES.getBpn())); + var contractPolicyId = PLATO.createPolicyDefinition(createContractPolicy(SOKRATES.getBpn())); + PLATO.createContractDefinition(assetId, "def-1", accessPolicyId, contractPolicyId); + var transferProcessId = SOKRATES.requestAsset(PLATO, assetId, Json.createObjectBuilder().build(), createProxyRequest(), "HttpData-PULL"); + + var edr = new AtomicReference(); + + // wait until transfer process completes + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var tpState = SOKRATES.getTransferProcessState(transferProcessId); + assertThat(tpState).isNotNull().isEqualTo(TransferProcessStates.STARTED.toString()); + }); + + // wait until EDC is available on the consumer side + server.when(request().withMethod("GET").withPath(MOCK_BACKEND_PATH)).respond(response().withStatusCode(200).withBody("test response")); + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + edr.set(SOKRATES.edrs().getEdr(transferProcessId)); + assertThat(edr).isNotNull(); + }); + + + // wait until the EDR expires + await().pollDelay(Duration.ofSeconds(VERY_SHORT_TOKEN_EXPIRY + 1)) + .pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var endpoint = edr.get().getString("endpoint"); + var token = edr.get().getString("authorization"); + given() + .baseUri(endpoint) + .header("Authorization", token) + .when() + .get() + .then() + .statusCode(403); + }); + + // assert the data has not been fetched + server.verify(request().withPath(MOCK_BACKEND_PATH), VerificationTimes.never()); + + // get EDR with automatic refresh + var renewedEdr = SOKRATES.edrs().getEdrWithRefresh(transferProcessId, true) + .statusCode(200) + .extract().body() + .as(JsonObject.class); + + // make sure the consumer has now been able to fetch the data. + var data = SOKRATES.data().pullData(renewedEdr, Map.of()); + assertThat(data).isNotNull().isEqualTo("test response"); + + server.verify(request().withPath(MOCK_BACKEND_PATH), VerificationTimes.exactly(1)); + } + + @AfterEach + void teardown() { + server.stop(); + } + + protected JsonObject createAccessPolicy(String bpn) { + return bnpPolicy(bpn); + } + + protected JsonObject createContractPolicy(String bpn) { + return bnpPolicy(bpn); + } +} diff --git a/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/AzureToAzureTest.java b/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/AzureToAzureTest.java index ba23e280c..ad62b71cd 100644 --- a/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/AzureToAzureTest.java +++ b/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/AzureToAzureTest.java @@ -21,7 +21,7 @@ import io.restassured.http.ContentType; import org.eclipse.edc.azure.testfixtures.annotations.AzureStorageIntegrationTest; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; +import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -37,7 +37,7 @@ import static io.restassured.RestAssured.given; import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; +import static org.eclipse.edc.util.io.Ports.getFreePort; import static org.eclipse.tractusx.edc.dataplane.transfer.test.TestConstants.AZBLOB_CONSUMER_ACCOUNT_KEY; import static org.eclipse.tractusx.edc.dataplane.transfer.test.TestConstants.AZBLOB_CONSUMER_ACCOUNT_NAME; import static org.eclipse.tractusx.edc.dataplane.transfer.test.TestConstants.AZBLOB_CONSUMER_CONTAINER_NAME; @@ -59,7 +59,7 @@ /** * This test is intended to verify transfers within the same cloud provider, i.e. AzureBlob-to-AzureBlob. - * It spins up a fully-fledged dataplane and issues the DataFlowRequest via the data plane's Control API + * It spins up a fully-fledged dataplane and issues the DataFlowStartMessage via the data plane's Control API */ @SuppressWarnings("resource") @Testcontainers @@ -142,7 +142,7 @@ void transferFile_success() { */ @ParameterizedTest(name = "File size bytes: {0}") // 1mb, 512mb, 1gb - @ValueSource(longs = {1024 * 1024 * 512, 1024L * 1024L * 1024L, /*1024L * 1024L * 1024L * 1024 takes extremely long!*/}) + @ValueSource(longs = { 1024 * 1024 * 512, 1024L * 1024L * 1024L, /*1024L * 1024L * 1024L * 1024 takes extremely long!*/ }) void transferFile_largeFile(long sizeBytes) throws IOException { // upload file to provider's blob store var bcc = providerBlobHelper.createContainer(AZBLOB_PROVIDER_CONTAINER_NAME); @@ -216,13 +216,12 @@ void transferFile_targetContainerNotExist_shouldFail() { .severe(contains("Error creating blob %s on account %s".formatted(TESTFILE_NAME, AZBLOB_CONSUMER_ACCOUNT_NAME)), isA(IOException.class))); } - private DataFlowRequest createFlowRequest(String blobName) { - return DataFlowRequest.Builder.newInstance() + private DataFlowStartMessage createFlowRequest(String blobName) { + return DataFlowStartMessage.Builder.newInstance() .id("test-request") .sourceDataAddress(blobSourceAddress(blobName)) .destinationDataAddress(blobDestinationAddress(blobName)) .processId("test-process-id") - .trackable(false) .build(); } diff --git a/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/MultiCloudTest.java b/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/MultiCloudTest.java index 157599195..fda365150 100644 --- a/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/MultiCloudTest.java +++ b/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/MultiCloudTest.java @@ -28,7 +28,7 @@ import org.eclipse.edc.azure.testfixtures.annotations.AzureStorageIntegrationTest; import org.eclipse.edc.junit.testfixtures.TestUtils; import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; +import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -48,7 +48,7 @@ import static io.restassured.RestAssured.given; import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; +import static org.eclipse.edc.util.io.Ports.getFreePort; import static org.eclipse.tractusx.edc.dataplane.transfer.test.TestConstants.AZBLOB_CONSUMER_ACCOUNT_KEY; import static org.eclipse.tractusx.edc.dataplane.transfer.test.TestConstants.AZBLOB_CONSUMER_ACCOUNT_NAME; import static org.eclipse.tractusx.edc.dataplane.transfer.test.TestConstants.AZBLOB_CONSUMER_CONTAINER_NAME; @@ -126,7 +126,7 @@ void transferFile_azureToS3() { assertThat(r.sdkHttpResponse().isSuccessful()).isTrue(); // create data flow request - var dfr = DataFlowRequest.Builder.newInstance() + var dfr = DataFlowStartMessage.Builder.newInstance() .id("test-request") .sourceDataAddress(DataAddress.Builder.newInstance() .type("AzureStorage") @@ -147,7 +147,6 @@ void transferFile_azureToS3() { .build() ) .processId("test-process-id") - .trackable(false) .build(); var url = "http://localhost:%s/control/transfer".formatted(PROVIDER_CONTROL_PORT); @@ -185,7 +184,7 @@ void transferFile_s3ToAzure() { // create data flow request - var dfr = DataFlowRequest.Builder.newInstance() + var dfr = DataFlowStartMessage.Builder.newInstance() .id("test-request") .sourceDataAddress(DataAddress.Builder.newInstance() .type(S3BucketSchema.TYPE) @@ -199,7 +198,6 @@ void transferFile_s3ToAzure() { ) .destinationDataAddress(blobDestinationAddress(TESTFILE_NAME)) .processId("test-process-id") - .trackable(false) .build(); var url = "http://localhost:%s/control/transfer".formatted(PROVIDER_CONTROL_PORT); diff --git a/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/ParticipantRuntime.java b/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/ParticipantRuntime.java index 49d9efbb1..a3662b2b0 100644 --- a/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/ParticipantRuntime.java +++ b/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/ParticipantRuntime.java @@ -19,13 +19,13 @@ package org.eclipse.tractusx.edc.dataplane.transfer.test; +import org.eclipse.edc.boot.system.injection.InjectionContainer; import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; import org.eclipse.edc.spi.monitor.ConsoleMonitor; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.security.Vault; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.injection.InjectionContainer; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; diff --git a/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/RuntimeConfig.java b/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/RuntimeConfig.java index 896131a74..dbf85bb19 100644 --- a/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/RuntimeConfig.java +++ b/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/RuntimeConfig.java @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Map; -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; +import static org.eclipse.edc.util.io.Ports.getFreePort; /** * Configuration baseline for Data-Plane e2e tests diff --git a/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/S3ToS3Test.java b/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/S3ToS3Test.java index 976547dad..e4b18e4ba 100644 --- a/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/S3ToS3Test.java +++ b/edc-tests/edc-dataplane/cloud-transfer-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/transfer/test/S3ToS3Test.java @@ -27,7 +27,7 @@ import org.eclipse.edc.aws.s3.testfixtures.annotations.AwsS3IntegrationTest; import org.eclipse.edc.junit.testfixtures.TestUtils; import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; +import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -54,7 +54,7 @@ import static io.restassured.RestAssured.given; import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; +import static org.eclipse.edc.util.io.Ports.getFreePort; import static org.eclipse.tractusx.edc.dataplane.transfer.test.TestConstants.MINIO_CONTAINER_PORT; import static org.eclipse.tractusx.edc.dataplane.transfer.test.TestConstants.MINIO_DOCKER_IMAGE; import static org.eclipse.tractusx.edc.dataplane.transfer.test.TestConstants.S3_ACCESS_KEY_ID; @@ -71,7 +71,7 @@ /** * This test is intended to verify transfers within the same cloud provider, i.e. S3-to-S3. - * It spins up a fully-fledged dataplane and issues the DataFlowRequest via the data plane's Control API + * It spins up a fully-fledged dataplane and issues the DataFlowStartMessage via the data plane's Control API */ @Testcontainers @AwsS3IntegrationTest @@ -178,13 +178,13 @@ void transferFile_targetContainerNotExist_shouldFail() { // wait until the data plane logs an exception that it cannot transfer the file await().pollInterval(Duration.ofSeconds(2)) .atMost(Duration.ofSeconds(10)) - .untilAsserted(() -> verify(DATAPLANE_RUNTIME.getContext().getMonitor()).severe(startsWith("Error writing the %s object on the %s bucket: The specified bucket does not exist".formatted(TESTFILE_NAME, S3_CONSUMER_BUCKET_NAME)), + .untilAsserted(() -> verify(DATAPLANE_RUNTIME.getContext().getMonitor()).severe(startsWith("Failed to upload the %s object: The specified bucket does not exist".formatted(TESTFILE_NAME)), isA(NoSuchBucketException.class))); } @ParameterizedTest(name = "File size bytes: {0}") // 1mb, 512mb, 1gb - @ValueSource(longs = {1024 * 1024 * 512, 1024L * 1024L * 1024L, 1024L * 1024L * 1024L * 1024}) + @ValueSource(longs = { 1024 * 1024 * 512, 1024L * 1024L * 1024L, 1024L * 1024L * 1024L * 1024 }) void transferfile_largeFile(long sizeBytes) { // create large sparse file @@ -244,8 +244,8 @@ private CompletableFuture uploadLargeFile(File file, String buc } - private DataFlowRequest createFlowRequest() { - return DataFlowRequest.Builder.newInstance() + private DataFlowStartMessage createFlowRequest() { + return DataFlowStartMessage.Builder.newInstance() .id("test-request") .sourceDataAddress(DataAddress.Builder.newInstance() .type(S3BucketSchema.TYPE) @@ -268,7 +268,6 @@ private DataFlowRequest createFlowRequest() { .build() ) .processId("test-process-id") - .trackable(false) .build(); } diff --git a/edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/DpfProxyEndToEndTest.java b/edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/DpfProxyEndToEndTest.java deleted file mode 100644 index d98c663c2..000000000 --- a/edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/DpfProxyEndToEndTest.java +++ /dev/null @@ -1,209 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.e2e; - -import io.restassured.specification.RequestSpecification; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import static io.restassured.RestAssured.given; -import static java.lang.String.format; -import static java.lang.String.valueOf; -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; -import static org.eclipse.tractusx.edc.dataplane.proxy.e2e.EdrCacheSetup.createEntries; -import static org.eclipse.tractusx.edc.dataplane.proxy.e2e.KeyStoreSetup.createKeyStore; -import static org.hamcrest.Matchers.is; - - -/** - * Performs end-to-end testing using a consumer data plane, a producer data plane, and a proxied HTTP endpoint. - *

- * The consumer runtime is configured with three EDRs: - *

    - *
  • One EDR is for the {@link #SINGLE_TRANSFER_ID} transfer process that is associated with a single contract agreement for the {@link #SINGLE_ASSET_ID} - * asset
  • - *
  • Two EDRs for transfer processes that are associated with contract agreements for the same asset, {@link #MULTI_ASSET_ID}
  • - *
- *

- * The end-to-end tests verify asset content is correctly proxied from the HTTP endpoint, error messages from the HTTP endpoint are correctly propagated, - * and invalid requests are properly handled. - *

- * This test can be executed using the Gradle or JUnit test runners. - */ -@EndToEndTest -public class DpfProxyEndToEndTest { - - public static final String KEYSTORE_PASS = "test123"; - private static final String LAUNCHER_MODULE = ":edc-tests:edc-dataplane:edc-dataplane-proxy-e2e"; - private static final int CONSUMER_HTTP_PORT = getFreePort(); - private static final int CONSUMER_PROXY_PORT = getFreePort(); - private static final int PRODUCER_HTTP_PORT = getFreePort(); - private static final int MOCK_ENDPOINT_PORT = getFreePort(); - - private static final int VALIDATION_ENDPOINT_PORT = getFreePort(); - - private static final String PROXY_SUBPATH = "proxy/aas/request"; - private static final String SINGLE_TRANSFER_ID = "5355d524-2616-43df-9096-558afffff659"; - private static final String SINGLE_ASSET_ID = "79f13b89-59a6-4278-8c8e-8540849dbab8"; - private static final String MULTI_ASSET_ID = "9260f395-3d94-4b8b-bdaa-941ead596ce5"; - private static final String REQUEST_TEMPLATE_TP = "{\"transferProcessId\": \"%s\", \"endpointUrl\" : \"http://localhost:%s/api/gateway/aas/test\"}"; - private static final String REQUEST_TEMPLATE_ASSET = "{\"assetId\": \"%s\", \"endpointUrl\" : \"http://localhost:%s/api/gateway/aas/test\"}"; - private static final String MOCK_ENDPOINT_200_BODY = "{\"message\":\"test\"}"; - private static final String API_KEY = "testkey"; - - @RegisterExtension - static EdcRuntimeExtension consumer = new EdcRuntimeExtension( - LAUNCHER_MODULE, - "consumer", - baseConfig(Map.of( - "web.http.port", valueOf(CONSUMER_HTTP_PORT), - "edc.api.auth.key", API_KEY, - "tx.dpf.consumer.proxy.port", valueOf(CONSUMER_PROXY_PORT) - ))); - @RegisterExtension - static EdcRuntimeExtension provider = new EdcRuntimeExtension( - LAUNCHER_MODULE, - "provider", - baseConfig(Map.of( - "web.http.port", valueOf(PRODUCER_HTTP_PORT), - "tx.dpf.proxy.gateway.aas.proxied.path", "http://localhost:" + MOCK_ENDPOINT_PORT - ))); - private static MockWebServer mockEndpoint = new MockWebServer(); - private static MockWebServer mockValidationEndpoint = new MockWebServer(); - private final TypeManager typeManager = new TypeManager(); - - @Test - void verify_end2EndFlows() throws IOException { - - seedEdrCache(); - - var dataAddress = HttpDataAddress.Builder.newInstance().baseUrl("http://localhost:" + MOCK_ENDPOINT_PORT).build(); - - mockValidationEndpoint.start(VALIDATION_ENDPOINT_PORT); - mockValidationEndpoint.enqueue(new MockResponse().setBody(typeManager.writeValueAsString(dataAddress))); - mockValidationEndpoint.enqueue(new MockResponse().setBody(typeManager.writeValueAsString(dataAddress))); - mockValidationEndpoint.enqueue(new MockResponse().setBody(typeManager.writeValueAsString(dataAddress))); - mockValidationEndpoint.enqueue(new MockResponse().setBody(typeManager.writeValueAsString(dataAddress))); - - // set up the HTTP endpoint - mockEndpoint.enqueue(new MockResponse().setBody(MOCK_ENDPOINT_200_BODY)); - mockEndpoint.enqueue(new MockResponse().setBody(MOCK_ENDPOINT_200_BODY)); - mockEndpoint.enqueue(new MockResponse().setResponseCode(404)); - mockEndpoint.enqueue(new MockResponse().setResponseCode(401)); - mockEndpoint.start(MOCK_ENDPOINT_PORT); - - var tpSpec = createSpecification(format(REQUEST_TEMPLATE_TP, SINGLE_TRANSFER_ID, PRODUCER_HTTP_PORT)); - - // verify content successfully proxied using a transfer process id - var rs = tpSpec.with() - .post(PROXY_SUBPATH) - .then() - .log().ifError() - .assertThat().statusCode(200) - .body(is(MOCK_ENDPOINT_200_BODY)); - - var str = rs.extract().body().asString(); - - // verify content successfully proxied using an asset id for the case where only one active transfer process exists for the asset - var assetSpec = createSpecification(format(REQUEST_TEMPLATE_ASSET, SINGLE_ASSET_ID, PRODUCER_HTTP_PORT)); - assetSpec.with() - .post(PROXY_SUBPATH) - .then() - .assertThat().statusCode(200) - .assertThat().body(is(MOCK_ENDPOINT_200_BODY)); - - // verify content not found (404) response at the endpoint is propagated - tpSpec.with() - .post(PROXY_SUBPATH) - .then() - .assertThat().statusCode(404); - - // verify unauthorized response (403) at the endpoint is propagated - tpSpec.with() - .post(PROXY_SUBPATH) - .then() - .assertThat().statusCode(401); - - // verify EDR not found results in a bad request response (400) - var invalidSpec = createSpecification(format(REQUEST_TEMPLATE_TP, "123", PRODUCER_HTTP_PORT)); - invalidSpec.with() - .post(PROXY_SUBPATH) - .then() - .assertThat().statusCode(400); - - // verify more than one contract for the same asset results in a precondition required response (428) - var multiAssetSpec = createSpecification(format(REQUEST_TEMPLATE_ASSET, MULTI_ASSET_ID, PRODUCER_HTTP_PORT)); - multiAssetSpec.with() - .post(PROXY_SUBPATH) - .then() - .assertThat().statusCode(428); - } - - private static Map baseConfig(Map values) { - var map = new HashMap<>(values); - map.put("edc.keystore", createKeyStore(KEYSTORE_PASS)); - map.put("edc.keystore.password", KEYSTORE_PASS); - map.put("edc.dataplane.token.validation.endpoint", "http://localhost:" + VALIDATION_ENDPOINT_PORT); - return map; - } - - private RequestSpecification createSpecification(String body) { - return given() - .baseUri("http://localhost:" + CONSUMER_PROXY_PORT) - .contentType("application/json") - .header("x-api-key", API_KEY) - .body(body); - } - - /** - * Loads the EDR cache. - */ - private void seedEdrCache() { - var edrCache = consumer.getContext().getService(EndpointDataReferenceCache.class); - createEntries().forEach(e -> edrCache.save(e.getEdrEntry(), e.getEdr())); - } - - @AfterAll - static void tearDown() throws IOException { - mockEndpoint.shutdown(); - mockValidationEndpoint.shutdown(); - } - - @BeforeAll - static void setUp() { - mockEndpoint = new MockWebServer(); - mockValidationEndpoint = new MockWebServer(); - } - - -} diff --git a/edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/EdrCacheSetup.java b/edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/EdrCacheSetup.java deleted file mode 100644 index 3fcb83976..000000000 --- a/edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/EdrCacheSetup.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.e2e; - -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.core.defaults.PersistentCacheEntry; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; - -import java.util.ArrayList; -import java.util.List; - -/** - * Creates test EDR cache entries. - */ -public class EdrCacheSetup { - - public static final String AUTHENTICATION = "authentication"; - public static final String ENDPOINT = "http://test.com"; - - private static String generateAuthCode() { - //noinspection StringBufferReplaceableByString - return new StringBuilder() - .append("eyJhbGciOiJSUzI1NiIsInZlcn") - .append("Npb24iOnRydWV9.") - .append("eyJpc3MiOiJ0ZXN0LWNvb") - .append("m5lY3RvciIsInN1YiI6ImNvbnN1bW") - .append("VyLWNvbm5lY3RvciIsImF1ZCI6InRlc3Q") - .append("tY29ubmVjdG9yIiwi") - .append("aWF0IjoxNjgxOTEzN") - .append("jM2LCJleHAiOjMzNDU5NzQwNzg4LCJjaWQiOiIzMmE2M") - .append("2E3ZC04MGQ2LTRmMmUtOTBlN") - .append("i04MGJhZjVmYzJiM2MifQ.QAuotoRxpEqfuzkTcTq2w5Tcyy") - .append("3Rc3UzUjjvNc_zwgNROGLe-wO") - .append("9tFET1dJ_I5BttRxkngDS37dS4R6lN5YXaGHgcH2rf_FuVcJUS") - .append("FqTp_usGAcx6m7pQQwqpNdcYgmq0NJp3xP87EFP") - .append("HAy4kBxB5bqpmx4J-zrj9U_gerZ2WlRqpu0SdgP0S5v5D1Gm-v") - .append("YkLqgvsugrAWH3Ti7OjC5UMdj0kDFwro2NpMY8SSNryiVvBEv8hn0KZdhhebIqPd") - .append("hqbEQZ9d8WKzcgoqQ3DBd4ijzkd3Fz7ADD2gy_Hxn8Hi2LcItuB514TjCxYA") - .append("ncTNqZC_JSFEyuxwcGFVz3LdSXgw") - .toString(); - } - - public static List createEntries() { - var list = new ArrayList(); - - var edrEntry = EndpointDataReferenceEntry.Builder.newInstance() - .assetId("79f13b89-59a6-4278-8c8e-8540849dbab8") - .agreementId("a62d02a3-eea5-4852-86d4-5482db4dffe8") - .transferProcessId("5355d524-2616-43df-9096-558afffff659") - .build(); - var edr = EndpointDataReference.Builder.newInstance() - .id("c470e649-5454-4e4d-b065-782752e5d759") - .endpoint(ENDPOINT) - .authKey(AUTHENTICATION) - .authCode(generateAuthCode()) - .contractId("test-contract-id") - .build(); - list.add(new PersistentCacheEntry(edrEntry, edr)); - - var edrEntry2 = EndpointDataReferenceEntry.Builder.newInstance() - .assetId("9260f395-3d94-4b8b-bdaa-941ead596ce5") - .agreementId("d6f73f25-b0aa-4b62-843f-7cfaba532b5b8") - .transferProcessId("b2859c0a-1a4f-4d10-a3fd-9652d7b3469a") - .build(); - var edr2 = EndpointDataReference.Builder.newInstance() - .id("514a4142-3d2a-4936-97c3-7892961c6a58") - .endpoint(ENDPOINT) - .authKey(AUTHENTICATION) - .authCode(generateAuthCode()) - .contractId("test-contract-id") - .build(); - list.add(new PersistentCacheEntry(edrEntry2, edr2)); - - var edrEntry3 = EndpointDataReferenceEntry.Builder.newInstance() - .assetId("9260f395-3d94-4b8b-bdaa-941ead596ce5") - .agreementId("7a23333b-03b5-4547-822b-595a54ad6d38") - .transferProcessId("7a23333b-03b5-4547-822b-595a54ad6d38") - .build(); - var edr3 = EndpointDataReference.Builder.newInstance() - .id("3563c5a1-685d-40e5-a380-0b5761523d2d") - .endpoint(ENDPOINT) - .contractId("test-contract-id") - .authKey(AUTHENTICATION) - .authCode(generateAuthCode()) - .build(); - - list.add(new PersistentCacheEntry(edrEntry3, edr3)); - - - return list; - } -} - diff --git a/edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/KeyStoreSetup.java b/edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/KeyStoreSetup.java deleted file mode 100644 index bbddb523b..000000000 --- a/edc-tests/edc-dataplane/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/KeyStoreSetup.java +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.dataplane.proxy.e2e; - -import java.io.File; -import java.io.FileOutputStream; -import java.security.KeyStore; - -/** - * Sets up a test keystore. - */ -public class KeyStoreSetup { - - public static String createKeyStore(String password) { - try { - var ks = KeyStore.getInstance(KeyStore.getDefaultType()); - - ks.load(null, password.toCharArray()); - - var file = File.createTempFile("test", "-keystore.jks"); - try (var fos = new FileOutputStream(file)) { - ks.store(fos, password.toCharArray()); - } - file.deleteOnExit(); - return file.getAbsolutePath(); - } catch (Exception e) { - throw new AssertionError(e); - } - } - - private KeyStoreSetup() { - } -} diff --git a/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/build.gradle.kts b/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/build.gradle.kts new file mode 100644 index 000000000..cd5b341a9 --- /dev/null +++ b/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/build.gradle.kts @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 { + `java-library` +} + +dependencies { + + testImplementation(project(":spi:tokenrefresh-spi")) + testImplementation(project(":edc-tests:e2e-tests")) + testImplementation(libs.edc.junit) + testImplementation(libs.restAssured) + + testImplementation(project(":spi:core-spi")) + testImplementation(libs.edc.dpf.http) + testImplementation(libs.edc.spi.identity.did) + testImplementation(libs.nimbus.jwt) +} + + + diff --git a/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/e2e/DataPlaneTokenRefreshEndToEndTest.java b/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/e2e/DataPlaneTokenRefreshEndToEndTest.java new file mode 100644 index 000000000..ff888c8fc --- /dev/null +++ b/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/e2e/DataPlaneTokenRefreshEndToEndTest.java @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * 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.edc.dataplane.tokenrefresh.e2e; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAuthorizationService; +import org.eclipse.edc.iam.did.spi.resolution.DidPublicKeyResolver; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; +import org.eclipse.edc.spi.types.domain.transfer.FlowType; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EmptySource; +import org.junit.jupiter.params.provider.NullSource; + +import java.net.URI; +import java.text.ParseException; +import java.util.Map; + +import static org.apache.http.HttpHeaders.AUTHORIZATION; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.AUDIENCE_PROPERTY; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_AUTH_NS; +import static org.hamcrest.Matchers.containsString; + + +@EndToEndTest +public class DataPlaneTokenRefreshEndToEndTest { + public static final String CONSUMER_DID = "did:web:alice"; + public static final String PROVIDER_DID = "did:web:bob"; + public static final String PROVIDER_KEY_ID = PROVIDER_DID + "#key-1"; + public static final String PROVIDER_KEY_ID_PUBLIC = PROVIDER_DID + "#key-1-public"; + public static final String CONSUMER_KEY_ID = CONSUMER_DID + "#cons-1"; + private static final RuntimeConfig RUNTIME_CONFIG = new RuntimeConfig(); + @RegisterExtension + protected static final EdcRuntimeExtension DATAPLANE_RUNTIME = new EdcRuntimeExtension( + ":edc-tests:runtime:dataplane-cloud", + "Token-Refresh-Dataplane", + with(RUNTIME_CONFIG.baseConfig(), Map.of("edc.transfer.proxy.token.signer.privatekey.alias", PROVIDER_KEY_ID, + "edc.transfer.proxy.token.verifier.publickey.alias", PROVIDER_KEY_ID_PUBLIC)) + ); + private ECKey providerKey; + private ECKey consumerKey; + + private static Map with(Map baseConfig, Map additionalConfig) { + baseConfig.putAll(additionalConfig); + return baseConfig; + } + + + @BeforeEach + void setup() throws JOSEException { + providerKey = new ECKeyGenerator(Curve.P_384).keyID(PROVIDER_KEY_ID).generate(); + consumerKey = new ECKeyGenerator(Curve.P_256).keyID(CONSUMER_KEY_ID).generate(); + + // mock the did resolver, hard-wire it to the provider or consumer DID + DATAPLANE_RUNTIME.registerServiceMock(DidPublicKeyResolver.class, s -> { + try { + if (s.startsWith(CONSUMER_DID)) { + return Result.success(consumerKey.toPublicKey()); + } else if (s.startsWith(PROVIDER_DID)) { + return Result.success(providerKey.toPublicKey()); + } + throw new IllegalArgumentException("DID '%s' could not be resolved.".formatted(s)); + } catch (JOSEException e) { + throw new RuntimeException(e); + } + }); + } + + @DisplayName("Refresh token success") + @Test + void refresh_success() { + + // register generator and secrets + prepareDataplaneRuntime(); + + var authorizationService = DATAPLANE_RUNTIME.getService(DataPlaneAuthorizationService.class); + var edr = authorizationService.createEndpointDataReference(createStartMessage("test-process-id", CONSUMER_DID)) + .orElseThrow(f -> new AssertionError(f.getFailureDetail())); + + var refreshToken = edr.getStringProperty(TX_AUTH_NS + "refreshToken"); + var accessToken = edr.getStringProperty(EDC_NAMESPACE + "authorization"); + var authToken = createAuthToken(accessToken, consumerKey); + + var tokenResponse = RUNTIME_CONFIG.getRefreshApi().baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", refreshToken) + .header(AUTHORIZATION, "Bearer " + authToken) + .post("/token") + .then() + .log().ifError() + .statusCode(200) + .extract().body().as(TokenResponse.class); + + assertThat(tokenResponse).isNotNull(); + } + + @DisplayName("Refresh token is null or empty (missing)") + @ParameterizedTest + @NullSource + @EmptySource + void refresh_invalidRefreshToken(String invalidRefreshToken) { + // register generator and secrets + prepareDataplaneRuntime(); + + var authorizationService = DATAPLANE_RUNTIME.getService(DataPlaneAuthorizationService.class); + var edr = authorizationService.createEndpointDataReference(createStartMessage("test-process-id", CONSUMER_DID)) + .orElseThrow(f -> new AssertionError(f.getFailureDetail())); + + var refreshToken = edr.getStringProperty(TX_AUTH_NS + "refreshToken"); + var accessToken = edr.getStringProperty(EDC_NAMESPACE + "authorization"); + var authToken = createAuthToken(accessToken, consumerKey); + + RUNTIME_CONFIG.getRefreshApi().baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", invalidRefreshToken) + .header(AUTHORIZATION, "Bearer " + authToken) + .post("/token") + .then() + .log().ifError() + .statusCode(400); + } + + @DisplayName("The Authorization header is empty") + @Test + void refresh_emptyAuthHeader() { + // register generator and secrets + prepareDataplaneRuntime(); + + var authorizationService = DATAPLANE_RUNTIME.getService(DataPlaneAuthorizationService.class); + var edr = authorizationService.createEndpointDataReference(createStartMessage("test-process-id", CONSUMER_DID)) + .orElseThrow(f -> new AssertionError(f.getFailureDetail())); + + var refreshToken = edr.getStringProperty(TX_AUTH_NS + "refreshToken"); + var accessToken = edr.getStringProperty(EDC_NAMESPACE + "authorization"); + + // auth header is empty + RUNTIME_CONFIG.getRefreshApi().baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", refreshToken) + .header(AUTHORIZATION, "") + .post("/token") + .then() + .log().ifError() + .statusCode(401); + } + + @DisplayName("The Authorization header is missing") + @Test + void refresh_missingAuthHeader() { + // register generator and secrets + prepareDataplaneRuntime(); + + var authorizationService = DATAPLANE_RUNTIME.getService(DataPlaneAuthorizationService.class); + var edr = authorizationService.createEndpointDataReference(createStartMessage("test-process-id", CONSUMER_DID)) + .orElseThrow(f -> new AssertionError(f.getFailureDetail())); + + var refreshToken = edr.getStringProperty(TX_AUTH_NS + "refreshToken"); + var accessToken = edr.getStringProperty(EDC_NAMESPACE + "authorization"); + + // auth header is empty + RUNTIME_CONFIG.getRefreshApi().baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", refreshToken) + .post("/token") + .then() + .log().ifError() + .statusCode(401); + } + + @DisplayName("The sign key of the authentication token is different from the public key from the DID") + @Test + void refresh_spoofedAuthToken() throws JOSEException { + prepareDataplaneRuntime(); + + var authorizationService = DATAPLANE_RUNTIME.getService(DataPlaneAuthorizationService.class); + var edr = authorizationService.createEndpointDataReference(createStartMessage("test-process-id", CONSUMER_DID)) + .orElseThrow(f -> new AssertionError(f.getFailureDetail())); + + var refreshToken = edr.getStringProperty(TX_AUTH_NS + "refreshToken"); + var accessToken = edr.getStringProperty(EDC_NAMESPACE + "authorization"); + var spoofedKey = new ECKeyGenerator(Curve.P_256).keyID(CONSUMER_KEY_ID).generate(); + var authTokenWithSpoofedKey = createAuthToken(accessToken, spoofedKey); + + RUNTIME_CONFIG.getRefreshApi().baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", refreshToken) + .header(AUTHORIZATION, "Bearer " + authTokenWithSpoofedKey) + .post("/token") + .then() + .log().ifValidationFails() + .statusCode(401) + .body(containsString("Token verification failed")); + } + + @DisplayName("The refresh token does not match the stored one") + @Test + void refresh_withWrongRefreshToken() { + prepareDataplaneRuntime(); + + var authorizationService = DATAPLANE_RUNTIME.getService(DataPlaneAuthorizationService.class); + var edr = authorizationService.createEndpointDataReference(createStartMessage("test-process-id", CONSUMER_DID)) + .orElseThrow(f -> new AssertionError(f.getFailureDetail())); + + var refreshToken = "invalid_refresh_token"; + var accessToken = edr.getStringProperty(EDC_NAMESPACE + "authorization"); + + RUNTIME_CONFIG.getRefreshApi().baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", refreshToken) + .header(AUTHORIZATION, "Bearer " + createAuthToken(accessToken, consumerKey)) + .post("/token") + .then() + .log().ifValidationFails() + .statusCode(401) + .body(containsString("Provided refresh token does not match the stored refresh token.")); + } + + + @DisplayName("The authentication token misses required claims: token") + @Test + void refresh_invalidAuthenticationToken_missingAccessToken() { + prepareDataplaneRuntime(); + + var authorizationService = DATAPLANE_RUNTIME.getService(DataPlaneAuthorizationService.class); + var edr = authorizationService.createEndpointDataReference(createStartMessage("test-process-id", CONSUMER_DID)) + .orElseThrow(f -> new AssertionError(f.getFailureDetail())); + + var refreshToken = edr.getStringProperty(TX_AUTH_NS + "refreshToken"); + var accessToken = edr.getStringProperty(EDC_NAMESPACE + "authorization"); + + var claims = new JWTClaimsSet.Builder() + /* missing: .claim("token", accessToken)*/ + .issuer(CONSUMER_DID) + .subject(CONSUMER_DID) + .audience("did:web:bob") + .jwtID(getJwtId(accessToken)) + .build(); + var authToken = createJwt(consumerKey, claims); + + RUNTIME_CONFIG.getRefreshApi().baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", refreshToken) + .header(AUTHORIZATION, "Bearer " + authToken) + .post("/token") + .then() + .log().ifValidationFails() + .statusCode(401) + .body(containsString("Required claim 'token' not present on token.")); + } + + @DisplayName("The authentication token misses required claims: audience") + @Test + void refresh_invalidAuthenticationToken_missingAudience() { + prepareDataplaneRuntime(); + + var authorizationService = DATAPLANE_RUNTIME.getService(DataPlaneAuthorizationService.class); + var edr = authorizationService.createEndpointDataReference(createStartMessage("test-process-id", CONSUMER_DID)) + .orElseThrow(f -> new AssertionError(f.getFailureDetail())); + + var refreshToken = edr.getStringProperty(TX_AUTH_NS + "refreshToken"); + var accessToken = edr.getStringProperty(EDC_NAMESPACE + "authorization"); + + var claims = new JWTClaimsSet.Builder() + .claim("token", accessToken) + .issuer(CONSUMER_DID) + .subject(CONSUMER_DID) + /* missing: .audience("did:web:bob")*/ + .jwtID(getJwtId(accessToken)) + .build(); + var authToken = createJwt(consumerKey, claims); + + RUNTIME_CONFIG.getRefreshApi().baseRequest() + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", refreshToken) + .header(AUTHORIZATION, "Bearer " + authToken) + .post("/token") + .then() + .log().ifValidationFails() + .statusCode(401) + .body(containsString("Required claim 'aud' not present on token.")); + } + + private void prepareDataplaneRuntime() { + var vault = DATAPLANE_RUNTIME.getContext().getService(Vault.class); + vault.storeSecret(PROVIDER_KEY_ID, providerKey.toJSONString()); + vault.storeSecret(PROVIDER_KEY_ID_PUBLIC, providerKey.toPublicJWK().toJSONString()); + } + + private String createAuthToken(String accessToken, ECKey signerKey) { + var claims = new JWTClaimsSet.Builder() + .claim("token", accessToken) + .issuer(CONSUMER_DID) + .subject(CONSUMER_DID) + .audience("did:web:bob") + .jwtID(getJwtId(accessToken)) + .build(); + return createJwt(signerKey, claims); + } + + private String createJwt(ECKey signerKey, JWTClaimsSet claims) { + var header = new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(consumerKey.getKeyID()).build(); + var jwt = new SignedJWT(header, claims); + try { + jwt.sign(new ECDSASigner(signerKey)); + return jwt.serialize(); + } catch (JOSEException e) { + throw new RuntimeException(e); + } + } + + private String getJwtId(String accessToken) { + try { + return SignedJWT.parse(accessToken).getJWTClaimsSet().getJWTID(); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + private DataFlowStartMessage createStartMessage(String processId, String audience) { + return DataFlowStartMessage.Builder.newInstance() + .processId(processId) + .sourceDataAddress(DataAddress.Builder.newInstance().type("HttpData").property(EDC_NAMESPACE + "baseUrl", "http://foo.bar/").build()) + .destinationDataAddress(DataAddress.Builder.newInstance().type("HttpData").property(EDC_NAMESPACE + "baseUrl", "http://fizz.buzz").build()) + .flowType(FlowType.PULL) + .participantId("some-participantId") + .assetId("test-asset") + .callbackAddress(URI.create("https://foo.bar/callback")) + .agreementId("test-agreement") + .property(AUDIENCE_PROPERTY, audience) + .build(); + } +} diff --git a/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/e2e/RuntimeConfig.java b/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/e2e/RuntimeConfig.java new file mode 100644 index 000000000..a69c5f3a6 --- /dev/null +++ b/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/e2e/RuntimeConfig.java @@ -0,0 +1,86 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.dataplane.tokenrefresh.e2e; + +import io.restassured.specification.RequestSpecification; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import static io.restassured.RestAssured.given; +import static org.eclipse.edc.util.io.Ports.getFreePort; + +/** + * Configuration baseline for Data-Plane e2e tests + */ +public class RuntimeConfig { + + private final Endpoint publicApi = new Endpoint(URI.create("http://localhost:%d/public".formatted(getFreePort()))); + private final Endpoint signalingApi = new Endpoint(URI.create("http://localhost:%d/signaling".formatted(getFreePort()))); + private final Endpoint refreshApi = publicApi; + private final Endpoint defaultApi = new Endpoint(URI.create("http://localhost:%d/api".formatted(getFreePort()))); + + /** + * Configures the data plane token endpoint, and all relevant HTTP contexts + */ + public Map baseConfig() { + return new HashMap<>() { + { + put("edc.dataplane.token.validation.endpoint", "http://token-validation.com"); + put("web.http.path", defaultApi.url().getPath()); + put("web.http.port", String.valueOf(defaultApi.url().getPort())); + put("web.http.public.path", publicApi.url().getPath()); + put("web.http.public.port", String.valueOf(publicApi.url().getPort())); + put("web.http.signaling.path", signalingApi.url().getPath()); + put("web.http.signaling.port", String.valueOf(signalingApi.url().getPort())); + } + }; + } + + public Endpoint getPublicApi() { + return publicApi; + } + + public Endpoint getSignalingApi() { + return signalingApi; + } + + public Endpoint getRefreshApi() { + return refreshApi; + } + + public Endpoint getDefaultApi() { + return defaultApi; + } + + public record Endpoint(URI url, Map headers) { + public Endpoint(URI url) { + this(url, Map.of()); + } + + public RequestSpecification baseRequest() { + return given().baseUri(url.toString()).headers(headers); + } + + } + +} + diff --git a/edc-tests/miw-tests/README.md b/edc-tests/miw-tests/README.md deleted file mode 100644 index f204147c3..000000000 --- a/edc-tests/miw-tests/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# E2E-Tests - -This module contains JUnit tests that spin up multiple runtimes in one JVM. diff --git a/edc-tests/miw-tests/build.gradle.kts b/edc-tests/miw-tests/build.gradle.kts deleted file mode 100644 index 85c1ea822..000000000 --- a/edc-tests/miw-tests/build.gradle.kts +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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 { - `java-library` -} - -dependencies { - testImplementation(project(":spi:edr-spi")) - testImplementation(project(":edc-extensions:edr:edr-api")) - testImplementation(libs.okhttp.mockwebserver) - testImplementation(libs.restAssured) - testImplementation(libs.nimbus.jwt) - testImplementation(libs.postgres) - testImplementation(libs.awaitility) - testImplementation(libs.aws.s3) - testImplementation(libs.edc.spi.core) - testImplementation(libs.edc.junit) - testImplementation(libs.edc.spi.policy) - testImplementation(libs.edc.spi.contract) - testImplementation(libs.edc.core.api) - testImplementation(libs.edc.spi.catalog) - testImplementation(libs.edc.api.catalog) - testImplementation(libs.edc.api.contractnegotiation) - testImplementation(libs.edc.api.transferprocess) - testImplementation(libs.edc.spi.dataplane.selector) - testImplementation(libs.edc.ext.jsonld) - testImplementation(libs.edc.dsp) - testImplementation(testFixtures(libs.edc.sql.core)) - - - testCompileOnly(project(":edc-tests:runtime:extensions")) - testCompileOnly(project(":edc-tests:runtime:runtime-memory")) - testCompileOnly(project(":edc-tests:runtime:runtime-memory-ssi")) - testCompileOnly(project(":edc-tests:runtime:runtime-postgresql")) - testImplementation(project(":edc-extensions:ssi:ssi-miw-credential-client")) - testImplementation(libs.edc.auth.oauth2.client) - - runtimeOnly(libs.tink) - -} - -// do not publish -edcBuild { - publish.set(false) -} diff --git a/edc-tests/miw-tests/src/test/java/org/eclipse/tractusx/edc/tag/MiwIntegrationTest.java b/edc-tests/miw-tests/src/test/java/org/eclipse/tractusx/edc/tag/MiwIntegrationTest.java deleted file mode 100644 index c3db731a4..000000000 --- a/edc-tests/miw-tests/src/test/java/org/eclipse/tractusx/edc/tag/MiwIntegrationTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tag; - -import org.eclipse.edc.junit.annotations.IntegrationTest; -import org.junit.jupiter.api.Tag; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@IntegrationTest -@Tag("MiwIntegrationTest") -public @interface MiwIntegrationTest { -} - diff --git a/edc-tests/miw-tests/src/test/java/org/eclipse/tractusx/edc/tests/miw/README.md b/edc-tests/miw-tests/src/test/java/org/eclipse/tractusx/edc/tests/miw/README.md deleted file mode 100644 index 339f93775..000000000 --- a/edc-tests/miw-tests/src/test/java/org/eclipse/tractusx/edc/tests/miw/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# Function-testing the Managed-Identity-Wallet - -## Test setup - -As test subject we used a `docker-compose.yml` file located in `src/main/resources/`. From that directory, simply -execute `docker compose up --wait`, and then, once everything is started, -run `docker exec -i resources-postgres-1 /opt/seed.sh` to seed test data. - -## Test suite description - -### `t0001` Request and verify a VP - -### `t0002` Wrong audience - -This test asserts, that a verification request is rejected, if the wrong `audience=` query parameter is supplied. -The `audience` query parameter must match the `aud` claim inside the token. - -### `t0003` A self-signed VP token is rejected - -This test asserts, that submitting a self-generated JWT (containing the original VP claim) should be rejected. The MIW -should only accept JWTs that were signed by the requestor's private key, which is hosted in MIW. Currently, no JWT -validation is done. - -A rejected flow would be: - -- request VC from MIW -- request VP from MIW, returned in JWT format -- decode the JWT, unpack the payload -- generate a random keypair -- re-use the original claims (payload) and header -- sign with the random keypair - -### `t0004` A bogus JWT is rejected - -This test is an amendment to `t0003` in that it not only forges the JWT itself, but the JWT does not contain any of the -required claims. For example, it does not even contain a `vp` claim, so there is no VerifiablePresentation. - -### `t0005` A forged VC proof (altered JWS) is rejected - -This test asserts, that an altered (and potentially even malformed) `jws` proof is rejected. This test specifically -targets the use of JsonWebSignature2020, because there the `proof` object contains a `jws` field. - -Altering that `jws` value, here by replacing all "a" with "X" should cause the MIW to reject the verification request. - -### `t0006` A tampered VC proof (changed document) is rejected - -Similar to `t0005`, which alters the proof itself, this test alters the document, for which the proof was created. -Technically this should alter the document hash, so the proof becomes invalid, and the MIW should reject the request. - -### `t0007` Forged `iss` claim is rejected - -In this test we construct an impersonation attack, which assumes there are at least two participants in the MIW. -Participant 1 requests a VP, decodes it, replaces the `iss` claim with the ID of Participant 2 and - using again a -randomly generated keypair - signs this forged VP token. This effectively gives any participant the possibility to mount -impersonation attacks. - -> Note that Participant 2 was created in the database using the `src/test/resources/db.sh` script - -### `t0008` Invalid `iss` claim is rejected (non-existent user) - -This test attempts to have a JWT verified where the `iss` claim cannot be resolved. - -### `t0009` Invalid `iss` claim is rejected (not did:web format) - -This test asserts that a malformed `iss` claim is rejected by MIW. Specifically, the claim must be in `did:web:....` -format. - -### `t0010` An altered `aud` claim is rejected - -Similar to `t0007`, and in extension to `t0003`, this test asserts, that a verification request is rejected by MIW, if -the `aud` claim inside the JWT token was replaced. -> Note that this attack is only possible if the integrity and provenance of the JWT is not checked, see `t0003`. diff --git a/edc-tests/miw-tests/src/test/java/org/eclipse/tractusx/edc/tests/miw/WalletTest.java b/edc-tests/miw-tests/src/test/java/org/eclipse/tractusx/edc/tests/miw/WalletTest.java deleted file mode 100644 index bf3192c5d..000000000 --- a/edc-tests/miw-tests/src/test/java/org/eclipse/tractusx/edc/tests/miw/WalletTest.java +++ /dev/null @@ -1,395 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.tests.miw; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.JOSEObjectType; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.JWSObject; -import com.nimbusds.jose.crypto.Ed25519Signer; -import com.nimbusds.jose.jwk.Curve; -import com.nimbusds.jose.jwk.JWK; -import com.nimbusds.jose.jwk.KeyUse; -import com.nimbusds.jose.jwk.gen.OctetKeyPairGenerator; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import dev.failsafe.RetryPolicy; -import jakarta.json.Json; -import okhttp3.OkHttpClient; -import org.eclipse.edc.connector.core.base.EdcHttpClientImpl; -import org.eclipse.edc.iam.oauth2.client.Oauth2ClientImpl; -import org.eclipse.edc.jsonld.util.JacksonJsonLd; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClient; -import org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl; -import org.eclipse.tractusx.edc.iam.ssi.miw.oauth2.MiwOauth2ClientConfiguration; -import org.eclipse.tractusx.edc.iam.ssi.miw.oauth2.MiwOauth2ClientImpl; -import org.eclipse.tractusx.edc.tag.MiwIntegrationTest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.text.ParseException; -import java.util.Date; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; - -@MiwIntegrationTest -public class WalletTest { - - static final String MIW_SOKRATES_URL = "http://localhost:8000"; - static final String OAUTH_TOKEN_URL = "http://localhost:8080/realms/miw_test/protocol/openid-connect/token"; - private static final ObjectMapper OBJECT_MAPPER = JacksonJsonLd.createObjectMapper(); - private static final String OTHER_PARTICIPANTS_DID = "did:web:localhost%3A8000:BPNL000000000042"; - private final TypeReference> mapRef = new TypeReference<>() { - }; - private MiwApiClient client; - - @BeforeEach - void setup() { - - var monitor = mock(Monitor.class); - var httpClient = new EdcHttpClientImpl(new OkHttpClient.Builder().build(), RetryPolicy.ofDefaults(), monitor); - var config = MiwOauth2ClientConfiguration.Builder.newInstance() - .clientId("miw_private_client") - .clientSecret("miw_private_client") - .tokenUrl(OAUTH_TOKEN_URL) - .build(); - var oauth2baseClient = new Oauth2ClientImpl(httpClient, new TypeManager()); - - var auth2client = spy(new MiwOauth2ClientImpl(oauth2baseClient, config)); - - this.client = new MiwApiClientImpl(httpClient, MIW_SOKRATES_URL, auth2client, "miw_private_client", "BPNL000000000000", OBJECT_MAPPER, monitor); - } - - @Test - @DisplayName("t0001: Request VC, create a VP and verify") - void requestAndVerifyVp() { - var credentialsResult = client.getCredentials(Set.of("SummaryCredential")); - assertThat(credentialsResult.succeeded()).withFailMessage(credentialsResult::getFailureDetail).isTrue(); - assertThat(credentialsResult.getContent()).isNotEmpty(); - - var presentationResult = client.createPresentation(credentialsResult.getContent(), "test-audience"); - assertThat(presentationResult.succeeded()).isTrue(); - - var vp = presentationResult.getContent().get("vp").toString(); - var verifyResult = client.verifyPresentation(vp, "test-audience"); - - assertThat(verifyResult.succeeded()).describedAs("Should be able to verify its own VP: " + verifyResult.getFailureDetail()).isTrue(); - } - - @Test - @DisplayName("t0002: A wrong audience (passed to API) is rejected") - void verifyVp_withWrongAudience() { - var credentialsResult = client.getCredentials(Set.of("SummaryCredential")); - - var presentationResult = client.createPresentation(credentialsResult.getContent(), "test-audience"); - - var verifyResult = client.verifyPresentation(presentationResult.getContent().get("vp").toString(), "wrong-audience"); - - assertThat(verifyResult.failed()).describedAs("Should not be able to verify against a wrong audience").isTrue(); - } - - @Test - @DisplayName("t0003: A a spoofed/self-signed VP is rejected") - void verifyVp_spoofedVp() throws ParseException, JOSEException { - // obtain VC, create VP - var credentialsResult = client.getCredentials(Set.of("SummaryCredential")); - var presentationResult = client.createPresentation(credentialsResult.getContent(), "test-audience"); - - var vpToken = presentationResult.getContent().get("vp").toString(); - - // extract content, parse into JWSobject - var jwt = JWSObject.parse(vpToken); - - // create new JWS header - var header = createHeader(); - - // sign JWS with own key - var claimsSet = new JWTClaimsSet.Builder(); - jwt.getPayload().toJSONObject().forEach(claimsSet::claim); - var jwk = createJwkKeypair(); - - var ownJwt = new SignedJWT(header, claimsSet.build()); - ownJwt.sign(new Ed25519Signer(jwk.toOctetKeyPair())); - - // this should definitely return false!! - var result = client.verifyPresentation(ownJwt.serialize(), "test-audience"); - - assertThat(result.succeeded()).isFalse(); - } - - @Test - @DisplayName("t0004: A completely bogus JWT (without even a VP inside) is rejected") - void verifyVp_bogusTokenWithoutVp() throws JOSEException { - - // extract content, parse into JWSobject - // create new JWS header - var header = createHeader(); - - // generate completely arbitrary JWT claims - var claimsSet = new JWTClaimsSet.Builder(); - claimsSet.claim("aud", "test-audience") - .claim("iss", OTHER_PARTICIPANTS_DID); - var jwk = createJwkKeypair(); - - var ownJwt = new SignedJWT(header, claimsSet.build()); - ownJwt.sign(new Ed25519Signer(jwk.toOctetKeyPair())); - - // this should definitely return false!! - var result = client.verifyPresentation(ownJwt.serialize(), "test-audience"); - - assertThat(result.succeeded()).isFalse(); - } - - @Test - @DisplayName("t0005: A forged VC proof (altered JWS) is rejected") - void verifyVp_spoofedVpAndForgedJws() throws JsonProcessingException, ParseException, JOSEException { - // obtain VC, create VP - var credentialsResult = client.getCredentials(Set.of("SummaryCredential")); - var presentationResult = client.createPresentation(credentialsResult.getContent(), "test-audience"); - - var vpToken = presentationResult.getContent().get("vp").toString(); - // extract content, parse into JWSobject - var jwt = JWSObject.parse(vpToken); - - // create new JWS header - var header = createHeader(); - - // sign JWS with own key - var claimsSet = new JWTClaimsSet.Builder(); - - - var payloadJson = jwt.getPayload().toJSONObject(); - var jo = Json.createObjectBuilder(payloadJson).build(); - - // replace JWS inside the VC's proof object - var jws = jo.getJsonObject("vp").getJsonArray("verifiableCredential").getJsonObject(0).getJsonObject("proof").getString("jws"); - var invalidJws = jws.replace("a", "X"); - var tamperedJson = jo.toString().replace(jws, invalidJws); - var tamperedJsonObject = OBJECT_MAPPER.readValue(tamperedJson, mapRef); - tamperedJsonObject.forEach(claimsSet::claim); - var jwk = createJwkKeypair(); - - var forgedJwt = new SignedJWT(header, claimsSet.build()); - forgedJwt.sign(new Ed25519Signer(jwk.toOctetKeyPair())); - - // this should definitely return false!! - var result = client.verifyPresentation(forgedJwt.serialize(), "test-audience"); - - assertThat(result.succeeded()).withFailMessage("Verifying a forged VC proof (JWS) should not be possible!").isFalse(); - } - - @Test - @DisplayName("t0006: A tampered VC proof (changed holderIdentifier) is rejected") - void verifyVp_spoofedVpAndTamperedVc() throws JsonProcessingException, ParseException, JOSEException { - // obtain VC, create VP - var credentialsResult = client.getCredentials(Set.of("SummaryCredential")); - var presentationResult = client.createPresentation(credentialsResult.getContent(), "test-audience"); - - var vpToken = presentationResult.getContent().get("vp").toString(); - - // extract content, parse into JWSobject - var jwt = JWSObject.parse(vpToken); - - // create new JWS header - var header = createHeader(); - - // sign JWS with own key - var claimsSet = new JWTClaimsSet.Builder(); - - - var payloadJson = jwt.getPayload().toString(); - var tamperedPayload = payloadJson.replace("\"holderIdentifier\":\"BPNL000000000000\"", "\"holderIdentifier\":\"wrongHolderIdentifier\""); - var tamperedJsonObject = OBJECT_MAPPER.readValue(tamperedPayload, mapRef); - tamperedJsonObject.forEach(claimsSet::claim); - - var jwk = createJwkKeypair(); - - var forgedJwt = new SignedJWT(header, claimsSet.build()); - forgedJwt.sign(new Ed25519Signer(jwk.toOctetKeyPair())); - - // this should definitely return false!! - var result = client.verifyPresentation(forgedJwt.serialize(), "test-audience"); - - assertThat(result.succeeded()).withFailMessage("Verifying a tampered holderIdentifier should not be possible!").isFalse(); - } - - @Test - @DisplayName("t0007: An invalid 'iss' claim (use existing other issuer) is rejected") - void verifyVp_invalidIssuerClaim() throws ParseException, JOSEException { - var credentialsResult = client.getCredentials(Set.of("SummaryCredential")); - var presentationResult = client.createPresentation(credentialsResult.getContent(), "test-audience"); - - var vpToken = presentationResult.getContent().get("vp").toString(); - - // extract content, parse into JWSobject - var jwt = JWSObject.parse(vpToken); - - // create new JWS header - var header = createHeader(); - - // replace payload - var claimsSet = new JWTClaimsSet.Builder(); - - var vp = jwt.getPayload().toJSONObject(); - // change the iss claim - please check the src/test/resources/db.sh script, this additional participant is added there - vp.replace("iss", OTHER_PARTICIPANTS_DID); - - // create new VP token - vp.forEach(claimsSet::claim); - - var jwk = createJwkKeypair(); - var forgedJwt = new SignedJWT(header, claimsSet.build()); - forgedJwt.sign(new Ed25519Signer(jwk.toOctetKeyPair())); - - var result = client.verifyPresentation(forgedJwt.serialize(), "test-audience"); - - assertThat(result.succeeded()).withFailMessage("Altering the 'iss' claim (impersonation) should not be possible!").isFalse(); - } - - @Test - @DisplayName("t0008: An invalid 'iss' claim (non-existing issuer) is rejected") - void verifyVp_invalidIssuerClaim_notExists() throws ParseException, JOSEException { - var credentialsResult = client.getCredentials(Set.of("SummaryCredential")); - var presentationResult = client.createPresentation(credentialsResult.getContent(), "test-audience"); - - var vpToken = presentationResult.getContent().get("vp").toString(); - - // extract content, parse into JWSobject - var jwt = JWSObject.parse(vpToken); - - // create new JWS header - var header = createHeader(); - - // replace payload - var claimsSet = new JWTClaimsSet.Builder(); - - var vp = jwt.getPayload().toJSONObject(); - // change the iss claim - please check the src/test/resources/db.sh script, this additional participant is added there - vp.replace("iss", "did:web:someotherissuer"); - - // create new VP token - vp.forEach(claimsSet::claim); - - var jwk = createJwkKeypair(); - var forgedJwt = new SignedJWT(header, claimsSet.build()); - forgedJwt.sign(new Ed25519Signer(jwk.toOctetKeyPair())); - - var result = client.verifyPresentation(forgedJwt.serialize(), "test-audience"); - - assertThat(result.succeeded()).withFailMessage("Altering the 'iss' claim (impersonation) should not be possible!").isFalse(); - } - - @Test - @DisplayName("t0009: An invalid 'iss' claim (not in DID:web format) is rejected") - void verifyVp_invalidIssuerClaim_notExists_issNotDidWeb() throws ParseException, JOSEException { - var credentialsResult = client.getCredentials(Set.of("SummaryCredential")); - var presentationResult = client.createPresentation(credentialsResult.getContent(), "test-audience"); - - var vpToken = presentationResult.getContent().get("vp").toString(); - - // extract content, parse into JWSobject - var jwt = JWSObject.parse(vpToken); - - // create new JWS header - var header = createHeader(); - - // replace payload - var claimsSet = new JWTClaimsSet.Builder(); - - var vp = jwt.getPayload().toJSONObject(); - // change the iss claim - please check the src/test/resources/db.sh script, this additional participant is added there - vp.replace("iss", "this_isnt_even_didweb"); - - // create new VP token - vp.forEach(claimsSet::claim); - - var jwk = createJwkKeypair(); - var forgedJwt = new SignedJWT(header, claimsSet.build()); - forgedJwt.sign(new Ed25519Signer(jwk.toOctetKeyPair())); - - var result = client.verifyPresentation(forgedJwt.serialize(), "test-audience"); - - assertThat(result.succeeded()).withFailMessage("Altering the 'iss' claim (impersonation) should not be possible!").isFalse(); - } - - @Test - @DisplayName("t0010: An altered 'aud' claim is rejected") - void verifyVp_modifiedAudClaim() throws ParseException, JOSEException { - var credentialsResult = client.getCredentials(Set.of("SummaryCredential")); - var presentationResult = client.createPresentation(credentialsResult.getContent(), "test-audience"); - - var vpToken = presentationResult.getContent().get("vp").toString(); - - // extract content, parse into JWSobject - var jwt = JWSObject.parse(vpToken); - - // create new JWS header - var header = createHeader(); - - // replace payload - var claimsSet = new JWTClaimsSet.Builder(); - - var vp = jwt.getPayload().toJSONObject(); - // change the iss claim - vp.replace("aud", "some-other-audience"); - - // create new VP token - vp.forEach(claimsSet::claim); - - var jwk = createJwkKeypair(); - var forgedJwt = new SignedJWT(header, claimsSet.build()); - forgedJwt.sign(new Ed25519Signer(jwk.toOctetKeyPair())); - - var result = client.verifyPresentation(forgedJwt.serialize(), "some-other-audience"); - - assertThat(result.succeeded()).withFailMessage("Altering the 'aud' claim (replay attack) should not be possible!").isFalse(); - } - - private JWSHeader createHeader() { - return new JWSHeader.Builder(JWSAlgorithm.EdDSA) - .type(JOSEObjectType.JWT) - .keyID(UUID.randomUUID().toString()) - .build(); - } - - private JWK createJwkKeypair() { - try { - return new OctetKeyPairGenerator(Curve.Ed25519) - .keyUse(KeyUse.SIGNATURE) // indicate the intended use of the key (optional) - .keyID(UUID.randomUUID().toString()) // give the key a unique ID (optional) - .issueTime(new Date()) // issued-at timestamp (optional) - .generate(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/edc-tests/miw-tests/src/test/resources/docker-environment/docker-compose.yaml b/edc-tests/miw-tests/src/test/resources/docker-environment/docker-compose.yaml deleted file mode 100644 index b89dc5729..000000000 --- a/edc-tests/miw-tests/src/test/resources/docker-environment/docker-compose.yaml +++ /dev/null @@ -1,92 +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 -################################################################################# - - -version: '3' - -services: - miw: - image: tractusx/managed-identity-wallet:main - platform: linux/amd64 - container_name: miw - env_file: - - ../env-files/env.docker - ports: - - "8000:8000" - - "8090:8090" - - "5005:5005" - networks: - - miw-net - entrypoint: "java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar miw-latest.jar" - depends_on: - postgres: - condition: service_started - keycloak: - condition: service_started - - postgres: - image: postgres:15.3-alpine3.18 - volumes: - - postgres_data:/var/lib/postgresql/data - - ./postgres/db.sh:/docker-entrypoint-initdb.d/init-database.sh - - ./postgres/seed.sh:/opt/seed.sh - env_file: - - ../env-files/env.docker - ports: - - "5432:5432" - networks: - - miw-net - healthcheck: - test: [ "CMD-SHELL", "pg_isready -U postgres" ] - interval: 5s - timeout: 30s - retries: 10 - - keycloak: - image: quay.io/keycloak/keycloak:21.1 - env_file: - - ../env-files/env.docker - environment: - DB_SCHEMA: public - command: - - start-dev - - --import-realm - volumes: - - ./keycloak/miw_test_realm.json:/opt/keycloak/data/import/miw_test_realm.json - - ./keycloak/health-check.sh:/opt/keycloak/health-check.sh - ports: - - "8080:8080" - depends_on: - postgres: - condition: service_healthy - networks: - - miw-net - healthcheck: - test: "bash /opt/keycloak/health-check.sh" - interval: 10s - timeout: 10s - retries: 15 - -volumes: - postgres_data: - driver: local - -networks: - miw-net: - name: miw-net diff --git a/edc-tests/miw-tests/src/test/resources/docker-environment/keycloak/miw_test_realm.json b/edc-tests/miw-tests/src/test/resources/docker-environment/keycloak/miw_test_realm.json deleted file mode 100644 index d2bc89329..000000000 --- a/edc-tests/miw-tests/src/test/resources/docker-environment/keycloak/miw_test_realm.json +++ /dev/null @@ -1,2479 +0,0 @@ -{ - "id": "e980fcc5-9e29-485c-bd56-440783e32014", - "realm": "miw_test", - "notBefore": 0, - "defaultSignatureAlgorithm": "RS256", - "revokeRefreshToken": false, - "refreshTokenMaxReuse": 0, - "accessTokenLifespan": 28800, - "accessTokenLifespanForImplicitFlow": 900, - "ssoSessionIdleTimeout": 1800, - "ssoSessionMaxLifespan": 36000, - "ssoSessionIdleTimeoutRememberMe": 0, - "ssoSessionMaxLifespanRememberMe": 0, - "offlineSessionIdleTimeout": 2592000, - "offlineSessionMaxLifespanEnabled": false, - "offlineSessionMaxLifespan": 5184000, - "clientSessionIdleTimeout": 0, - "clientSessionMaxLifespan": 0, - "clientOfflineSessionIdleTimeout": 0, - "clientOfflineSessionMaxLifespan": 0, - "accessCodeLifespan": 60, - "accessCodeLifespanUserAction": 300, - "accessCodeLifespanLogin": 1800, - "actionTokenGeneratedByAdminLifespan": 43200, - "actionTokenGeneratedByUserLifespan": 28800, - "oauth2DeviceCodeLifespan": 600, - "oauth2DevicePollingInterval": 5, - "enabled": true, - "sslRequired": "external", - "registrationAllowed": false, - "registrationEmailAsUsername": false, - "rememberMe": false, - "verifyEmail": false, - "loginWithEmailAllowed": true, - "duplicateEmailsAllowed": false, - "resetPasswordAllowed": false, - "editUsernameAllowed": false, - "bruteForceProtected": false, - "permanentLockout": false, - "maxFailureWaitSeconds": 900, - "minimumQuickLoginWaitSeconds": 60, - "waitIncrementSeconds": 60, - "quickLoginCheckMilliSeconds": 1000, - "maxDeltaTimeSeconds": 43200, - "failureFactor": 30, - "roles": { - "realm": [ - { - "id": "ad36b1ad-a3cb-4594-853b-b5744b86fcdb", - "name": "uma_authorization", - "description": "${role_uma_authorization}", - "composite": false, - "clientRole": false, - "containerId": "e980fcc5-9e29-485c-bd56-440783e32014", - "attributes": {} - }, - { - "id": "3247ecc3-6884-4548-bfaa-0f47cce0cda6", - "name": "default-roles-miw_test", - "description": "${role_default-roles}", - "composite": true, - "composites": { - "realm": [ - "offline_access", - "uma_authorization" - ], - "client": { - "realm-management": [ - "manage-users" - ], - "account": [ - "view-profile", - "manage-account" - ] - } - }, - "clientRole": false, - "containerId": "e980fcc5-9e29-485c-bd56-440783e32014", - "attributes": {} - }, - { - "id": "ce1ee2c7-517c-4cf0-a96f-3adac1d200a7", - "name": "offline_access", - "description": "${role_offline-access}", - "composite": false, - "clientRole": false, - "containerId": "e980fcc5-9e29-485c-bd56-440783e32014", - "attributes": {} - } - ], - "client": { - "realm-management": [ - { - "id": "e9eb031a-9dc3-413f-be30-8a396cf9a783", - "name": "manage-authorization", - "description": "${role_manage-authorization}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "b33997ba-a7cb-4f47-8272-d04c18e51416", - "name": "manage-identity-providers", - "description": "${role_manage-identity-providers}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "c66b4177-f470-4164-851c-018fa4445d78", - "name": "query-users", - "description": "${role_query-users}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "ac2965ec-c2f2-4e30-b8fd-e3a34afc0070", - "name": "create-client", - "description": "${role_create-client}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "fc813275-05d3-408f-a0d5-6943a66ada3f", - "name": "manage-events", - "description": "${role_manage-events}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "73d25c6c-ca63-414e-a908-22d2f2cb18f6", - "name": "view-realm", - "description": "${role_view-realm}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "2073b2f4-c5de-491f-a34d-ea0c687cae4e", - "name": "manage-users", - "description": "${role_manage-users}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "3f5e2b33-5611-4289-a36d-236b81485938", - "name": "view-identity-providers", - "description": "${role_view-identity-providers}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "9e9436f9-6f9a-4a86-adaa-da935522e551", - "name": "impersonation", - "description": "${role_impersonation}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "272c47ae-68d9-459a-8d8c-39b95136681b", - "name": "query-realms", - "description": "${role_query-realms}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "8d3984f8-408c-4c9f-8af5-dcdbbf76118c", - "name": "view-users", - "description": "${role_view-users}", - "composite": true, - "composites": { - "client": { - "realm-management": [ - "query-users", - "query-groups" - ] - } - }, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "9beee882-a768-42ed-b142-74e238928634", - "name": "realm-admin", - "description": "${role_realm-admin}", - "composite": true, - "composites": { - "client": { - "realm-management": [ - "manage-identity-providers", - "manage-authorization", - "query-users", - "create-client", - "manage-events", - "view-realm", - "manage-users", - "view-identity-providers", - "impersonation", - "query-realms", - "view-users", - "view-clients", - "view-authorization", - "query-groups", - "query-clients", - "view-events", - "manage-clients", - "manage-realm" - ] - } - }, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "df03dd95-6720-4ec8-a21e-25f124b9be51", - "name": "view-authorization", - "description": "${role_view-authorization}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "9f0a02be-2609-496c-82cc-c07b82d2b4cc", - "name": "view-clients", - "description": "${role_view-clients}", - "composite": true, - "composites": { - "client": { - "realm-management": [ - "query-clients" - ] - } - }, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "f2d938d7-835f-414b-af54-289c97fed144", - "name": "query-groups", - "description": "${role_query-groups}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "6dea15cf-8398-442a-9df6-639c45cce53b", - "name": "query-clients", - "description": "${role_query-clients}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "8f0da98f-988a-46cf-be03-44e12f1c3ad6", - "name": "view-events", - "description": "${role_view-events}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "3f2173cd-352d-4928-9525-1fdbaf289309", - "name": "manage-clients", - "description": "${role_manage-clients}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - }, - { - "id": "d0c8168f-9ac4-4ac8-a908-715fda68959c", - "name": "manage-realm", - "description": "${role_manage-realm}", - "composite": false, - "clientRole": true, - "containerId": "f2604867-9227-4947-8d36-6abc754f9883", - "attributes": {} - } - ], - "security-admin-console": [], - "miw_private_client": [ - { - "id": "232e256b-81b3-4282-8198-2a4557a2687a", - "name": "view_wallets", - "description": "view_wallets", - "composite": false, - "clientRole": true, - "containerId": "774d507f-5aa3-4d16-be24-0e461f35d66a", - "attributes": {} - }, - { - "id": "2a1f1417-4eed-4ff9-b569-7461f7ae0ead", - "name": "add_wallets", - "description": "add_wallets", - "composite": false, - "clientRole": true, - "containerId": "774d507f-5aa3-4d16-be24-0e461f35d66a", - "attributes": {} - }, - { - "id": "737ec30a-c542-419a-8533-8caa7a267b68", - "name": "update_wallet", - "description": "update_wallet", - "composite": false, - "clientRole": true, - "containerId": "774d507f-5aa3-4d16-be24-0e461f35d66a", - "attributes": {} - }, - { - "id": "b32143a1-23cc-4ea5-96b0-aec079958ca0", - "name": "view_wallet", - "description": "view_wallet", - "composite": false, - "clientRole": true, - "containerId": "774d507f-5aa3-4d16-be24-0e461f35d66a", - "attributes": {} - }, - { - "id": "8ac5652e-103e-49a2-a7d0-4a9cdc958543", - "name": "update_wallets", - "description": "update_wallets", - "composite": false, - "clientRole": true, - "containerId": "774d507f-5aa3-4d16-be24-0e461f35d66a", - "attributes": {} - } - ], - "admin-cli": [], - "account-console": [], - "broker": [ - { - "id": "bd277caa-1e1f-474a-9fb9-a0f6ec21bfa5", - "name": "read-token", - "description": "${role_read-token}", - "composite": false, - "clientRole": true, - "containerId": "f6dd02a1-9c2b-4af9-81bf-200efc0fcf22", - "attributes": {} - } - ], - "miw_public": [], - "account": [ - { - "id": "cbe6b27b-83b2-4c40-ba6b-e776b32d919c", - "name": "manage-account-links", - "description": "${role_manage-account-links}", - "composite": false, - "clientRole": true, - "containerId": "356d12b7-0894-474f-8701-c51c78182351", - "attributes": {} - }, - { - "id": "2e9938b0-51ea-47f6-91d5-93020fbbe094", - "name": "view-profile", - "description": "${role_view-profile}", - "composite": false, - "clientRole": true, - "containerId": "356d12b7-0894-474f-8701-c51c78182351", - "attributes": {} - }, - { - "id": "000f2103-4f84-4ab2-b2e9-72e006a7aa7a", - "name": "delete-account", - "description": "${role_delete-account}", - "composite": false, - "clientRole": true, - "containerId": "356d12b7-0894-474f-8701-c51c78182351", - "attributes": {} - }, - { - "id": "d0d1ec92-4928-4446-ab70-af4a5ec941f0", - "name": "manage-consent", - "description": "${role_manage-consent}", - "composite": true, - "composites": { - "client": { - "account": [ - "view-consent" - ] - } - }, - "clientRole": true, - "containerId": "356d12b7-0894-474f-8701-c51c78182351", - "attributes": {} - }, - { - "id": "be516b3c-47c9-4da9-b65a-c0269c066cd2", - "name": "view-consent", - "description": "${role_view-consent}", - "composite": false, - "clientRole": true, - "containerId": "356d12b7-0894-474f-8701-c51c78182351", - "attributes": {} - }, - { - "id": "f628b4e8-783f-4b2b-ad20-9ce7191ef39b", - "name": "manage-account", - "description": "${role_manage-account}", - "composite": true, - "composites": { - "client": { - "account": [ - "manage-account-links" - ] - } - }, - "clientRole": true, - "containerId": "356d12b7-0894-474f-8701-c51c78182351", - "attributes": {} - }, - { - "id": "465eff9a-73da-4fd3-ac96-e84db10cc263", - "name": "view-applications", - "description": "${role_view-applications}", - "composite": false, - "clientRole": true, - "containerId": "356d12b7-0894-474f-8701-c51c78182351", - "attributes": {} - }, - { - "id": "631c870f-24e9-4058-b506-993520d68d24", - "name": "view-groups", - "description": "${role_view-groups}", - "composite": false, - "clientRole": true, - "containerId": "356d12b7-0894-474f-8701-c51c78182351", - "attributes": {} - } - ] - } - }, - "groups": [], - "defaultRole": { - "id": "3247ecc3-6884-4548-bfaa-0f47cce0cda6", - "name": "default-roles-miw_test", - "description": "${role_default-roles}", - "composite": true, - "clientRole": false, - "containerId": "e980fcc5-9e29-485c-bd56-440783e32014" - }, - "requiredCredentials": [ - "password" - ], - "otpPolicyType": "totp", - "otpPolicyAlgorithm": "HmacSHA1", - "otpPolicyInitialCounter": 0, - "otpPolicyDigits": 6, - "otpPolicyLookAheadWindow": 1, - "otpPolicyPeriod": 30, - "otpPolicyCodeReusable": false, - "otpSupportedApplications": [ - "totpAppMicrosoftAuthenticatorName", - "totpAppGoogleName", - "totpAppFreeOTPName" - ], - "webAuthnPolicyRpEntityName": "keycloak", - "webAuthnPolicySignatureAlgorithms": [ - "ES256" - ], - "webAuthnPolicyRpId": "", - "webAuthnPolicyAttestationConveyancePreference": "not specified", - "webAuthnPolicyAuthenticatorAttachment": "not specified", - "webAuthnPolicyRequireResidentKey": "not specified", - "webAuthnPolicyUserVerificationRequirement": "not specified", - "webAuthnPolicyCreateTimeout": 0, - "webAuthnPolicyAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyAcceptableAaguids": [], - "webAuthnPolicyPasswordlessRpEntityName": "keycloak", - "webAuthnPolicyPasswordlessSignatureAlgorithms": [ - "ES256" - ], - "webAuthnPolicyPasswordlessRpId": "", - "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", - "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", - "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", - "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", - "webAuthnPolicyPasswordlessCreateTimeout": 0, - "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyPasswordlessAcceptableAaguids": [], - "users": [ - { - "id": "7e5c957b-2f20-41e0-85fb-e84656caadfe", - "createdTimestamp": 1687957169104, - "username": "service-account-miw_private_client", - "enabled": true, - "totp": false, - "emailVerified": false, - "serviceAccountClientId": "miw_private_client", - "disableableCredentialTypes": [], - "requiredActions": [], - "realmRoles": [ - "default-roles-miw_test" - ], - "clientRoles": { - "miw_private_client": [ - "view_wallets", - "update_wallet", - "add_wallets", - "view_wallet", - "update_wallets" - ] - }, - "notBefore": 0, - "groups": [] - } - ], - "scopeMappings": [ - { - "clientScope": "offline_access", - "roles": [ - "offline_access" - ] - } - ], - "clientScopeMappings": { - "account": [ - { - "client": "account-console", - "roles": [ - "manage-account", - "view-groups" - ] - } - ] - }, - "clients": [ - { - "id": "356d12b7-0894-474f-8701-c51c78182351", - "clientId": "account", - "name": "${client_account}", - "rootUrl": "${authBaseUrl}", - "baseUrl": "/realms/miw_test/account/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [ - "/realms/miw_test/account/*" - ], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "post.logout.redirect.uris": "+" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [ - "web-origins", - "acr", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "id": "e33fa081-88ee-4443-955a-22b57d96bd9a", - "clientId": "account-console", - "name": "${client_account-console}", - "rootUrl": "${authBaseUrl}", - "baseUrl": "/realms/miw_test/account/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [ - "/realms/miw_test/account/*" - ], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "post.logout.redirect.uris": "+", - "pkce.code.challenge.method": "S256" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "db8af579-9b62-4a5d-8f21-9113cacce594", - "name": "audience resolve", - "protocol": "openid-connect", - "protocolMapper": "oidc-audience-resolve-mapper", - "consentRequired": false, - "config": {} - } - ], - "defaultClientScopes": [ - "web-origins", - "acr", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "id": "e6ecff04-23e9-4828-ae48-2eaf9cf21086", - "clientId": "admin-cli", - "name": "${client_admin-cli}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": false, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "post.logout.redirect.uris": "+" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [ - "web-origins", - "acr", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "id": "f6dd02a1-9c2b-4af9-81bf-200efc0fcf22", - "clientId": "broker", - "name": "${client_broker}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": true, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "post.logout.redirect.uris": "+" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [ - "web-origins", - "acr", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "id": "774d507f-5aa3-4d16-be24-0e461f35d66a", - "clientId": "miw_private_client", - "name": "miw_private_client", - "description": "miw_private_client", - "rootUrl": "", - "adminUrl": "", - "baseUrl": "", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "miw_private_client", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": true, - "publicClient": false, - "frontchannelLogout": true, - "protocol": "openid-connect", - "attributes": { - "oidc.ciba.grant.enabled": "false", - "client.secret.creation.time": "1684923648", - "backchannel.logout.session.required": "true", - "post.logout.redirect.uris": "+", - "display.on.consent.screen": "false", - "oauth2.device.authorization.grant.enabled": "false", - "backchannel.logout.revoke.offline.tokens": "false" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": true, - "nodeReRegistrationTimeout": -1, - "protocolMappers": [ - { - "id": "767fc59d-4812-4147-a4c0-c1d36854a111", - "name": "User Client Role", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-client-role-mapper", - "consentRequired": false, - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "usermodel.clientRoleMapping.clientId": "miw_private_client", - "multivalued": "true", - "userinfo.token.claim": "true" - } - }, - { - "id": "c46e9cc6-3057-4640-a78b-e12fc3a714df", - "name": "BPN", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "aggregate.attrs": "false", - "userinfo.token.claim": "true", - "multivalued": "false", - "user.attribute": "BPN", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "BPN" - } - }, - { - "id": "f446598c-1637-4585-b2b6-0204d2e6e92e", - "name": "client_bpn_mapper", - "protocol": "openid-connect", - "protocolMapper": "oidc-hardcoded-claim-mapper", - "consentRequired": false, - "config": { - "claim.value": "BPNL000000000000", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "BPN", - "access.tokenResponse.claim": "false" - } - }, - { - "id": "1340463e-a737-4507-8ecb-b01715a9fde4", - "name": "Client IP Address", - "protocol": "openid-connect", - "protocolMapper": "oidc-usersessionmodel-note-mapper", - "consentRequired": false, - "config": { - "user.session.note": "clientAddress", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "clientAddress", - "jsonType.label": "String" - } - }, - { - "id": "9096587b-3781-4104-b1ec-458c7ca95e8d", - "name": "Client ID", - "protocol": "openid-connect", - "protocolMapper": "oidc-usersessionmodel-note-mapper", - "consentRequired": false, - "config": { - "user.session.note": "clientId", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "clientId", - "jsonType.label": "String" - } - }, - { - "id": "370515a5-370a-4b68-9704-9a67407c1390", - "name": "Client Host", - "protocol": "openid-connect", - "protocolMapper": "oidc-usersessionmodel-note-mapper", - "consentRequired": false, - "config": { - "user.session.note": "clientHost", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "clientHost", - "jsonType.label": "String" - } - } - ], - "defaultClientScopes": [ - "web-origins", - "acr", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "id": "7dbe3954-6da4-43f1-a1df-cf160fee58e2", - "clientId": "miw_public", - "name": "", - "description": "", - "rootUrl": "", - "adminUrl": "", - "baseUrl": "", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [ - "http://localhost:8080/*", - "http://localhost/*", - "http://localhost:8087/*" - ], - "webOrigins": [ - "http://localhost:8080", - "http://localhost", - "http://localhost:8087" - ], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": true, - "protocol": "openid-connect", - "attributes": { - "oidc.ciba.grant.enabled": "false", - "backchannel.logout.session.required": "true", - "post.logout.redirect.uris": "+", - "display.on.consent.screen": "false", - "oauth2.device.authorization.grant.enabled": "false", - "backchannel.logout.revoke.offline.tokens": "false" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": true, - "nodeReRegistrationTimeout": -1, - "protocolMappers": [ - { - "id": "1312c58c-7950-4e3f-b45d-a77b827a62d7", - "name": "BPN_user_attribute", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "aggregate.attrs": "false", - "userinfo.token.claim": "true", - "multivalued": "false", - "user.attribute": "BPN", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "BPN" - } - } - ], - "defaultClientScopes": [ - "web-origins", - "acr", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "id": "f2604867-9227-4947-8d36-6abc754f9883", - "clientId": "realm-management", - "name": "${client_realm-management}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": true, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "post.logout.redirect.uris": "+" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [ - "web-origins", - "acr", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - }, - { - "id": "d966ce87-fa07-4c99-9ed1-899961993d88", - "clientId": "security-admin-console", - "name": "${client_security-admin-console}", - "rootUrl": "${authAdminUrl}", - "baseUrl": "/admin/miw_test/console/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [ - "/admin/miw_test/console/*" - ], - "webOrigins": [ - "+" - ], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "post.logout.redirect.uris": "+", - "pkce.code.challenge.method": "S256" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "088895dc-a6b7-4d7a-b8e8-70804dd7a4be", - "name": "locale", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "locale", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "locale", - "jsonType.label": "String" - } - } - ], - "defaultClientScopes": [ - "web-origins", - "acr", - "profile", - "roles", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ] - } - ], - "clientScopes": [ - { - "id": "e7addfcc-9187-43b2-9dd8-d883c3d7d4ce", - "name": "email", - "description": "OpenID Connect built-in scope: email", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${emailScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "7f56bfa8-3c9c-4ddb-ba03-bf3baee76b5e", - "name": "email verified", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "emailVerified", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email_verified", - "jsonType.label": "boolean" - } - }, - { - "id": "7ae07240-7a54-4e77-a3ed-1cff45e70a6f", - "name": "email", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" - } - } - ] - }, - { - "id": "6447876f-32c7-42b7-864c-61b8c12f651f", - "name": "offline_access", - "description": "OpenID Connect built-in scope: offline_access", - "protocol": "openid-connect", - "attributes": { - "consent.screen.text": "${offlineAccessScopeConsentText}", - "display.on.consent.screen": "true" - } - }, - { - "id": "7b162106-cbc9-4c05-9043-6fbece4d7600", - "name": "role_list", - "description": "SAML role list", - "protocol": "saml", - "attributes": { - "consent.screen.text": "${samlRoleListScopeConsentText}", - "display.on.consent.screen": "true" - }, - "protocolMappers": [ - { - "id": "445b2b60-0bf1-4eb8-ab60-99351b616da6", - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", - "consentRequired": false, - "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" - } - } - ] - }, - { - "id": "ad308290-1c37-4d33-99f3-8d23e2f74501", - "name": "microprofile-jwt", - "description": "Microprofile - JWT built-in scope", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "false" - }, - "protocolMappers": [ - { - "id": "7fbc621e-a6ad-48d4-b981-55be57bae980", - "name": "groups", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-realm-role-mapper", - "consentRequired": false, - "config": { - "multivalued": "true", - "userinfo.token.claim": "true", - "user.attribute": "foo", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "groups", - "jsonType.label": "String" - } - }, - { - "id": "3eacc647-eff9-48a4-a9ca-cdd8b1a02665", - "name": "upn", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "upn", - "jsonType.label": "String" - } - } - ] - }, - { - "id": "f6d808aa-019d-4f3f-951e-dda5a77f841c", - "name": "acr", - "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "false" - }, - "protocolMappers": [ - { - "id": "d3204d28-9023-4cf6-b996-fd845180c8dd", - "name": "acr loa level", - "protocol": "openid-connect", - "protocolMapper": "oidc-acr-mapper", - "consentRequired": false, - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - } - ] - }, - { - "id": "fcfb1f12-dc72-4529-be32-51b16d4b7c58", - "name": "profile", - "description": "OpenID Connect built-in scope: profile", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${profileScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "7091a3bd-ffd1-40cf-82cf-636aa49728ce", - "name": "nickname", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "nickname", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "nickname", - "jsonType.label": "String" - } - }, - { - "id": "27f9ab53-8807-4ef1-b9a0-12a8a76ab5ec", - "name": "birthdate", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "birthdate", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "birthdate", - "jsonType.label": "String" - } - }, - { - "id": "29402017-bf33-48c2-8e7c-9eae2c44e929", - "name": "locale", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "locale", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "locale", - "jsonType.label": "String" - } - }, - { - "id": "6e24f73b-8529-43ff-9815-2901cb1d5a91", - "name": "updated at", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "updatedAt", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "updated_at", - "jsonType.label": "long" - } - }, - { - "id": "a45c35be-f77d-4627-9bf9-a3414722e484", - "name": "picture", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "picture", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "picture", - "jsonType.label": "String" - } - }, - { - "id": "eba7c338-cce4-4cd6-8044-083273ddca3a", - "name": "full name", - "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", - "consentRequired": false, - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - }, - { - "id": "bfb08dad-0a9f-41fd-871b-1fbfb0d43594", - "name": "profile", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "profile", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "profile", - "jsonType.label": "String" - } - }, - { - "id": "b8f94365-aa92-44d7-9f96-84822aef4cad", - "name": "family name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" - } - }, - { - "id": "b8849581-e158-4daf-98f0-b23f351b7362", - "name": "given name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" - } - }, - { - "id": "7104be3f-1760-4fa7-9ad7-985959f852f2", - "name": "website", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "website", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "website", - "jsonType.label": "String" - } - }, - { - "id": "c7a9ba7a-62bf-4846-9b2f-56a8c6b31901", - "name": "gender", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "gender", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "gender", - "jsonType.label": "String" - } - }, - { - "id": "1e5a4e39-1fbc-4245-bced-f1271c01cf28", - "name": "middle name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "middleName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "middle_name", - "jsonType.label": "String" - } - }, - { - "id": "20bca7ef-8879-4b77-85fc-e38dd86518da", - "name": "username", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" - } - }, - { - "id": "679465a3-8205-404b-ac12-f0ce50194f71", - "name": "zoneinfo", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "zoneinfo", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "zoneinfo", - "jsonType.label": "String" - } - } - ] - }, - { - "id": "fc9f5da4-557c-432f-87ec-128c07e09c79", - "name": "phone", - "description": "OpenID Connect built-in scope: phone", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${phoneScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "bbe96ba8-010c-4798-83e5-38fa3c7e7d66", - "name": "phone number", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "phoneNumber", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "phone_number", - "jsonType.label": "String" - } - }, - { - "id": "15f0c6ce-d7a5-4165-9ae2-978e3776d4a4", - "name": "phone number verified", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "phoneNumberVerified", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "phone_number_verified", - "jsonType.label": "boolean" - } - } - ] - }, - { - "id": "96747a05-db5f-4289-bca2-8e3ebc0b244e", - "name": "roles", - "description": "OpenID Connect scope for add user roles to the access token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "true", - "consent.screen.text": "${rolesScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "7db29b64-30f8-43df-99f7-73f16db774b4", - "name": "audience resolve", - "protocol": "openid-connect", - "protocolMapper": "oidc-audience-resolve-mapper", - "consentRequired": false, - "config": {} - }, - { - "id": "1fa84511-e274-4ffb-8cb7-a426dd5ebe4a", - "name": "realm roles", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-realm-role-mapper", - "consentRequired": false, - "config": { - "user.attribute": "foo", - "access.token.claim": "true", - "claim.name": "realm_access.roles", - "jsonType.label": "String", - "multivalued": "true" - } - }, - { - "id": "8594b20e-3ade-4661-bea2-bf0b5d47ff1e", - "name": "client roles", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-client-role-mapper", - "consentRequired": false, - "config": { - "user.attribute": "foo", - "access.token.claim": "true", - "claim.name": "resource_access.${client_id}.roles", - "jsonType.label": "String", - "multivalued": "true" - } - } - ] - }, - { - "id": "801527ae-e765-4d90-8d87-5547fc96d2be", - "name": "address", - "description": "OpenID Connect built-in scope: address", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${addressScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "5519fbcf-8042-4b00-9c2a-a79bf16b9d59", - "name": "address", - "protocol": "openid-connect", - "protocolMapper": "oidc-address-mapper", - "consentRequired": false, - "config": { - "user.attribute.formatted": "formatted", - "user.attribute.country": "country", - "user.attribute.postal_code": "postal_code", - "userinfo.token.claim": "true", - "user.attribute.street": "street", - "id.token.claim": "true", - "user.attribute.region": "region", - "access.token.claim": "true", - "user.attribute.locality": "locality" - } - } - ] - }, - { - "id": "99a7cadd-76c0-406f-88bf-24947fec442e", - "name": "web-origins", - "description": "OpenID Connect scope for add allowed web origins to the access token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "false", - "consent.screen.text": "" - }, - "protocolMappers": [ - { - "id": "a57ca5de-7d7a-4695-b181-1099790ec07f", - "name": "allowed web origins", - "protocol": "openid-connect", - "protocolMapper": "oidc-allowed-origins-mapper", - "consentRequired": false, - "config": {} - } - ] - } - ], - "defaultDefaultClientScopes": [ - "role_list", - "profile", - "email", - "roles", - "web-origins", - "acr" - ], - "defaultOptionalClientScopes": [ - "offline_access", - "address", - "phone", - "microprofile-jwt" - ], - "browserSecurityHeaders": { - "contentSecurityPolicyReportOnly": "", - "xContentTypeOptions": "nosniff", - "xRobotsTag": "none", - "xFrameOptions": "SAMEORIGIN", - "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", - "xXSSProtection": "1; mode=block", - "strictTransportSecurity": "max-age=31536000; includeSubDomains" - }, - "smtpServer": {}, - "eventsEnabled": false, - "eventsListeners": [ - "jboss-logging" - ], - "enabledEventTypes": [], - "adminEventsEnabled": false, - "adminEventsDetailsEnabled": false, - "identityProviders": [], - "identityProviderMappers": [], - "components": { - "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ - { - "id": "bc6e125a-0c96-4a44-ac91-bf6ecc035cec", - "name": "Allowed Client Scopes", - "providerId": "allowed-client-templates", - "subType": "authenticated", - "subComponents": {}, - "config": { - "allow-default-scopes": [ - "true" - ] - } - }, - { - "id": "a9aceec7-3d4d-4fc7-9ee7-b0862b3f212a", - "name": "Allowed Client Scopes", - "providerId": "allowed-client-templates", - "subType": "anonymous", - "subComponents": {}, - "config": { - "allow-default-scopes": [ - "true" - ] - } - }, - { - "id": "476306a8-3346-430b-a6da-f3fc52910ce9", - "name": "Max Clients Limit", - "providerId": "max-clients", - "subType": "anonymous", - "subComponents": {}, - "config": { - "max-clients": [ - "200" - ] - } - }, - { - "id": "b3cc2af0-dc32-4a7d-9298-fdc664f3bb83", - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "authenticated", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "oidc-sha256-pairwise-sub-mapper", - "saml-user-attribute-mapper", - "saml-user-property-mapper", - "oidc-full-name-mapper", - "oidc-usermodel-attribute-mapper", - "saml-role-list-mapper", - "oidc-address-mapper", - "oidc-usermodel-property-mapper" - ] - } - }, - { - "id": "7da42bd3-7368-4be2-bc0c-82067fc48463", - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "anonymous", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "oidc-full-name-mapper", - "saml-user-attribute-mapper", - "oidc-address-mapper", - "oidc-sha256-pairwise-sub-mapper", - "oidc-usermodel-property-mapper", - "saml-user-property-mapper", - "oidc-usermodel-attribute-mapper", - "saml-role-list-mapper" - ] - } - }, - { - "id": "706c9166-d41a-4d1e-872c-45c587b0ac6b", - "name": "Full Scope Disabled", - "providerId": "scope", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "id": "bc67afe8-8f95-49eb-915c-18d11f4bbc2b", - "name": "Consent Required", - "providerId": "consent-required", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "id": "c8570184-4c4c-460f-9d78-95d36838e89a", - "name": "Trusted Hosts", - "providerId": "trusted-hosts", - "subType": "anonymous", - "subComponents": {}, - "config": { - "host-sending-registration-request-must-match": [ - "true" - ], - "client-uris-must-match": [ - "true" - ] - } - } - ], - "org.keycloak.userprofile.UserProfileProvider": [ - { - "id": "254a0e2b-b22b-4e1e-94ba-feb82f4e55f4", - "providerId": "declarative-user-profile", - "subComponents": {}, - "config": {} - } - ], - "org.keycloak.keys.KeyProvider": [ - { - "id": "f19c25ec-b555-4a60-8d98-fa41190c58d8", - "name": "hmac-generated", - "providerId": "hmac-generated", - "subComponents": {}, - "config": { - "priority": [ - "100" - ], - "algorithm": [ - "HS256" - ] - } - }, - { - "id": "25496642-de17-48e8-8b48-49982e3e0bff", - "name": "aes-generated", - "providerId": "aes-generated", - "subComponents": {}, - "config": { - "priority": [ - "100" - ] - } - }, - { - "id": "31fc839e-8c60-48b7-b9a2-66ecaa0902e5", - "name": "rsa-enc-generated", - "providerId": "rsa-enc-generated", - "subComponents": {}, - "config": { - "priority": [ - "100" - ], - "algorithm": [ - "RSA-OAEP" - ] - } - }, - { - "id": "c09f0435-5a5c-4ee6-af62-6bc7db9fbc88", - "name": "rsa-generated", - "providerId": "rsa-generated", - "subComponents": {}, - "config": { - "priority": [ - "100" - ] - } - } - ] - }, - "internationalizationEnabled": false, - "supportedLocales": [], - "authenticationFlows": [ - { - "id": "04cc2aa7-9e5b-4178-a1a2-dad58cf99367", - "alias": "Account verification options", - "description": "Method with which to verity the existing account", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-email-verification", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Verify Existing Account by Re-authentication", - "userSetupAllowed": false - } - ] - }, - { - "id": "fa4d6b27-5fac-4b3b-9cbc-badb7cfe90ed", - "alias": "Authentication Options", - "description": "Authentication options.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "basic-auth", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "basic-auth-otp", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-spnego", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 30, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "266db702-5928-4149-b2bd-701d0722eb93", - "alias": "Browser - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-otp-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "dd326252-8827-445d-a098-9ec953932387", - "alias": "Direct Grant - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "direct-grant-validate-otp", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "b8f5c247-b9ba-40c7-a14e-05a235bed46f", - "alias": "First broker login - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-otp-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "f40cbe9a-ad2a-476c-b85d-ec426ce100b2", - "alias": "Handle Existing Account", - "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-confirm-link", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Account verification options", - "userSetupAllowed": false - } - ] - }, - { - "id": "60ba180d-92f3-4195-abd4-a925121994e7", - "alias": "Reset - Conditional OTP", - "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-otp", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "0b5f7bb3-59e5-4d0e-9e8e-6d0e52984ad2", - "alias": "User creation or linking", - "description": "Flow for the existing/non-existing user alternatives", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticatorConfig": "create unique user config", - "authenticator": "idp-create-user-if-unique", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Handle Existing Account", - "userSetupAllowed": false - } - ] - }, - { - "id": "37290b7b-23f8-4653-ad2c-2593db5760f3", - "alias": "Verify Existing Account by Re-authentication", - "description": "Reauthentication of existing account", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-username-password-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "First broker login - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "2e5ceac1-9c0d-4109-b8f2-22c9efb00f0b", - "alias": "browser", - "description": "browser based authentication", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-cookie", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-spnego", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "identity-provider-redirector", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 25, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 30, - "autheticatorFlow": true, - "flowAlias": "forms", - "userSetupAllowed": false - } - ] - }, - { - "id": "c35579f7-cd70-4c66-9ee7-c21bf7ddd1e0", - "alias": "clients", - "description": "Base authentication for clients", - "providerId": "client-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "client-secret", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-jwt", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-secret-jwt", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 30, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-x509", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 40, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "c2487b50-dbf9-4536-be9d-940c8ac5eb21", - "alias": "direct grant", - "description": "OpenID Connect Resource Owner Grant", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "direct-grant-validate-username", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "direct-grant-validate-password", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 30, - "autheticatorFlow": true, - "flowAlias": "Direct Grant - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "e98419d1-4cb4-469d-a866-2adc9fdb4c6a", - "alias": "docker auth", - "description": "Used by Docker clients to authenticate against the IDP", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "docker-http-basic-authenticator", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "672acd89-be23-48ee-ac51-c5d846e77faf", - "alias": "first broker login", - "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticatorConfig": "review profile config", - "authenticator": "idp-review-profile", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "User creation or linking", - "userSetupAllowed": false - } - ] - }, - { - "id": "1099c284-d2f6-44de-b1b3-87d5cb0990c1", - "alias": "forms", - "description": "Username, password, otp and other auth forms.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-username-password-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Browser - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "d02c9502-c51d-4968-ba5d-d3771054e85a", - "alias": "http challenge", - "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "no-cookie-redirect", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Authentication Options", - "userSetupAllowed": false - } - ] - }, - { - "id": "18ee7c5d-3b4b-45c7-8d5a-761c2de30711", - "alias": "registration", - "description": "registration flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-page-form", - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": true, - "flowAlias": "registration form", - "userSetupAllowed": false - } - ] - }, - { - "id": "41c9dfb7-686d-4679-b471-abd04c08519d", - "alias": "registration form", - "description": "registration form", - "providerId": "form-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-user-creation", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "registration-profile-action", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 40, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "registration-password-action", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 50, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "registration-recaptcha-action", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 60, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "2d4c9ede-ca14-4454-bf7b-60e9c23b1951", - "alias": "reset credentials", - "description": "Reset credentials for a user if they forgot their password or something", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "reset-credentials-choose-user", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-credential-email", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-password", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 30, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 40, - "autheticatorFlow": true, - "flowAlias": "Reset - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "d1fea7bd-8e31-4b67-9cb8-b720c2b5b49c", - "alias": "saml ecp", - "description": "SAML ECP Profile Authentication Flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "http-basic-authenticator", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - } - ], - "authenticatorConfig": [ - { - "id": "519345fd-5f36-411f-ac29-9a28fea6e1f1", - "alias": "create unique user config", - "config": { - "require.password.update.after.registration": "false" - } - }, - { - "id": "2ad5fe8b-f6aa-4608-bbc2-cbf2ff218b67", - "alias": "review profile config", - "config": { - "update.profile.on.first.login": "missing" - } - } - ], - "requiredActions": [ - { - "alias": "CONFIGURE_TOTP", - "name": "Configure OTP", - "providerId": "CONFIGURE_TOTP", - "enabled": true, - "defaultAction": false, - "priority": 10, - "config": {} - }, - { - "alias": "TERMS_AND_CONDITIONS", - "name": "Terms and Conditions", - "providerId": "TERMS_AND_CONDITIONS", - "enabled": false, - "defaultAction": false, - "priority": 20, - "config": {} - }, - { - "alias": "UPDATE_PASSWORD", - "name": "Update Password", - "providerId": "UPDATE_PASSWORD", - "enabled": true, - "defaultAction": false, - "priority": 30, - "config": {} - }, - { - "alias": "UPDATE_PROFILE", - "name": "Update Profile", - "providerId": "UPDATE_PROFILE", - "enabled": true, - "defaultAction": false, - "priority": 40, - "config": {} - }, - { - "alias": "VERIFY_EMAIL", - "name": "Verify Email", - "providerId": "VERIFY_EMAIL", - "enabled": true, - "defaultAction": false, - "priority": 50, - "config": {} - }, - { - "alias": "delete_account", - "name": "Delete Account", - "providerId": "delete_account", - "enabled": false, - "defaultAction": false, - "priority": 60, - "config": {} - }, - { - "alias": "webauthn-register", - "name": "Webauthn Register", - "providerId": "webauthn-register", - "enabled": true, - "defaultAction": false, - "priority": 70, - "config": {} - }, - { - "alias": "webauthn-register-passwordless", - "name": "Webauthn Register Passwordless", - "providerId": "webauthn-register-passwordless", - "enabled": true, - "defaultAction": false, - "priority": 80, - "config": {} - }, - { - "alias": "update_user_locale", - "name": "Update User Locale", - "providerId": "update_user_locale", - "enabled": true, - "defaultAction": false, - "priority": 1000, - "config": {} - } - ], - "browserFlow": "browser", - "registrationFlow": "registration", - "directGrantFlow": "direct grant", - "resetCredentialsFlow": "reset credentials", - "clientAuthenticationFlow": "clients", - "dockerAuthenticationFlow": "docker auth", - "attributes": { - "cibaBackchannelTokenDeliveryMode": "poll", - "cibaAuthRequestedUserHint": "login_hint", - "clientOfflineSessionMaxLifespan": "0", - "oauth2DevicePollingInterval": "5", - "clientSessionIdleTimeout": "0", - "actionTokenGeneratedByUserLifespan-execute-actions": "", - "actionTokenGeneratedByUserLifespan-verify-email": "", - "clientOfflineSessionIdleTimeout": "0", - "actionTokenGeneratedByUserLifespan-reset-credentials": "", - "cibaInterval": "5", - "realmReusableOtpCode": "false", - "cibaExpiresIn": "120", - "oauth2DeviceCodeLifespan": "600", - "actionTokenGeneratedByUserLifespan-idp-verify-account-via-email": "", - "parRequestUriLifespan": "60", - "clientSessionMaxLifespan": "0" - }, - "keycloakVersion": "21.1", - "userManagedAccessAllowed": false, - "clientProfiles": { - "profiles": [] - }, - "clientPolicies": { - "policies": [] - } -} \ No newline at end of file diff --git a/edc-tests/miw-tests/src/test/resources/docker-environment/postgres/db.sh b/edc-tests/miw-tests/src/test/resources/docker-environment/postgres/db.sh deleted file mode 100755 index 0be008b1e..000000000 --- a/edc-tests/miw-tests/src/test/resources/docker-environment/postgres/db.sh +++ /dev/null @@ -1,28 +0,0 @@ -################################################################################# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# -# 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 -################################################################################# - -set -e - -psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL - CREATE DATABASE $POSTGRES_DB_NAME_MIW; - CREATE USER $POSTGRES_USERNAME_MIW WITH ENCRYPTED PASSWORD '$POSTGRES_PASSWORD_MIW'; - GRANT ALL PRIVILEGES ON DATABASE $POSTGRES_DB_NAME_MIW TO $POSTGRES_USERNAME_MIW; - \c $POSTGRES_DB_NAME_MIW - GRANT ALL ON SCHEMA public TO $POSTGRES_USERNAME_MIW; -EOSQL diff --git a/edc-tests/miw-tests/src/test/resources/docker-environment/postgres/seed.sh b/edc-tests/miw-tests/src/test/resources/docker-environment/postgres/seed.sh deleted file mode 100755 index 5c505a387..000000000 --- a/edc-tests/miw-tests/src/test/resources/docker-environment/postgres/seed.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -################################################################################# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# -# 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 -################################################################################# - -set -e - -psql -v ON_ERROR_STOP=1 --username "keycloak" --dbname "keycloak" <<-EOSQL - -- the following lines add one additional participant to MiW, used for the impersonation attack test - \c miw - INSERT INTO public.wallet (id, name, did, bpn, algorithm, did_document, created_at, modified_at, modified_from) VALUES (2, 'Another Participant', 'did:web:localhost%3A8000:BPNL000000000042', 'BPNL000000000042', 'ED25519', '{"verificationMethod":[{"publicKeyJwk":{"kty":"OKP","crv":"Ed25519","x":"Xok4qFXhNjMC3l-VHoQBJ_RHhtDmxevaoN13PE3j8MY"},"controller":"did:web:localhost%3A8000:BPNL000000000000","id":"did:web:localhost%3A8000:BPNL000000000000#","type":"JsonWebKey2020"}],"@context":"https://www.w3.org/ns/did/v1","id":"did:web:localhost%3A8000:BPNL000000000000"}', '2023-06-29 13:49:07.138000', '2023-06-29 13:49:07.140000', null); - INSERT INTO public.issuers_credential (id, holder_did, issuer_did, credential_id, credential_data, credential_type, created_at, modified_at, modified_from) VALUES (3, 'did:web:localhost%3A8000:BPNL000000000042', 'did:web:localhost%3A8000:BPNL000000000042', 'a043c406-d51d-4672-ad89-517c68d025f9', '{"issuanceDate":"2023-06-29T13:49:11Z","credentialSubject":[{"holderIdentifier":"BPNL000000000000","id":"did:web:localhost%3A8000:BPNL000000000000","type":"SummaryCredential","items":["BpnCredential"],"contractTemplates":"https://public.catena-x.org/contracts/"}],"id":"a043c406-d51d-4672-ad89-517c68d025f9","proof":{"assertionMethod":"did:web:localhost%3A8000:BPNL000000000000#","proofPurpose":"proofPurpose","type":"JsonWebSignature2020","created":"2023-06-29T13:49:11Z","jws":"eyJhbGciOiJFZERTQSJ9..waFlAQyE42TddNz0v4q_MkIbSgbjexDQqdt-k6LEQG3BvhE1Adj6SIERtUzmYowuLtdcTBbwVAROf9yzpfNMAg"},"type":["VerifiableCredential","SummaryCredential"],"@context":["https://www.w3.org/2018/credentials/v1","https://catenax-ng.github.io/product-core-schemas/SummaryVC.json"],"issuer":"did:web:localhost%3A8000:BPNL000000000000","expirationDate":"2025-01-01T00:00:00Z"}', 'SummaryCredential', '2023-06-29 13:49:11.870000', '2023-06-29 13:49:11.925000', null); - INSERT INTO public.holders_credential (id, holder_did, issuer_did, credential_id, credential_data, credential_type, created_at, modified_at, modified_from) VALUES (3, 'did:web:localhost%3A8000:BPNL000000000042', 'did:web:localhost%3A8000:BPNL000000000042', 'a043c406-d51d-4672-ad89-517c68d025f9', '{"issuanceDate":"2023-06-29T13:49:11Z","credentialSubject":[{"holderIdentifier":"BPNL000000000000","id":"did:web:localhost%3A8000:BPNL000000000000","type":"SummaryCredential","items":["BpnCredential"],"contractTemplates":"https://public.catena-x.org/contracts/"}],"id":"a043c406-d51d-4672-ad89-517c68d025f9","proof":{"assertionMethod":"did:web:localhost%3A8000:BPNL000000000000#","proofPurpose":"proofPurpose","type":"JsonWebSignature2020","created":"2023-06-29T13:49:11Z","jws":"eyJhbGciOiJFZERTQSJ9..waFlAQyE42TddNz0v4q_MkIbSgbjexDQqdt-k6LEQG3BvhE1Adj6SIERtUzmYowuLtdcTBbwVAROf9yzpfNMAg"},"type":["VerifiableCredential","SummaryCredential"],"@context":["https://www.w3.org/2018/credentials/v1","https://catenax-ng.github.io/product-core-schemas/SummaryVC.json"],"issuer":"did:web:localhost%3A8000:BPNL000000000000","expirationDate":"2025-01-01T00:00:00Z"}', 'SummaryCredential', '2023-06-29 13:49:11.870000', '2023-06-29 13:49:11.925000', null); -EOSQL diff --git a/edc-tests/miw-tests/src/test/resources/env-files/env.docker b/edc-tests/miw-tests/src/test/resources/env-files/env.docker deleted file mode 100644 index 30c2fe9a2..000000000 --- a/edc-tests/miw-tests/src/test/resources/env-files/env.docker +++ /dev/null @@ -1,52 +0,0 @@ -# Personal development data -GITHUB_USERNAME= -GITHUB_TOKEN= - -# Build -SKIP_GRADLE_TASKS_PARAM="-x jacocoTestCoverageVerification -x test" -DEV_ENVIRONMENT=docker - -# Docker: Postgres config -POSTGRES_DB=keycloak -POSTGRES_USER=keycloak -POSTGRES_PASSWORD=postgres -POSTGRES_DB_NAME_MIW=miw -POSTGRES_USERNAME_MIW=miw_user -POSTGRES_PASSWORD_MIW=postgres - -# Docker: Keycloak config -KEYCLOAK_MIW_PUBLIC_CLIENT=miw_public -DB_DATABASE=keycloak -KEYCLOAK_ADMIN=admin -KEYCLOAK_ADMIN_PASSWORD=admin -KC_HOSTNAME=keycloak -KC_HEALTH_ENABLED=true - -# Docker: App config -KEYCLOAK_CLIENT_ID=miw_private_client -ENCRYPTION_KEY=Woh9waid4Ei5eez0aitieghoow9so4oe -AUTHORITY_WALLET_BPN=BPNL000000000000 -AUTHORITY_WALLET_DID=did:web:miw:BPNL000000000000 -AUTHORITY_WALLET_NAME=Catena-X -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:8000 -ENFORCE_HTTPS_IN_DID_RESOLUTION=false - -######### DON'T MODIFY ANYTHING BELOW THIS LINE !!! ######### -AUTH_SERVER_URL=http://keycloak:8080 -DEV_ENVIRONMENT=docker -APPLICATION_PORT=8000 -MANAGEMENT_PORT=8090 -APPLICATION_ENVIRONMENT=dev -DB_HOST=postgres -DB_PORT=5432 -USE_SSL=false -DB_USER=${POSTGRES_USER} -DB_NAME=${POSTGRES_DB_NAME_MIW} -DB_USER_NAME=${POSTGRES_USERNAME_MIW} -DB_PASSWORD=${POSTGRES_PASSWORD_MIW} -ORG_GRADLE_PROJECT_githubUserName=${GITHUB_USERNAME} -ORG_GRADLE_PROJECT_githubToken=${GITHUB_TOKEN} diff --git a/edc-tests/runtime/extensions/build.gradle.kts b/edc-tests/runtime/extensions/build.gradle.kts index 1a54ff9ca..be04ee59e 100644 --- a/edc-tests/runtime/extensions/build.gradle.kts +++ b/edc-tests/runtime/extensions/build.gradle.kts @@ -25,10 +25,8 @@ plugins { dependencies { implementation(libs.edc.core.controlplane) - implementation(libs.edc.util) + implementation(libs.edc.lib.util) implementation(libs.edc.spi.web) - // for the controller - implementation(libs.jakarta.rsApi) } diff --git a/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java b/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java deleted file mode 100644 index c29c223a6..000000000 --- a/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.lifecycle; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -@Path("/consumer") -public class ConsumerEdrHandlerController { - - private final Monitor monitor; - private final Map dataReference; - - public ConsumerEdrHandlerController(Monitor monitor) { - this.monitor = monitor; - dataReference = new HashMap<>(); - } - - @Path("/datareference") - @POST - @Consumes({MediaType.APPLICATION_JSON}) - public void pushDataReference(EndpointDataReference edr) { - monitor.debug("Received new endpoint data reference with url " + edr.getEndpoint()); - dataReference.put(edr.getId(), edr); - } - - @Path("/datareference/{id}") - @GET - @Produces({MediaType.APPLICATION_JSON}) - public EndpointDataReference getDataReference(@PathParam("id") String id) { - return Optional.ofNullable(dataReference.get(id)).orElseGet(() -> { - monitor.warning("No EndpointDataReference found with id " + id); - return null; - }); - } - -} diff --git a/edc-tests/runtime/extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-tests/runtime/extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 1db031d31..4e2b9da92 100644 --- a/edc-tests/runtime/extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-tests/runtime/extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -17,5 +17,4 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# -org.eclipse.tractusx.edc.lifecycle.ConsumerServicesExtension org.eclipse.tractusx.edc.lifecycle.VaultSeedExtension diff --git a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/CredentialsJsonLdExtension.java b/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/CredentialsJsonLdExtension.java index 061a73a05..584ae5308 100644 --- a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/CredentialsJsonLdExtension.java +++ b/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/CredentialsJsonLdExtension.java @@ -30,11 +30,11 @@ @Extension("Credentials JSON LD extension") public class CredentialsJsonLdExtension implements ServiceExtension { - public static final String BUSINESS_PARTNER_DATA = "https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json"; + public static final String BUSINESS_PARTNER_DATA = "https://w3id.org/catenax/credentials"; @Inject private JsonLd jsonLd; - + @Override public void initialize(ServiceExtensionContext context) { diff --git a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/ih/IdentityHubExtension.java b/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/ih/IdentityHubExtension.java index 2a5bd9a74..c54b60b22 100644 --- a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/ih/IdentityHubExtension.java +++ b/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/ih/IdentityHubExtension.java @@ -24,14 +24,12 @@ import org.eclipse.edc.runtime.metamodel.annotation.Provider; import org.eclipse.edc.spi.system.ServiceExtension; -import java.util.List; - @Extension("Identity Hub extension for testing") public class IdentityHubExtension implements ServiceExtension { @Provider public ScopeToCriterionTransformer scopeToCriterionTransformer() { - return new TxScopeToCriterionTransformer(List.of("MembershipCredential", "DismantlerCredential", "BpnCredential")); + return new TxScopeToCriterionTransformer(); } } diff --git a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/ih/TxScopeToCriterionTransformer.java b/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/ih/TxScopeToCriterionTransformer.java index a65434fc5..df1c0b577 100644 --- a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/ih/TxScopeToCriterionTransformer.java +++ b/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/ih/TxScopeToCriterionTransformer.java @@ -34,17 +34,12 @@ */ public class TxScopeToCriterionTransformer implements ScopeToCriterionTransformer { - public static final String TYPE_OPERAND = "verifiableCredential.credential.types"; + public static final String TYPE_OPERAND = "verifiableCredential.credential.type"; public static final String ALIAS_LITERAL = "org.eclipse.tractusx.vc.type"; public static final String CONTAINS_OPERATOR = "contains"; private static final String SCOPE_SEPARATOR = ":"; - private final List knownCredentialTypes; private final List allowedOperations = List.of("read", "*", "all"); - public TxScopeToCriterionTransformer(List knownCredentialTypes) { - this.knownCredentialTypes = knownCredentialTypes; - } - @Override public Result transform(String scope) { var tokens = tokenize(scope); @@ -52,14 +47,7 @@ public Result transform(String scope) { return failure("Scope string cannot be converted: %s".formatted(tokens.getFailureDetail())); } var credentialType = tokens.getContent()[1]; - - if (!knownCredentialTypes.contains(credentialType)) { - //select based on the credentialSubject.useCaseType property - // even though "claims" is a Map, we need to access it using the dot notation. See ReflectionUtil.java - return success(new Criterion("verifiableCredential.credential.credentialSubject.useCaseType", "=", credentialType)); - } else { - return success(new Criterion(TYPE_OPERAND, CONTAINS_OPERATOR, credentialType)); - } + return success(new Criterion(TYPE_OPERAND, CONTAINS_OPERATOR, credentialType)); } protected Result tokenize(String scope) { diff --git a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/CredentialPolicyEvaluationExtension.java b/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/CredentialPolicyEvaluationExtension.java deleted file mode 100644 index d3cfa820c..000000000 --- a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/CredentialPolicyEvaluationExtension.java +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iatp.policy; - -import org.eclipse.edc.policy.engine.spi.AtomicConstraintFunction; -import org.eclipse.edc.policy.engine.spi.PolicyEngine; -import org.eclipse.edc.policy.engine.spi.RuleBindingRegistry; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.spi.agent.ParticipantAgentService; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; - -import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_SCHEMA; -import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_CREDENTIAL_NAMESPACE; - -@Extension("TX credential policy evaluation extension") -public class CredentialPolicyEvaluationExtension implements ServiceExtension { - - public static final String MEMBERSHIP_CONSTRAINT_KEY = "Membership"; - private static final String CATALOG_SCOPE = "catalog"; - private static final String NEGOTIATION_SCOPE = "contract.negotiation"; - private static final String TRANSFER_PROCESS_SCOPE = "transfer.process"; - - @Inject - private PolicyEngine policyEngine; - - @Inject - private RuleBindingRegistry ruleBindingRegistry; - - - @Inject - private ParticipantAgentService participantAgentService; - - @Override - public void initialize(ServiceExtensionContext context) { - - var fct = new MembershipCredentialEvaluationFunction(); - - bindPermissionFunction(fct, TRANSFER_PROCESS_SCOPE, MEMBERSHIP_CONSTRAINT_KEY); - bindPermissionFunction(fct, NEGOTIATION_SCOPE, MEMBERSHIP_CONSTRAINT_KEY); - bindPermissionFunction(fct, CATALOG_SCOPE, MEMBERSHIP_CONSTRAINT_KEY); - - registerUseCase("pcf"); - registerUseCase("traceability"); - registerUseCase("sustainability"); - registerUseCase("quality"); - registerUseCase("resiliency"); - - participantAgentService.register(new IdentityExtractor()); - } - - private void bindPermissionFunction(AtomicConstraintFunction function, String scope, String constraintType) { - ruleBindingRegistry.bind("USE", scope); - ruleBindingRegistry.bind(ODRL_SCHEMA + "use", scope); - ruleBindingRegistry.bind(constraintType, scope); - ruleBindingRegistry.bind(TX_CREDENTIAL_NAMESPACE + constraintType, scope); - - policyEngine.registerFunction(scope, Permission.class, constraintType, function); - policyEngine.registerFunction(scope, Permission.class, TX_CREDENTIAL_NAMESPACE + constraintType, function); - } - - private void registerUseCase(String useCaseName) { - var frameworkFunction = new FrameworkCredentialEvaluationFunction(useCaseName); - var usecase = frameworkFunction.key(); - - bindPermissionFunction(frameworkFunction, TRANSFER_PROCESS_SCOPE, usecase); - bindPermissionFunction(frameworkFunction, NEGOTIATION_SCOPE, usecase); - bindPermissionFunction(frameworkFunction, CATALOG_SCOPE, usecase); - } -} diff --git a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/FrameworkCredentialEvaluationFunction.java b/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/FrameworkCredentialEvaluationFunction.java deleted file mode 100644 index ef0e007c1..000000000 --- a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/FrameworkCredentialEvaluationFunction.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iatp.policy; - -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.spi.agent.ParticipantAgent; - -public class FrameworkCredentialEvaluationFunction extends BaseCredentialEvaluationFunction { - - public static final String CONTRACT_VERSION = "contractVersion"; - public static final String HOLDER_IDENTIFIER = "holderIdentifier"; - public static final String USE_CASE_TYPE = "useCaseType"; - public static final String CONTRACT_TEMPLATE = "contractTemplate"; - private final String usecase; - private final String useCaseCredential; - - public FrameworkCredentialEvaluationFunction(String usecase) { - this.usecase = usecase; - this.useCaseCredential = "%sCredential".formatted(capitalize(usecase)); - } - - @Override - public boolean evaluate(Operator operator, Object rightOperand, Permission permission, PolicyContext policyContext) { - if (!operator.equals(Operator.EQ)) { - policyContext.reportProblem("Cannot evaluate operator %s, only %s is supported".formatted(operator, Operator.EQ)); - return false; - } - if (!"active".equalsIgnoreCase(rightOperand.toString())) { - policyContext.reportProblem("Use case credentials only support right operand 'active', but found '%s'".formatted(operator.toString())); - return false; - } - var pa = policyContext.getContextData(ParticipantAgent.class); - if (pa == null) { - policyContext.reportProblem("ParticipantAgent not found on PolicyContext"); - return false; - } - - var claims = pa.getClaims(); - - var version = getClaim(String.class, CONTRACT_VERSION, claims); - var holderIdentifier = getClaim(String.class, HOLDER_IDENTIFIER, claims); - var contractTemplate = getClaim(String.class, CONTRACT_TEMPLATE, claims); - var useCaseType = getClaim(String.class, USE_CASE_TYPE, claims); - - return version != null && holderIdentifier != null && contractTemplate != null && useCaseCredential.equals(useCaseType); - } - - public String key() { - return "FrameworkAgreement.%s".formatted(usecase); - } - - private String capitalize(String input) { - return input.substring(0, 1).toUpperCase() + input.substring(1).toLowerCase(); - } -} diff --git a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/IdentityExtractor.java b/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/IdentityExtractor.java deleted file mode 100644 index ca5bdcda0..000000000 --- a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/IdentityExtractor.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iatp.policy; - -import org.eclipse.edc.spi.agent.ParticipantAgentServiceExtension; -import org.eclipse.edc.spi.iam.ClaimToken; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; - -import static org.eclipse.edc.spi.agent.ParticipantAgent.PARTICIPANT_IDENTITY; - -public class IdentityExtractor implements ParticipantAgentServiceExtension { - @Override - public @NotNull Map attributesFor(ClaimToken claimToken) { - return Map.of(PARTICIPANT_IDENTITY, getClaim(String.class, "holderIdentifier", claimToken.getClaims())); - } - - protected T getClaim(Class type, String postfix, Map claims) { - return claims.entrySet().stream().filter(e -> e.getKey().endsWith(postfix)) - .findFirst() - .map(Map.Entry::getValue) - .map(type::cast) - .orElse(null); - } -} diff --git a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/MembershipCredentialEvaluationFunction.java b/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/MembershipCredentialEvaluationFunction.java deleted file mode 100644 index eca1c8315..000000000 --- a/edc-tests/runtime/iatp/iatp-extensions/src/main/java/org/eclipse/tractusx/edc/iatp/policy/MembershipCredentialEvaluationFunction.java +++ /dev/null @@ -1,91 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iatp.policy; - -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Operator; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.spi.agent.ParticipantAgent; - -import java.time.Instant; -import java.util.Map; -import java.util.function.BiFunction; - -public class MembershipCredentialEvaluationFunction extends BaseCredentialEvaluationFunction { - - public static final String START_TIME = "startTime"; - public static final String MEMBER_OF = "memberOf"; - public static final String STATUS = "status"; - public static final String ACTIVE = "Active"; - public static final String CATENA_X = "Catena-X"; - private final Map> claimsCheckers = Map.of( - STATUS, this::validateStatus, - MEMBER_OF, this::validateMemberOf, - START_TIME, this::validateStartTime - ); - - @Override - public boolean evaluate(Operator operator, Object rightOperand, Permission permission, PolicyContext policyContext) { - - if (!operator.equals(Operator.EQ)) { - policyContext.reportProblem("Invalid operator '%s', only accepts '%s'".formatted(operator, Operator.EQ)); - return false; - } - var pa = policyContext.getContextData(ParticipantAgent.class); - if (pa == null) { - policyContext.reportProblem("No ParticipantAgent found on context."); - return false; - } - var claims = pa.getClaims(); - if ("active".equalsIgnoreCase(rightOperand.toString())) { - return claimsCheckers.entrySet().stream() - .reduce(true, (i, checker) -> checker.getValue().apply(policyContext, getClaim(String.class, checker.getKey(), claims)), (first, left) -> first && left); - } - return false; - } - - private boolean validateMemberOf(PolicyContext policyContext, String memberOf) { - return validateField(policyContext, MEMBER_OF, CATENA_X, memberOf); - - } - - private boolean validateStatus(PolicyContext policyContext, String status) { - return validateField(policyContext, STATUS, ACTIVE, status); - } - - private boolean validateField(PolicyContext policyContext, String field, String expectedValue, String currentValue) { - if (expectedValue.equals(currentValue)) { - return true; - } else { - policyContext.reportProblem("Invalid membership %s '%s', only accepts '%s'".formatted(field, currentValue, expectedValue)); - return false; - } - } - - private boolean validateStartTime(PolicyContext policyContext, String since) { - var membershipStartDate = Instant.parse(since); - if (membershipStartDate.isBefore(Instant.now())) { - return true; - } else { - policyContext.reportProblem("Invalid membership start date"); - return false; - } - } -} diff --git a/edc-tests/runtime/iatp/iatp-extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-tests/runtime/iatp/iatp-extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index db5562906..3fd86f849 100644 --- a/edc-tests/runtime/iatp/iatp-extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-tests/runtime/iatp/iatp-extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -19,5 +19,4 @@ org.eclipse.tractusx.edc.iatp.TestAudienceMapperExtension org.eclipse.tractusx.edc.iatp.ih.IdentityHubExtension -org.eclipse.tractusx.edc.iatp.policy.CredentialPolicyEvaluationExtension org.eclipse.tractusx.edc.iatp.CredentialsJsonLdExtension diff --git a/edc-tests/runtime/iatp/iatp-extensions/src/main/resources/cx-credentials-context.json b/edc-tests/runtime/iatp/iatp-extensions/src/main/resources/cx-credentials-context.json index 110c98f99..dd90f3745 100644 --- a/edc-tests/runtime/iatp/iatp-extensions/src/main/resources/cx-credentials-context.json +++ b/edc-tests/runtime/iatp/iatp-extensions/src/main/resources/cx-credentials-context.json @@ -1,48 +1,60 @@ { "@context": { "@version": 1.1, - "ctx": "https://raw.githubusercontent.com/catenax-ng/product-core-schemas/main/businessPartnerData#", - "schema": "https://schema.org/", + "@protected": true, + "cred": "https://www.w3.org/2018/credentials#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "cx-credentials": "https://w3id.org/catenax/credentials/", + "BehavioralTwinCredential": { + "@id": "cx-credentials:BehavioralTwinCredential" + }, "BpnCredential": { - "@id": "ctx:BpnCredential", - "@context": { - "@version": 1.1, - "id": "@id", - "type": "@type", - "bpn": "schema:identifier" - } + "@id": "cx-credentials:BpnCredential" + }, + "DismantlerCredential": { + "@id": "cx-credentials:DismantlerCredential" }, "MembershipCredential": { - "@id": "ctx:MembershipCredential", - "@context": { - "@version": 1.1, - "id": "@id", - "type": "@type", - "startTime": { - "@id": "schema:startTime", - "@type": "schema:DateTime" - }, - "memberOf": { - "@id": "schema:memberOf", - "@type": "schema:Text" - }, - "status": { - "@id": "ctx:status", - "@type": "schema:Text" - }, - "holderIdentifier": { - "@id": "ctx:holderIdentifier" - } - } - }, - "UseCaseFrameworkCondition": { - "@id": "ctx:UseCaseFrameworkCondition" - }, - "contractVersion": "ctx:contractVersion", - "contractTemplate": "ctx:contractTemplate", - "useCaseType": "ctx:useCaseType", + "@id": "cx-credentials:MembershipCredential" + }, + "PcfCredential": { + "@id": "cx-credentials:PcfCredential" + }, + "ResiliencyCredential": { + "@id": "cx-credentials:ResiliencyCredential" + }, + "QualityCredential": { + "@id": "cx-credentials:QualityCredential" + }, + "SustainabilityCredential": { + "@id": "cx-credentials:SustainabilityCredential" + }, + "TraceabilityCredential": { + "@id": "cx-credentials:TraceabilityCredential" + }, + "activityType": { + "@id": "cx-credentials:activityType", + "@type": "xsd:string" + }, + "allowedVehicleBrands": { + "@id": "cx-credentials:allowedVehicleBrands", + "@container": "@set" + }, + "bpn": { + "@id": "cx-credentials:bpn", + "@type": "xsd:string" + }, + "contractTemplate": { + "@id": "cx-credentials:contractTemplate", + "@type": "@id" + }, + "contractVersion": { + "@id": "cx-credentials:contractVersion", + "@type": "@id" + }, "holderIdentifier": { - "@id": "ctx:holderIdentifier" + "@id": "cx-credentials:holderIdentifier", + "@type": "xsd:string" } } } diff --git a/edc-tests/runtime/runtime-memory-ssi/README.md b/edc-tests/runtime/iatp/runtime-memory-iatp-dim-ih/README.md similarity index 100% rename from edc-tests/runtime/runtime-memory-ssi/README.md rename to edc-tests/runtime/iatp/runtime-memory-iatp-dim-ih/README.md diff --git a/edc-tests/runtime/runtime-postgresql-hashicorp/build.gradle.kts b/edc-tests/runtime/iatp/runtime-memory-iatp-dim-ih/build.gradle.kts similarity index 57% rename from edc-tests/runtime/runtime-postgresql-hashicorp/build.gradle.kts rename to edc-tests/runtime/iatp/runtime-memory-iatp-dim-ih/build.gradle.kts index 692248c43..792511dd4 100644 --- a/edc-tests/runtime/runtime-postgresql-hashicorp/build.gradle.kts +++ b/edc-tests/runtime/iatp/runtime-memory-iatp-dim-ih/build.gradle.kts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -22,20 +22,20 @@ plugins { id("application") } - dependencies { // use basic (all in-mem) control plane - implementation(project(":edc-controlplane:edc-controlplane-postgresql-hashicorp-vault")) { - exclude(module = "data-encryption") - exclude(module = "json-ld-core") - exclude(module = "ssi-identity-core") - exclude(module = "ssi-miw-credential-client") - exclude(module = "ssi-identity-extractor") - exclude(module = "cx-policy") + implementation(project(":edc-controlplane:edc-controlplane-base")) { + exclude(module = "bdrs-client") + exclude("org.eclipse.edc", "identity-trust-issuers-configuration") } + implementation(project(":core:json-ld-core")) + implementation(project(":edc-extensions:cx-policy")) + implementation(project(":edc-extensions:iatp:tx-iatp")) + implementation(project(":edc-extensions:iatp:tx-iatp-sts-dim")) implementation(project(":edc-tests:runtime:extensions")) + implementation(project(":edc-tests:runtime:iatp:iatp-extensions")) // use basic (all in-mem) data plane runtimeOnly(project(":edc-dataplane:edc-dataplane-base")) { @@ -43,8 +43,22 @@ dependencies { } implementation(libs.edc.core.controlplane) + implementation(libs.edc.core.identitytrust) + implementation(libs.edc.core.did) + implementation(libs.edc.identity.trust.transform) + implementation(libs.edc.identity.trust.issuers.configuration) + implementation(libs.edc.auth.oauth2.client) + implementation(libs.edc.ih.api) + implementation(libs.edc.ih.credentials) + implementation(libs.edc.ih.keypairs) + implementation(libs.edc.ih.participants) + implementation(libs.edc.ih.did) + // for the controller implementation(libs.jakarta.rsApi) + + runtimeOnly(libs.edc.lib.store) + } application { diff --git a/edc-tests/runtime/runtime-memory-ssi/build.gradle.kts b/edc-tests/runtime/iatp/runtime-memory-iatp-dim/build.gradle.kts similarity index 73% rename from edc-tests/runtime/runtime-memory-ssi/build.gradle.kts rename to edc-tests/runtime/iatp/runtime-memory-iatp-dim/build.gradle.kts index fdadffe79..c9ebd1ba2 100644 --- a/edc-tests/runtime/runtime-memory-ssi/build.gradle.kts +++ b/edc-tests/runtime/iatp/runtime-memory-iatp-dim/build.gradle.kts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -22,30 +22,31 @@ plugins { id("application") } - dependencies { // use basic (all in-mem) control plane - implementation(project(":edc-controlplane:edc-controlplane-base")) { - exclude(module = "data-encryption") - } - implementation(project(":core:json-ld-core")) - - - implementation(project(":edc-extensions:ssi:ssi-identity-core")) - implementation(project(":edc-extensions:ssi:ssi-miw-credential-client")); - implementation(project(":edc-extensions:ssi:ssi-identity-extractor")) + implementation(project(":edc-controlplane:edc-controlplane-base")) implementation(project(":edc-extensions:cx-policy")) + implementation(project(":core:json-ld-core")) + implementation(project(":edc-extensions:iatp:tx-iatp")) + implementation(project(":edc-extensions:iatp:tx-iatp-sts-dim")) implementation(project(":edc-tests:runtime:extensions")) + implementation(project(":edc-tests:runtime:iatp:iatp-extensions")) // use basic (all in-mem) data plane runtimeOnly(project(":edc-dataplane:edc-dataplane-base")) { exclude("org.eclipse.edc", "api-observability") } - implementation(libs.edc.core.controlplane) + implementation(libs.edc.core.identitytrust) + implementation(libs.edc.core.did) + implementation(libs.edc.identity.did.web) + implementation(libs.edc.identity.trust.transform) + implementation(libs.edc.identity.trust.issuers.configuration) + implementation(libs.edc.auth.oauth2.client) + // for the controller implementation(libs.jakarta.rsApi) } diff --git a/edc-tests/runtime/iatp/runtime-memory-iatp-ih/build.gradle.kts b/edc-tests/runtime/iatp/runtime-memory-iatp-ih/build.gradle.kts index 329cbf632..8b0fe5b63 100644 --- a/edc-tests/runtime/iatp/runtime-memory-iatp-ih/build.gradle.kts +++ b/edc-tests/runtime/iatp/runtime-memory-iatp-ih/build.gradle.kts @@ -26,12 +26,11 @@ dependencies { // use basic (all in-mem) control plane implementation(project(":edc-controlplane:edc-controlplane-base")) { - exclude(module = "data-encryption") - exclude(module = "ssi-identity-core") - exclude(module = "ssi-miw-credential-client") - exclude(module = "ssi-identity-extractor") - exclude(module = "cx-policy") + exclude(module = "bdrs-client") + exclude(module = "tx-iatp-sts-dim") + exclude("org.eclipse.edc", "identity-trust-issuers-configuration") } + implementation(project(":edc-extensions:cx-policy")) implementation(project(":core:json-ld-core")) implementation(project(":edc-extensions:iatp:tx-iatp")) @@ -44,10 +43,10 @@ dependencies { } implementation(libs.edc.core.controlplane) - implementation(libs.edc.identity.core.trust) - implementation(libs.edc.identity.core.did) + implementation(libs.edc.core.identitytrust) + implementation(libs.edc.core.did) implementation(libs.edc.identity.trust.transform) - implementation(libs.edc.identity.trust.sts.remote) + implementation(libs.edc.identity.trust.sts.remote.client) implementation(libs.edc.identity.trust.issuers.configuration) implementation(libs.edc.auth.oauth2.client) implementation(libs.edc.ih.api) diff --git a/edc-tests/runtime/iatp/runtime-memory-sts/build.gradle.kts b/edc-tests/runtime/iatp/runtime-memory-sts/build.gradle.kts index 00cc47b67..796408b2b 100644 --- a/edc-tests/runtime/iatp/runtime-memory-sts/build.gradle.kts +++ b/edc-tests/runtime/iatp/runtime-memory-sts/build.gradle.kts @@ -26,20 +26,23 @@ dependencies { // use basic (all in-mem) control plane implementation(project(":edc-controlplane:edc-controlplane-base")) { - exclude(module = "data-encryption") exclude(module = "ssi-identity-core") exclude(module = "ssi-miw-credential-client") exclude(module = "ssi-identity-extractor") - exclude(module = "cx-policy") + exclude(module = "tx-iatp-sts-dim") + exclude("org.eclipse.edc", "identity-trust-issuers-configuration") } implementation(project(":core:json-ld-core")) implementation(project(":edc-tests:runtime:extensions")) implementation(libs.edc.iam.mock) + implementation(libs.edc.spi.keys) // for the controller implementation(libs.jakarta.rsApi) implementation(libs.bundles.edc.sts) + implementation(libs.edc.identity.trust.sts.embedded) + implementation(libs.edc.core.token) } application { diff --git a/edc-tests/runtime/iatp/runtime-memory-sts/src/main/java/org/eclipse/tractusx/edc/lifecycle/SecureTokenServiceExtension.java b/edc-tests/runtime/iatp/runtime-memory-sts/src/main/java/org/eclipse/tractusx/edc/lifecycle/SecureTokenServiceExtension.java new file mode 100644 index 000000000..31aa5a597 --- /dev/null +++ b/edc-tests/runtime/iatp/runtime-memory-sts/src/main/java/org/eclipse/tractusx/edc/lifecycle/SecureTokenServiceExtension.java @@ -0,0 +1,57 @@ +/******************************************************************************** + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.lifecycle; + +import org.eclipse.edc.iam.identitytrust.spi.SecureTokenService; +import org.eclipse.edc.iam.identitytrust.sts.embedded.EmbeddedSecureTokenService; +import org.eclipse.edc.keys.spi.PrivateKeyResolver; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.token.JwtGenerationService; + +import java.security.PrivateKey; +import java.time.Clock; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +public class SecureTokenServiceExtension implements ServiceExtension { + public static final String STS_PRIVATE_KEY_ALIAS = "edc.iam.sts.privatekey.alias"; + public static final String STS_PUBLIC_KEY_ID = "edc.iam.sts.publickey.id"; + private static final String STS_TOKEN_EXPIRATION = "edc.iam.sts.token.expiration"; // in minutes + private static final int DEFAULT_STS_TOKEN_EXPIRATION_MIN = 5; + @Inject + private PrivateKeyResolver privateKeyResolver; + + @Inject + private Clock clock; + + @Provider + public SecureTokenService createEmbeddedSts(ServiceExtensionContext context) { + var tokenExpiration = context.getSetting(STS_TOKEN_EXPIRATION, DEFAULT_STS_TOKEN_EXPIRATION_MIN); + var publicKeyId = context.getSetting(STS_PUBLIC_KEY_ID, null); + var privKeyAlias = context.getSetting(STS_PRIVATE_KEY_ALIAS, null); + + Supplier supplier = () -> privateKeyResolver.resolvePrivateKey(privKeyAlias).orElseThrow(f -> new EdcException("This EDC instance is not operational due to the following error: %s".formatted(f.getFailureDetail()))); + return new EmbeddedSecureTokenService(new JwtGenerationService(), supplier, () -> publicKeyId, clock, TimeUnit.MINUTES.toSeconds(tokenExpiration)); + } +} diff --git a/edc-extensions/data-encryption/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-tests/runtime/iatp/runtime-memory-sts/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 93% rename from edc-extensions/data-encryption/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-tests/runtime/iatp/runtime-memory-sts/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 13333ee69..bb3dc7fbb 100644 --- a/edc-extensions/data-encryption/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-tests/runtime/iatp/runtime-memory-sts/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -17,5 +17,4 @@ # SPDX-License-Identifier: Apache-2.0 ################################################################################# -org.eclipse.tractusx.edc.data.encryption.TxEncryptorExtension - +org.eclipse.tractusx.edc.lifecycle.SecureTokenServiceExtension diff --git a/edc-tests/runtime/runtime-memory-signaling/README.md b/edc-tests/runtime/runtime-memory-signaling/README.md new file mode 100644 index 000000000..2f9593a75 --- /dev/null +++ b/edc-tests/runtime/runtime-memory-signaling/README.md @@ -0,0 +1,3 @@ +# In-Memory Runtime for Testing Purposes + +This module provides a very small, purely in-mem runtime to execute tests against. Not intended for anything other than testing! diff --git a/edc-tests/runtime/runtime-memory-signaling/build.gradle.kts b/edc-tests/runtime/runtime-memory-signaling/build.gradle.kts new file mode 100644 index 000000000..518aabfcd --- /dev/null +++ b/edc-tests/runtime/runtime-memory-signaling/build.gradle.kts @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 { + `java-library` + id("application") +} + + +dependencies { + + // use basic (all in-mem) control plane + implementation(project(":edc-tests:runtime:runtime-memory")) +} + +application { + mainClass.set("org.eclipse.edc.boot.system.runtime.BaseRuntime") +} + +edcBuild { + publish.set(false) +} diff --git a/edc-tests/runtime/runtime-memory/build.gradle.kts b/edc-tests/runtime/runtime-memory/build.gradle.kts index a2cf6f64a..b2ff89caf 100644 --- a/edc-tests/runtime/runtime-memory/build.gradle.kts +++ b/edc-tests/runtime/runtime-memory/build.gradle.kts @@ -31,9 +31,14 @@ dependencies { exclude(module = "ssi-identity-core") exclude(module = "ssi-miw-credential-client") exclude(module = "ssi-identity-extractor") - exclude(module = "cx-policy") + exclude(module = "tx-iatp-sts-dim") + exclude(module = "tx-iatp") + exclude("org.eclipse.edc", "identity-trust-issuers-configuration") } + // use an embedded STS + runtimeOnly(libs.edc.identity.trust.sts.embedded) + implementation(project(":edc-tests:runtime:extensions")) // use basic (all in-mem) data plane diff --git a/edc-tests/runtime/runtime-postgresql-hashicorp/README.md b/edc-tests/runtime/runtime-postgresql-hashicorp/README.md deleted file mode 100644 index 7ec7be084..000000000 --- a/edc-tests/runtime/runtime-postgresql-hashicorp/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# PostgreSQL + Hashicorp Vault Runtime for Testing Purposes - -This module provides a very small,runtime using PostgreSQL as persistence backend and Hashicorp Vault to execute tests against. Not intended for anything other than testing! diff --git a/edc-tests/runtime/runtime-postgresql/build.gradle.kts b/edc-tests/runtime/runtime-postgresql/build.gradle.kts index 993e47481..39f49763d 100644 --- a/edc-tests/runtime/runtime-postgresql/build.gradle.kts +++ b/edc-tests/runtime/runtime-postgresql/build.gradle.kts @@ -27,20 +27,22 @@ dependencies { // use basic (all in-mem) control plane implementation(project(":edc-controlplane:edc-controlplane-postgresql-hashicorp-vault")) { - exclude(module = "data-encryption") exclude(module = "json-ld-core") exclude(module = "ssi-identity-core") exclude(module = "ssi-miw-credential-client") exclude(module = "ssi-identity-extractor") - exclude(module = "cx-policy") + exclude(module = "tx-iatp-sts-dim") exclude(group = "org.eclipse.edc", "vault-hashicorp") + exclude(module = "tx-iatp") + exclude("org.eclipse.edc", "identity-trust-issuers-configuration") } implementation(project(":edc-tests:runtime:extensions")) // use basic (all in-mem) data plane - runtimeOnly(project(":edc-dataplane:edc-dataplane-base")) { + runtimeOnly(project(":edc-dataplane:edc-dataplane-hashicorp-vault")) { exclude("org.eclipse.edc", "api-observability") + exclude(group = "org.eclipse.edc", "vault-hashicorp") } implementation(libs.edc.core.controlplane) diff --git a/gradle.properties b/gradle.properties index 8eab499dd..1e6531c7e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=org.eclipse.tractusx.edc -version=0.6.0 +version=0.7.0-rc1 # configure the build: txScmConnection=scm:git:git@github.com:eclipse-tractusx/tractusx-edc.git txWebsiteUrl=https://github.com/eclipse-tractusx/tractusx-edc.git diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e47d813e2..30969fcf5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,28 +2,28 @@ format.version = "1.1" [versions] -edc = "0.5.1" -apache-sshd = "2.12.0" +edc = "0.6.1-20240405-SNAPSHOT" +apache-sshd = "2.12.1" assertj = "3.25.3" awaitility = "4.2.0" -aws = "2.23.19" -azure-identity = "1.11.2" +aws = "2.25.21" +azure-identity = "1.11.4" bouncyCastle-jdk18on = "1.77" -flyway = "9.22.3" -iron-vc = "0.8.1" -jackson = "2.16.1" +flyway = "10.10.0" +jackson = "2.17.0" jakarta-json = "2.0.1" jupiter = "5.10.2" mockwebserver = "5.0.0-alpha.12" nimbus = "9.37.3" +netty-mockserver = "5.15.0" okhttp = "4.12.0" -postgres = "42.7.1" +postgres = "42.7.3" restAssured = "5.4.0" rsApi = "3.1.0" slf4j = "2.0.12" -testcontainers = "1.19.4" +testcontainers = "1.19.7" tink = "1.12.0" -titanium = "1.3.3" +titanium = "1.4.0" [libraries] edc-spi-catalog = { module = "org.eclipse.edc:catalog-spi", version.ref = "edc" } @@ -39,13 +39,16 @@ edc-spi-controlplane = { module = "org.eclipse.edc:control-plane-spi", version.r edc-controlplane-apiclient = { module = "org.eclipse.edc:control-plane-api-client", version.ref = "edc" } edc-spi-web = { module = "org.eclipse.edc:web-spi", version.ref = "edc" } edc-spi-http = { module = "org.eclipse.edc:http-spi", version.ref = "edc" } +edc-spi-keys = { module = "org.eclipse.edc:keys-spi", version.ref = "edc" } edc-spi-jsonld = { module = "org.eclipse.edc:json-ld-spi", version.ref = "edc" } edc-spi-jwt = { module = "org.eclipse.edc:jwt-spi", version.ref = "edc" } edc-spi-token = { module = "org.eclipse.edc:token-spi", version.ref = "edc" } edc-spi-transform = { module = "org.eclipse.edc:transform-spi", version.ref = "edc" } +edc-spi-identity-did = { module = "org.eclipse.edc:identity-did-spi", version.ref = "edc" } +edc-spi-vc = { module = "org.eclipse.edc:verifiable-credentials-spi", version.ref = "edc" } +edc-spi-edrstore = { module = "org.eclipse.edc:edr-store-spi", version.ref = "edc" } edc-token-core = { module = "org.eclipse.edc:token-core", version.ref = "edc" } edc-spi-oauth2 = { module = "org.eclipse.edc:oauth2-spi", version.ref = "edc" } -edc-util = { module = "org.eclipse.edc:util", version.ref = "edc" } edc-boot = { module = "org.eclipse.edc:boot", version.ref = "edc" } edc-build-plugin = { module = "org.eclipse.edc.edc-build:org.eclipse.edc.edc-build.gradle.plugin", version.ref = "edc" } edc-config-filesystem = { module = "org.eclipse.edc:configuration-filesystem", version.ref = "edc" } @@ -59,9 +62,8 @@ edc-core-jersey = { module = "org.eclipse.edc:jersey-core", version.ref = "edc" edc-core-api = { module = "org.eclipse.edc:api-core", version.ref = "edc" } edc-core-policy-monitor = { module = "org.eclipse.edc:policy-monitor-core", version.ref = "edc" } edc-core-sql = { module = "org.eclipse.edc:sql-core", version.ref = "edc" } -edc-core-validator = { module = "org.eclipse.edc:validator-core", version.ref = "edc" } -edc-core-transform = { module = "org.eclipse.edc:transform-core", version.ref = "edc" } -edc-statemachine = { module = "org.eclipse.edc:state-machine", version.ref = "edc" } +edc-core-token = { module = "org.eclipse.edc:token-core", version.ref = "edc" } +edc-core-edrstore = { module = "org.eclipse.edc:edr-store-core", version.ref = "edc" } edc-junit = { module = "org.eclipse.edc:junit", version.ref = "edc" } edc-api-management-config = { module = "org.eclipse.edc:management-api-configuration", version.ref = "edc" } edc-api-management = { module = "org.eclipse.edc:management-api", version.ref = "edc" } @@ -74,7 +76,6 @@ edc-api-transferprocess = { module = "org.eclipse.edc:transfer-process-api", ver edc-api-controlplane = { module = "org.eclipse.edc:control-plane-api", version.ref = "edc" } edc-dsp = { module = "org.eclipse.edc:dsp", version.ref = "edc" } edc-iam-mock = { module = "org.eclipse.edc:iam-mock", version.ref = "edc" } -edc-policy-engine = { module = "org.eclipse.edc:policy-engine", version.ref = "edc" } edc-auth-tokenbased = { module = "org.eclipse.edc:auth-tokenbased", version.ref = "edc" } edc-auth-oauth2-client = { module = "org.eclipse.edc:oauth2-client", version.ref = "edc" } edc-transaction-local = { module = "org.eclipse.edc:transaction-local", version.ref = "edc" } @@ -82,9 +83,23 @@ edc-ext-http = { module = "org.eclipse.edc:http", version.ref = "edc" } edc-ext-azure-cosmos-core = { module = "org.eclipse.edc:azure-cosmos-core", version.ref = "edc" } edc-ext-azure-test = { module = "org.eclipse.edc:azure-test", version.ref = "edc" } edc-ext-jsonld = { module = "org.eclipse.edc:json-ld", version.ref = "edc" } -edc-ext-jersey-providers = { module = "org.eclipse.edc:jersey-providers", version.ref = "edc" } edc-validator-data-address-http-data = { module = "org.eclipse.edc:validator-data-address-http-data", version.ref = "edc" } +# EDC lib dependencies +edc-lib-boot = { module = "org.eclipse.edc:boot-lib", version.ref = "edc" } +edc-lib-cryptocommon = { module = "org.eclipse.edc:crypto-common-lib", version.ref = "edc" } +edc-lib-http = { module = "org.eclipse.edc:http-lib", version.ref = "edc" } +edc-lib-jersey-providers = { module = "org.eclipse.edc:jersey-providers-lib", version.ref = "edc" } +edc-lib-jsonld = { module = "org.eclipse.edc:json-ld-lib", version.ref = "edc" } +edc-lib-jws2020 = { module = "org.eclipse.edc:jws2020-lib", version.ref = "edc" } +edc-lib-policyengine = { module = "org.eclipse.edc:policy-engine-lib", version.ref = "edc" } +edc-lib-query = { module = "org.eclipse.edc:query-lib", version.ref = "edc" } +edc-lib-store = { module = "org.eclipse.edc:store-lib", version.ref = "edc" } +edc-lib-statemachine = { module = "org.eclipse.edc:state-machine-lib", version.ref = "edc" } +edc-lib-util = { module = "org.eclipse.edc:util-lib", version.ref = "edc" } +edc-lib-validator = { module = "org.eclipse.edc:validator-lib", version.ref = "edc" } +edc-lib-transform = { module = "org.eclipse.edc:transform-lib", version.ref = "edc" } + # implementations edc-sql-assetindex = { module = "org.eclipse.edc:asset-index-sql", version.ref = "edc" } edc-sql-contract-definition = { module = "org.eclipse.edc:contract-definition-store-sql", version.ref = "edc" } @@ -95,6 +110,9 @@ edc-sql-core = { module = "org.eclipse.edc:sql-core", version.ref = "edc" } edc-sql-lease = { module = "org.eclipse.edc:sql-lease", version.ref = "edc" } edc-sql-pool = { module = "org.eclipse.edc:sql-pool-apache-commons", version.ref = "edc" } edc-sql-policy-monitor = { module = "org.eclipse.edc:policy-monitor-store-sql", version.ref = "edc" } +edc-sql-edrindex = { module = "org.eclipse.edc:edr-index-sql", version.ref = "edc" } +edc-sql-accesstokendata = { module = "org.eclipse.edc:accesstokendata-store-sql", version.ref = "edc" } +edc-sql-dataplane = { module = "org.eclipse.edc:data-plane-store-sql", version.ref = "edc" } # azure stuff edc-azure-vault = { module = "org.eclipse.edc:vault-azure", version.ref = "edc" } @@ -111,20 +129,21 @@ edc-controlplane-callback-dispatcher-event = { module = "org.eclipse.edc:callbac edc-controlplane-callback-dispatcher-http = { module = "org.eclipse.edc:callback-http-dispatcher", version.ref = "edc" } # IATP Modules +edc-spi-identitytrust = { module = "org.eclipse.edc:identity-trust-spi", version.ref = "edc" } +edc-core-identitytrust = { module = "org.eclipse.edc:identity-trust-core", version.ref = "edc" } +edc-core-did = { module = "org.eclipse.edc:identity-did-core", version.ref = "edc" } -edc-identity-core-trust = { module = "org.eclipse.edc:identity-trust-core", version.ref = "edc" } -edc-identity-core-did = { module = "org.eclipse.edc:identity-did-core", version.ref = "edc" } edc-identity-did-web = { module = "org.eclipse.edc:identity-did-web", version.ref = "edc" } edc-identity-vc-ldp = { module = "org.eclipse.edc:ldp-verifiable-credentials", version.ref = "edc" } edc-identity-vc-jwt = { module = "org.eclipse.edc:jwt-verifiable-credentials", version.ref = "edc" } -edc-identity-jws2020 = { module = "org.eclipse.edc:jws2020", version.ref = "edc" } edc-identity-trust-transform = { module = "org.eclipse.edc:identity-trust-transform", version.ref = "edc" } -edc-identity-trust-sts-remote = { module = "org.eclipse.edc:identity-trust-sts-remote-core", version.ref = "edc" } edc-identity-trust-issuers-configuration = { module = "org.eclipse.edc:identity-trust-issuers-configuration", version.ref = "edc" } # IATP for Testing +edc-identity-trust-sts-remote-client = { module = "org.eclipse.edc:identity-trust-sts-remote-client", version.ref = "edc" } +edc-identity-trust-sts-embedded = { module = "org.eclipse.edc:identity-trust-sts-embedded", version.ref = "edc" } edc-identity-trust-sts-core = { module = "org.eclipse.edc:identity-trust-sts-core", version.ref = "edc" } edc-identity-trust-sts-api = { module = "org.eclipse.edc:identity-trust-sts-api", version.ref = "edc" } edc-identity-trust-sts-client-configuration = { module = "org.eclipse.edc:identity-trust-sts-client-configuration", version.ref = "edc" } @@ -134,6 +153,7 @@ edc-identity-trust-sts-client-configuration = { module = "org.eclipse.edc:identi edc-ih-api = { module = "org.eclipse.edc:identity-hub-api", version.ref = "edc" } edc-ih-credentials = { module = "org.eclipse.edc:identity-hub-credentials", version.ref = "edc" } edc-ih-spi-store = { module = "org.eclipse.edc:identity-hub-store-spi", version.ref = "edc" } +edc-ih-spi = { module = "org.eclipse.edc:identity-hub-spi", version.ref = "edc" } edc-ih-keypairs = { module = "org.eclipse.edc:identity-hub-keypairs", version.ref = "edc" } edc-ih-participants = { module = "org.eclipse.edc:identity-hub-participants", version.ref = "edc" } edc-ih-did = { module = "org.eclipse.edc:identity-hub-did", version.ref = "edc" } @@ -146,7 +166,7 @@ edc-spi-dataplane-http = { module = "org.eclipse.edc:data-plane-http-spi", versi edc-dpf-transferclient = { module = "org.eclipse.edc:data-plane-transfer-client", version.ref = "edc" } edc-dpf-selector-spi = { module = "org.eclipse.edc:data-plane-selector-spi", version.ref = "edc" } edc-dpf-selector-core = { module = "org.eclipse.edc:data-plane-selector-core", version.ref = "edc" } -edc-dpf-transfer = { module = "org.eclipse.edc:transfer-data-plane", version.ref = "edc" } +edc-dpf-transfer-signaling = { module = "org.eclipse.edc:transfer-data-plane-signaling", version.ref = "edc" } edc-dpf-core = { module = "org.eclipse.edc:data-plane-core", version.ref = "edc" } edc-dpf-util = { module = "org.eclipse.edc:data-plane-util", version.ref = "edc" } @@ -154,7 +174,9 @@ edc-dpf-azblob = { module = "org.eclipse.edc:data-plane-azure-storage", version. edc-dpf-http = { module = "org.eclipse.edc:data-plane-http", version.ref = "edc" } edc-dpf-oauth2 = { module = "org.eclipse.edc:data-plane-http-oauth2", version.ref = "edc" } edc-dpf-api-control = { module = "org.eclipse.edc:data-plane-control-api", version.ref = "edc" } -edc-dpf-api-public = { module = "org.eclipse.edc:data-plane-public-api", version.ref = "edc" } +edc-dpf-api-public-v2 = { module = "org.eclipse.edc:data-plane-public-api-v2", version.ref = "edc" } + +edc-dpf-api-signaling = { module = "org.eclipse.edc:data-plane-signaling-api", version.ref = "edc" } # micrometer and other infra stuff edc-micrometer-core = { module = "org.eclipse.edc:micrometer-core", version.ref = "edc" } @@ -163,18 +185,19 @@ edc-micrometer-jetty = { module = "org.eclipse.edc:jetty-micrometer", version.re edc-monitor-jdklogger = { module = "org.eclipse.edc:monitor-jdk-logger", version.ref = "edc" } edc-transfer-dynamicreceiver = { module = "org.eclipse.edc:transfer-pull-http-dynamic-receiver", version.ref = "edc" } edc-transfer-receiver = { module = "org.eclipse.edc:transfer-pull-http-receiver", version.ref = "edc" } +edc-edr-store-receiver = { module = "org.eclipse.edc:edr-store-receiver", version.ref = "edc" } # other deps apache-sshd-core = { module = "org.apache.sshd:sshd-core", version.ref = "apache-sshd" } apache-sshd-sftp = { module = "org.apache.sshd:sshd-sftp", version.ref = "apache-sshd" } -apicatalog-iron-vc = { module = "com.apicatalog:iron-verifiable-credentials", version.ref = "iron-vc" } assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" } awaitility = { module = "org.awaitility:awaitility", version.ref = "awaitility" } aws-s3 = { module = "software.amazon.awssdk:s3", version.ref = "aws" } aws-s3transfer = { module = "software.amazon.awssdk:s3-transfer-manager", version.ref = "aws" } bouncyCastle-bcpkixJdk18on = { module = "org.bouncycastle:bcpkix-jdk18on", version.ref = "bouncyCastle-jdk18on" } flyway-core = { module = "org.flywaydb:flyway-core", version.ref = "flyway" } +flyway-database-postgres = { module = "org.flywaydb:flyway-database-postgresql", version.ref = "flyway" } jackson-datatypeJsr310 = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310", version.ref = "jackson" } jacksonJsonP = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jakarta-jsonp", version.ref = "jackson" } jakarta-rsApi = { module = "jakarta.ws.rs:jakarta.ws.rs-api", version.ref = "rsApi" } @@ -183,6 +206,7 @@ junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.re nimbus-jwt = { module = "com.nimbusds:nimbus-jose-jwt", version.ref = "nimbus" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "mockwebserver" } +netty-mockserver = { module = "org.mock-server:mockserver-netty", version.ref = "netty-mockserver" } postgres = { module = "org.postgresql:postgresql", version.ref = "postgres" } restAssured = { module = "io.rest-assured:rest-assured", version.ref = "restAssured" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } @@ -194,7 +218,7 @@ titaniumJsonLd = { module = "com.apicatalog:titanium-json-ld", version.ref = "ti [bundles] edc-connector = ["edc.boot", "edc.core-connector", "edc.core-controlplane", "edc.api-observability"] -edc-dpf = ["edc.dpf-transfer", "edc.dpf-selector-core", "edc.spi-dataplane-selector"] -edc-sqlstores = ["edc.sql-assetindex", "edc.sql-contract-definition", "edc.sql-contract-negotiation", "edc.sql-transferprocess", "edc.sql-policydef", "edc.sql-policy-monitor"] +edc-dpf = ["edc.dpf-transfer-signaling", "edc.dpf-selector-core", "edc.spi-dataplane-selector"] +edc-sqlstores = ["edc.sql-assetindex", "edc.sql-contract-definition", "edc.sql-contract-negotiation", "edc.sql-transferprocess", "edc.sql-policydef", "edc.sql-policy-monitor", "edc.sql-edrindex"] edc-monitoring = ["edc.micrometer-core", "edc.micrometer-jersey", "edc.micrometer-jetty"] edc-sts = ["edc-identity-trust-sts-core", "edc-identity-trust-sts-api", "edc-identity-trust-sts-client-configuration"] diff --git a/resources/hashtag.header b/resources/hashtag.header index a6357d5b2..15b2ffccb 100644 --- a/resources/hashtag.header +++ b/resources/hashtag.header @@ -1,5 +1,5 @@ ^#! -^##{80}$ +^##+$ ^# Copyright \(c\) 20\d\d((,| -)20\d{2})? [A-Za-z].+\S$ ^#$ ^# See the NOTICE file\(s\) distributed with this work for additional$ @@ -16,5 +16,5 @@ ^# under the License\.$ ^#$ ^# SPDX-License-Identifier: Apache\-2\.0$ -^##{80}$ +^##+$ ^$ diff --git a/resources/java.header b/resources/java.header index aea312737..8ff72fff0 100644 --- a/resources/java.header +++ b/resources/java.header @@ -1,4 +1,4 @@ -^/\*{80}$ +^/\*+$ ^ \* Copyright \(c\) 20\d\d((,| -)20\d{2})? [A-Za-z].+\S$ ^ \*$ ^ \* See the NOTICE file\(s\) distributed with this work for additional$ @@ -6,7 +6,7 @@ ^ \*$ ^ \* 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\.$ +^ \* 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$ @@ -15,6 +15,6 @@ ^ \* under the License\.$ ^ \*$ ^ \* SPDX-License-Identifier: Apache\-2\.0$ -^ \*{80}/$ +^ \*+/$ ^$ -^package +package .* \ No newline at end of file diff --git a/samples/multi-tenancy/build.gradle.kts b/samples/multi-tenancy/build.gradle.kts index 31239069d..ad6cd1d8e 100644 --- a/samples/multi-tenancy/build.gradle.kts +++ b/samples/multi-tenancy/build.gradle.kts @@ -28,12 +28,19 @@ dependencies { implementation(libs.edc.boot) implementation(libs.edc.iam.mock) implementation(project(":edc-controlplane:edc-controlplane-base")) { - exclude("org.eclipse.tractusx.edc", "data-encryption") exclude(module = "ssi-miw-credential-client") exclude(module = "ssi-identity-core") exclude(module = "auth-tokenbased") + // the token refresh extension is not needed + exclude(module = "tx-iatp-sts-dim") + exclude(module = "tokenrefresh-handler") + exclude(module = "edr-core") + exclude(module = "edr-api-v2") + exclude(module = "edr-callback") + exclude("org.eclipse.edc", "identity-trust-issuers-configuration") } implementation(libs.edc.core.controlplane) + implementation(libs.jakarta.rsApi) } application { diff --git a/samples/multi-tenancy/src/main/java/org/eclipse/tractusx/edc/samples/multitenancy/MultiTenantRuntime.java b/samples/multi-tenancy/src/main/java/org/eclipse/tractusx/edc/samples/multitenancy/MultiTenantRuntime.java index d7ac26513..b4c8c0025 100644 --- a/samples/multi-tenancy/src/main/java/org/eclipse/tractusx/edc/samples/multitenancy/MultiTenantRuntime.java +++ b/samples/multi-tenancy/src/main/java/org/eclipse/tractusx/edc/samples/multitenancy/MultiTenantRuntime.java @@ -47,7 +47,8 @@ public static void main(String[] args) { runtime.boot(); } - protected void boot() { + @Override + public void boot() { loadTenantsConfig().getConfig("edc.tenants").partition().forEach(this::bootTenant); } diff --git a/settings.gradle.kts b/settings.gradle.kts index 3b2cbcff6..c33a612e9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,49 +23,61 @@ rootProject.name = "tractusx-edc" include(":spi:callback-spi") include(":spi:edr-spi") include(":spi:core-spi") -include(":spi:ssi-spi") +include(":spi:tokenrefresh-spi") +include(":spi:bdrs-client-spi") // core modules -include(":core:edr-cache-core") include(":core:edr-core") include(":core:json-ld-core") +include(":core:core-utils") - +// extensions - control plane include(":edc-extensions:bpn-validation") include(":edc-extensions:bpn-validation:bpn-validation-api") include(":edc-extensions:bpn-validation:bpn-validation-spi") include(":edc-extensions:bpn-validation:bpn-validation-core") include(":edc-extensions:bpn-validation:business-partner-store-sql") -include(":edc-extensions:data-encryption") -include(":edc-extensions:dataplane-selector-configuration") -include(":edc-extensions:postgresql-migration") +include(":edc-extensions:migrations:postgresql-migration-lib") +include(":edc-extensions:migrations:control-plane-migration") +include(":edc-extensions:migrations:data-plane-migration") +include(":edc-extensions:tokenrefresh-handler") +include(":edc-extensions:bdrs-client") include(":edc-extensions:provision-additional-headers") include(":edc-extensions:transferprocess-sftp-client") include(":edc-extensions:transferprocess-sftp-common") include(":edc-extensions:transferprocess-sftp-provisioner") -include(":edc-extensions:edr:edr-api") +include(":edc-extensions:edr:edr-api-v2") include(":edc-extensions:edr:edr-callback") -include(":edc-extensions:edr:edr-cache-sql") include(":edc-extensions:cx-policy") -include("edc-extensions:ssi:ssi-identity-core") -include("edc-extensions:ssi:ssi-miw-credential-client") -include(":edc-extensions:ssi:ssi-identity-extractor") include(":edc-extensions:iatp:tx-iatp") +include(":edc-extensions:iatp:tx-iatp-sts-dim") +include(":edc-extensions:data-flow-properties-provider") + +// extensions - data plane +include(":edc-extensions:dataplane:dataplane-proxy:edc-dataplane-proxy-consumer-api") +include(":edc-extensions:dataplane:dataplane-selector-configuration") +include(":edc-extensions:dataplane:dataplane-token-refresh:token-refresh-core") +include(":edc-extensions:dataplane:dataplane-token-refresh:token-refresh-api") // test modules include(":edc-tests:e2e-tests") -include(":edc-tests:miw-tests") +include(":edc-tests:edc-controlplane:edr-api-tests") +include(":edc-tests:edc-controlplane:catalog-tests") +include(":edc-tests:edc-controlplane:transfer-tests") +include(":edc-tests:edc-controlplane:iatp-tests") +include(":edc-tests:edc-controlplane:policy-tests") +include(":edc-tests:edc-controlplane:fixtures") include(":edc-tests:runtime:extensions") include(":edc-tests:runtime:runtime-memory") include(":edc-tests:runtime:dataplane-cloud") -include(":edc-tests:runtime:runtime-memory-ssi") include(":edc-tests:runtime:runtime-postgresql") -include(":edc-tests:runtime:runtime-postgresql-hashicorp") include(":edc-tests:runtime:iatp:runtime-memory-iatp-ih") +include(":edc-tests:runtime:iatp:runtime-memory-iatp-dim-ih") +include(":edc-tests:runtime:iatp:runtime-memory-iatp-dim") include(":edc-tests:runtime:iatp:runtime-memory-sts") include(":edc-tests:runtime:iatp:iatp-extensions") -include(":edc-tests:edc-dataplane:edc-dataplane-proxy-e2e") +include(":edc-tests:edc-dataplane:edc-dataplane-tokenrefresh-tests") include(":edc-tests:edc-dataplane:cloud-transfer-tests") // modules for controlplane artifacts @@ -80,10 +92,8 @@ include(":edc-dataplane") include(":edc-dataplane:edc-dataplane-azure-vault") include(":edc-dataplane:edc-dataplane-base") include(":edc-dataplane:edc-dataplane-hashicorp-vault") -include(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-consumer-api") -include(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-spi") -include(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-core") -include(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-api") + + include(":samples:multi-tenancy") diff --git a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/SsiConstants.java b/spi/bdrs-client-spi/build.gradle.kts similarity index 87% rename from spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/SsiConstants.java rename to spi/bdrs-client-spi/build.gradle.kts index 87d1896b4..63365c44c 100644 --- a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/SsiConstants.java +++ b/spi/bdrs-client-spi/build.gradle.kts @@ -17,9 +17,11 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.spi; - -public interface SsiConstants { - String SSI_TOKEN_CONTEXT = "ssi-token-context"; +plugins { + `java-library` + `maven-publish` +} +dependencies { + implementation(libs.edc.spi.core) } diff --git a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/SsiTokenValidationService.java b/spi/bdrs-client-spi/src/main/java/org/eclipse/tractusx/edc/spi/identity/mapper/BdrsClient.java similarity index 69% rename from spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/SsiTokenValidationService.java rename to spi/bdrs-client-spi/src/main/java/org/eclipse/tractusx/edc/spi/identity/mapper/BdrsClient.java index 05f3a6a7b..70be4dfc9 100644 --- a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/SsiTokenValidationService.java +++ b/spi/bdrs-client-spi/src/main/java/org/eclipse/tractusx/edc/spi/identity/mapper/BdrsClient.java @@ -17,22 +17,21 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.iam.ssi.spi; +package org.eclipse.tractusx.edc.spi.identity.mapper; import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.token.spi.TokenValidationRule; - -import java.util.List; /** - * Validates SSI tokens + * Interface for resolving BPNs to DIDs */ - @ExtensionPoint -public interface SsiTokenValidationService { +public interface BdrsClient { - Result validate(TokenRepresentation tokenRepresentation, List rules); + /** + * Resolve the input BPN to a DID + * + * @param bpn The participantID (BPN) + * @return The resolved DID if found, null otherwise + */ + String resolve(String bpn); } diff --git a/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallback.java b/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallback.java index 4528daca0..2b5ffbb44 100644 --- a/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallback.java +++ b/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallback.java @@ -19,7 +19,7 @@ package org.eclipse.tractusx.edc.spi.callback; -import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; +import org.eclipse.edc.connector.controlplane.services.spi.callback.CallbackEventRemoteMessage; import org.eclipse.edc.spi.event.Event; import org.eclipse.edc.spi.result.Result; diff --git a/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallbackRegistry.java b/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallbackRegistry.java index 6cffe8d46..a5da4b3fa 100644 --- a/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallbackRegistry.java +++ b/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallbackRegistry.java @@ -20,7 +20,7 @@ package org.eclipse.tractusx.edc.spi.callback; -import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; +import org.eclipse.edc.connector.controlplane.services.spi.callback.CallbackEventRemoteMessage; import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; import org.eclipse.edc.spi.event.Event; import org.eclipse.edc.spi.result.Result; diff --git a/spi/core-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/CoreConstants.java b/spi/core-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/CoreConstants.java index d3448b178..f21301a1b 100644 --- a/spi/core-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/CoreConstants.java +++ b/spi/core-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/CoreConstants.java @@ -19,14 +19,29 @@ package org.eclipse.tractusx.edc.edr.spi; + +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; + public final class CoreConstants { public static final String TX_PREFIX = "tx"; + public static final String TX_AUTH_PREFIX = "tx-auth"; public static final String TX_NAMESPACE = "https://w3id.org/tractusx/v0.0.1/ns/"; - public static final String TX_CREDENTIAL_NAMESPACE = "https://w3id.org/tractusx/credentials/v0.0.1/ns/"; public static final String TX_CONTEXT = "https://w3id.org/tractusx/edc/v0.0.1"; + public static final String TX_AUTH_NS = "https://w3id.org/tractusx/auth/"; public static final String EDC_CONTEXT = "https://w3id.org/edc/v0.0.1"; - + public static final String CX_CREDENTIAL_NS = "https://w3id.org/catenax/credentials/"; + public static final String CX_POLICY_NS = "https://w3id.org/catenax/policy/"; + public static final String TX_CREDENTIAL_NAMESPACE = "https://w3id.org/tractusx/credentials/v0.0.1/ns/"; + + // constants related to token refresh/renewal + public static final String EDR_PROPERTY_AUTHORIZATION = EDC_NAMESPACE + "authorization"; + public static final String EDR_PROPERTY_REFRESH_TOKEN = TX_AUTH_NS + "refreshToken"; + public static final String EDR_PROPERTY_REFRESH_ENDPOINT = TX_AUTH_NS + "refreshEndpoint"; + public static final String EDR_PROPERTY_REFRESH_AUDIENCE = TX_AUTH_NS + "refreshAudience"; + public static final String AUDIENCE_PROPERTY = TX_AUTH_NS + "audience"; + public static final String EDR_PROPERTY_EXPIRES_IN = TX_AUTH_NS + "expiresIn"; + private CoreConstants() { } } diff --git a/spi/edr-spi/build.gradle.kts b/spi/edr-spi/build.gradle.kts index 2ac7abdf0..0096d50ca 100644 --- a/spi/edr-spi/build.gradle.kts +++ b/spi/edr-spi/build.gradle.kts @@ -26,6 +26,7 @@ dependencies { implementation(project(":spi:core-spi")) implementation(libs.edc.spi.core) implementation(libs.edc.spi.contract) + implementation(libs.edc.spi.edrstore) testFixturesImplementation(libs.edc.junit) testFixturesImplementation(libs.junit.jupiter.api) diff --git a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EdrManager.java b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EdrManager.java deleted file mode 100644 index 51960cefb..000000000 --- a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EdrManager.java +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.spi; - -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; -import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; -import org.eclipse.edc.spi.response.StatusResult; -import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; - -/** - * Manages EDRs lifecycle - */ -@ExtensionPoint -public interface EdrManager { - - /** - * Initiated a new EDR negotiation. An EDR negotiation consists on two sub-processes. Contract negotiation and transfer - * request. Once the latter is completed the returned EDR from the provided will be store in the EDR cache for consumption - * - * @param request Request Data - * @return The contract negotiation - */ - StatusResult initiateEdrNegotiation(NegotiateEdrRequest request); - -} diff --git a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/service/EdrService.java b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/service/EdrService.java index 25417637c..332329415 100644 --- a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/service/EdrService.java +++ b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/service/EdrService.java @@ -19,41 +19,38 @@ package org.eclipse.tractusx.edc.edr.spi.service; -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.result.ServiceResult; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.tractusx.edc.edr.spi.types.RefreshMode; import java.util.List; /** - * Service for opening a transfer process. + * Mediate the access to the {@link EndpointDataReferenceEntry} and cached {@link DataAddress} EDRs. */ @ExtensionPoint public interface EdrService { + /** - * Open a transfer process by firing a contract negotiation. Implementors should fire a contract negotiation - * and automatically fire a transfer process once the agreement has been reached. + * Resolve a {@link DataAddress} EDR associated with the transfer process. The token will be refreshed + * accordingly the {@link RefreshMode} * - * @param request The open request - * @return The result containing the contract negotiation id + * @param transferProcessId The id of the transfer process + * @param mode The {@link RefreshMode} + * @return If the token in {@link DataAddress} is expired a refresh one */ - ServiceResult initiateEdrNegotiation(NegotiateEdrRequest request); + ServiceResult resolveByTransferProcess(String transferProcessId, RefreshMode mode); /** - * Return a {@link EndpointDataReference} associated with the transferProcessId in input + * Search for {@link EndpointDataReferenceEntry} * - * @param transferProcessId The transferProcessId - * @return The result containing the {@link EndpointDataReference} + * @param query The {@link QuerySpec} + * @return The list of matching {@link EndpointDataReferenceEntry} if success, failure otherwise */ - ServiceResult findByTransferProcessId(String transferProcessId); - - ServiceResult> findBy(QuerySpec querySpec); - - ServiceResult deleteByTransferProcessId(String transferProcessId); + ServiceResult> query(QuerySpec query); } diff --git a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/store/EndpointDataReferenceCache.java b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/store/EndpointDataReferenceCache.java deleted file mode 100644 index f1f4496f5..000000000 --- a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/store/EndpointDataReferenceCache.java +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.spi.store; - -import org.eclipse.edc.spi.persistence.StateEntityStore; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.result.StoreResult; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.stream.Stream; - -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.NEGOTIATED; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.REFRESHING; - -/** - * Caches and resolves {@link EndpointDataReference}s - */ -public interface EndpointDataReferenceCache extends StateEntityStore { - - /** - * Resolves an {@link EndpointDataReference} for the transfer process, returning null if one does not exist. - */ - @Nullable - EndpointDataReference resolveReference(String transferProcessId); - - /** - * Resolves an {@link EndpointDataReference} for the transfer process, returning null if one does not exist. - */ - @Nullable - StoreResult findByIdAndLease(String transferProcessId); - - /** - * Resolves the {@link EndpointDataReference}s for the asset. - */ - @NotNull - List referencesForAsset(String assetId, String providerId); - - - /** - * Filter the {@link EndpointDataReferenceEntry} that are in negotiated or refreshing state - * - * @param entry The {@link EndpointDataReferenceEntry} - */ - default boolean filterActive(EndpointDataReferenceEntry entry) { - return entry.getState() == NEGOTIATED.code() || entry.getState() == REFRESHING.code(); - } - - /** - * Returns all the EDR entries in the store that are covered by a given {@link QuerySpec}. - */ - - Stream queryForEntries(QuerySpec spec); - - /** - * Saves an {@link EndpointDataReference} to the cache using upsert semantics. - */ - void save(EndpointDataReferenceEntry entry, EndpointDataReference edr); - - - /** - * Saves an {@link EndpointDataReference} to the cache using upsert semantics. - */ - void update(EndpointDataReferenceEntry entry); - - /** - * Deletes stored endpoint reference data associated with the given transfer process. - */ - StoreResult deleteByTransferProcessId(String id); - - -} diff --git a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/EndpointDataReferenceEntry.java b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/EndpointDataReferenceEntry.java deleted file mode 100644 index 3c3736867..000000000 --- a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/EndpointDataReferenceEntry.java +++ /dev/null @@ -1,235 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.spi.types; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import org.eclipse.edc.spi.entity.StatefulEntity; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; - -import java.util.Arrays; -import java.util.Objects; -import java.util.function.Predicate; - -import static java.lang.String.format; -import static java.util.Objects.requireNonNull; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.DELETING; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.ERROR; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.EXPIRED; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.NEGOTIATED; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.REFRESHING; - -/** - * An entry in the cache for an {@link EndpointDataReference}. - */ -@JsonDeserialize(builder = EndpointDataReferenceEntry.Builder.class) -public class EndpointDataReferenceEntry extends StatefulEntity { - - public static final String SIMPLE_TYPE = "EndpointDataReferenceEntry"; - public static final String EDR_ENTRY_TYPE = TX_NAMESPACE + SIMPLE_TYPE; - public static final String EDR_ENTRY_STATE = TX_NAMESPACE + "edrState"; - public static final String EDR_ENTRY_EXPIRATION_DATE = TX_NAMESPACE + "expirationDate"; - public static final String ASSET_ID = "assetId"; - public static final String EDR_ENTRY_ASSET_ID = EDC_NAMESPACE + ASSET_ID; - public static final String AGREEMENT_ID = "agreementId"; - public static final String EDR_ENTRY_AGREEMENT_ID = EDC_NAMESPACE + AGREEMENT_ID; - public static final String CONTRACT_NEGOTIATION_ID = "contractNegotiationId"; - public static final String EDR_ENTRY_CONTRACT_NEGOTIATION_ID = EDC_NAMESPACE + CONTRACT_NEGOTIATION_ID; - - public static final String TRANSFER_PROCESS_ID = "transferProcessId"; - public static final String EDR_ENTRY_TRANSFER_PROCESS_ID = EDC_NAMESPACE + TRANSFER_PROCESS_ID; - public static final String PROVIDER_ID = "providerId"; - public static final String EDR_ENTRY_PROVIDER_ID = EDC_NAMESPACE + PROVIDER_ID; - private String assetId; - private String agreementId; - private String transferProcessId; - - private String contractNegotiationId; - - private String providerId; - - private Long expirationTimestamp; - - private EndpointDataReferenceEntry() { - state = NEGOTIATED.code(); - } - - @Override - public String getId() { - return getTransferProcessId(); - } - - - @Override - public EndpointDataReferenceEntry copy() { - var builder = Builder.newInstance() - .transferProcessId(transferProcessId) - .agreementId(agreementId) - .assetId(assetId) - .providerId(providerId) - .contractNegotiationId(contractNegotiationId) - .expirationTimestamp(expirationTimestamp); - return copy(builder); - } - - @Override - public String stateAsString() { - return EndpointDataReferenceEntryStates.from(state).toString(); - } - - @JsonIgnore - public String getEdrState() { - return EndpointDataReferenceEntryStates.from(getState()).name(); - } - - public String getAssetId() { - return assetId; - } - - public String getAgreementId() { - return agreementId; - } - - public String getTransferProcessId() { - return transferProcessId; - } - - public String getProviderId() { - return providerId; - } - - public String getContractNegotiationId() { - return contractNegotiationId; - } - - public Long getExpirationTimestamp() { - return expirationTimestamp; - } - - @Override - public int hashCode() { - return Objects.hash(assetId, agreementId, transferProcessId); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - var that = (EndpointDataReferenceEntry) o; - - return transferProcessId.equals(that.transferProcessId); - } - - public void transitionToRefreshing() { - transition(REFRESHING, REFRESHING, NEGOTIATED); - } - - public void transitionToNegotiated() { - transition(NEGOTIATED, NEGOTIATED, REFRESHING); - } - - public void transitionError() { - transition(ERROR, REFRESHING, NEGOTIATED); - } - - public void transitionToExpired() { - transition(EXPIRED, EXPIRED, NEGOTIATED, REFRESHING); - } - - public void transitionToDeleting() { - transition(DELETING, DELETING, EXPIRED); - } - - private void transition(EndpointDataReferenceEntryStates end, Predicate canTransitTo) { - if (!canTransitTo.test(EndpointDataReferenceEntryStates.from(state))) { - throw new IllegalStateException(format("Cannot transition from state %s to %s", EndpointDataReferenceEntryStates.from(state), EndpointDataReferenceEntryStates.from(end.code()))); - } - transitionTo(end.code()); - } - - private void transition(EndpointDataReferenceEntryStates end, EndpointDataReferenceEntryStates... starts) { - transition(end, (state) -> Arrays.stream(starts).anyMatch(s -> s == state)); - } - - - @JsonPOJOBuilder(withPrefix = "") - public static class Builder extends StatefulEntity.Builder { - - private Builder() { - super(new EndpointDataReferenceEntry()); - } - - @JsonCreator - public static Builder newInstance() { - return new Builder(); - } - - public Builder assetId(String assetId) { - entity.assetId = assetId; - return this; - } - - public Builder agreementId(String agreementId) { - entity.agreementId = agreementId; - return this; - } - - public Builder transferProcessId(String transferProcessId) { - entity.transferProcessId = transferProcessId; - entity.id = transferProcessId; - return this; - } - - public Builder providerId(String providerId) { - entity.providerId = providerId; - return this; - } - - public Builder contractNegotiationId(String contractNegotiationId) { - entity.contractNegotiationId = contractNegotiationId; - return this; - } - - public Builder expirationTimestamp(Long expirationTimestamp) { - entity.expirationTimestamp = expirationTimestamp; - return this; - } - - @Override - public Builder self() { - return this; - } - - public EndpointDataReferenceEntry build() { - super.build(); - requireNonNull(entity.assetId, ASSET_ID); - requireNonNull(entity.agreementId, AGREEMENT_ID); - requireNonNull(entity.transferProcessId, TRANSFER_PROCESS_ID); - - return entity; - } - } - -} diff --git a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/EndpointDataReferenceEntryStates.java b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/EndpointDataReferenceEntryStates.java deleted file mode 100644 index ebb13cad5..000000000 --- a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/EndpointDataReferenceEntryStates.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021,2022 Microsoft Corporation - * - * 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.edc.edr.spi.types; - -import java.util.Arrays; - -/** - * Defines the states an EDR entry can be in. - */ -public enum EndpointDataReferenceEntryStates { - - NEGOTIATED(50), - - REFRESHING(100), - - EXPIRED(200), - - ERROR(300), - - DELETING(400); - private final int code; - - EndpointDataReferenceEntryStates(int code) { - this.code = code; - } - - public static EndpointDataReferenceEntryStates from(int code) { - return Arrays.stream(values()).filter(tps -> tps.code == code).findFirst().orElse(null); - } - - public int code() { - return code; - } - -} diff --git a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/NegotiateEdrRequest.java b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/NegotiateEdrRequest.java deleted file mode 100644 index 71cc862f4..000000000 --- a/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/NegotiateEdrRequest.java +++ /dev/null @@ -1,107 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.spi.types; - -import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; -import org.eclipse.edc.spi.types.domain.offer.ContractOffer; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -public class NegotiateEdrRequest { - - private String connectorAddress; - private String protocol = "ids-multipart"; - private String connectorId; - private ContractOffer offer; - - private List callbackAddresses = new ArrayList<>(); - - private NegotiateEdrRequest() { - - } - - public String getConnectorAddress() { - return connectorAddress; - } - - public String getProtocol() { - return protocol; - } - - public String getConnectorId() { - return connectorId; - } - - - public List getCallbackAddresses() { - return callbackAddresses; - } - - public ContractOffer getOffer() { - return offer; - } - - - public static final class Builder { - private final NegotiateEdrRequest entity; - - private Builder() { - entity = new NegotiateEdrRequest(); - } - - public Builder connectorAddress(String connectorAddress) { - entity.connectorAddress = connectorAddress; - return this; - } - - public Builder protocol(String protocol) { - entity.protocol = protocol; - return this; - } - - public Builder connectorId(String connectorId) { - entity.connectorId = connectorId; - return this; - } - - public Builder offer(ContractOffer offer) { - entity.offer = offer; - return this; - } - - public Builder callbackAddresses(List callbackAddresses) { - entity.callbackAddresses = callbackAddresses; - return this; - } - - public NegotiateEdrRequest build() { - Objects.requireNonNull(entity.protocol, "protocol should not be null"); - Objects.requireNonNull(entity.connectorAddress, "connector address should not be null"); - Objects.requireNonNull(entity.offer, "offer should not be null"); - return entity; - } - - public static Builder newInstance() { - return new Builder(); - } - } -} diff --git a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/postgres/PostgresEdrStatements.java b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/RefreshMode.java similarity index 71% rename from edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/postgres/PostgresEdrStatements.java rename to spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/RefreshMode.java index fea1c59b1..371457ae0 100644 --- a/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/postgres/PostgresEdrStatements.java +++ b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/RefreshMode.java @@ -17,15 +17,16 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.edr.store.sql.schema.postgres; +package org.eclipse.tractusx.edc.edr.spi.types; -import org.eclipse.edc.sql.translation.PostgresqlOperatorTranslator; -import org.eclipse.tractusx.edc.edr.store.sql.schema.BaseSqlEdrStatements; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.tractusx.edc.edr.spi.service.EdrService; -public class PostgresEdrStatements extends BaseSqlEdrStatements { - - - public PostgresEdrStatements() { - super(new PostgresqlOperatorTranslator()); - } +/** + * Different refresh modes to be used when resolving an {@link DataAddress} with {@link EdrService#resolveByTransferProcess} + */ +public enum RefreshMode { + NO_REFRESH, + AUTO_REFRESH, + FORCE_REFRESH } diff --git a/spi/edr-spi/src/test/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntryTest.java b/spi/edr-spi/src/test/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntryTest.java deleted file mode 100644 index a58c074b7..000000000 --- a/spi/edr-spi/src/test/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntryTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.spi; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.junit.jupiter.api.Test; - -import static java.util.UUID.randomUUID; -import static org.assertj.core.api.Assertions.assertThat; - -class EndpointDataReferenceEntryTest { - - @Test - void verify_serializeDeserialize() throws JsonProcessingException { - var mapper = new ObjectMapper(); - - var entry = EndpointDataReferenceEntry.Builder.newInstance() - .assetId(randomUUID().toString()) - .agreementId(randomUUID().toString()) - .transferProcessId(randomUUID().toString()) - .build(); - - var serialized = mapper.writeValueAsString(entry); - var deserialized = mapper.readValue(serialized, EndpointDataReferenceEntry.class); - - assertThat(deserialized.getTransferProcessId()).isNotEmpty(); - assertThat(deserialized.getAssetId()).isNotEmpty(); - assertThat(deserialized.getAgreementId()).isNotEmpty(); - } -} diff --git a/spi/edr-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCacheTestBase.java b/spi/edr-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCacheTestBase.java deleted file mode 100644 index 44decab32..000000000 --- a/spi/edr-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCacheTestBase.java +++ /dev/null @@ -1,414 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.spi; - -import org.eclipse.edc.spi.query.Criterion; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.result.StoreFailure; -import org.eclipse.edc.spi.result.StoreResult; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static java.util.UUID.randomUUID; -import static java.util.stream.IntStream.range; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.awaitility.Awaitility.await; -import static org.eclipse.edc.spi.persistence.StateEntityStore.hasState; -import static org.eclipse.tractusx.edc.edr.spi.TestFunctions.edr; -import static org.eclipse.tractusx.edc.edr.spi.TestFunctions.edrEntry; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.NEGOTIATED; -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.REFRESHING; -import static org.hamcrest.Matchers.hasSize; - -public abstract class EndpointDataReferenceCacheTestBase { - - public static final String CONNECTOR_NAME = "test-connector"; - - @Test - void save() { - - var tpId = "tp1"; - var assetId = "asset1"; - var edrId = "edr1"; - - var edr = edr(edrId); - var entry = edrEntry(assetId, randomUUID().toString(), tpId); - - getStore().save(entry, edr); - - assertThat(getStore().resolveReference(tpId)) - .isNotNull() - .extracting(EndpointDataReference::getId) - .isEqualTo(edrId); - - var edrs = getStore().referencesForAsset(assetId, null); - assertThat(edrs.size()).isEqualTo(1); - assertThat(edrs.get((0)).getId()).isEqualTo(edrId); - - - } - - @Test - void findByTransferProcessId() { - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - - assertThat(getStore().findByIdAndLease(entry.getTransferProcessId())).isNotNull(); - } - - @Test - void queryEntries_noQuerySpec() { - var all = IntStream.range(0, 10) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i)) - .peek(entry -> getStore().save(entry, edr(entry.getTransferProcessId()))) - .collect(Collectors.toList()); - - assertThat(getStore().queryForEntries(QuerySpec.none())).containsExactlyInAnyOrderElementsOf(all); - } - - @Test - void queryEntries_assetIdQuerySpec() { - IntStream.range(0, 10) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i)) - .forEach(entry -> getStore().save(entry, edr(entry.getTransferProcessId()))); - - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - - var filter = Criterion.Builder.newInstance() - .operandLeft("assetId") - .operator("=") - .operandRight(entry.getAssetId()) - .build(); - - assertThat(getStore().queryForEntries(QuerySpec.Builder.newInstance().filter(filter).build())).containsOnly(entry); - } - - @Test - void queryEntries_agreementIdQuerySpec() { - IntStream.range(0, 10) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i)) - .forEach(entry -> getStore().save(entry, edr(entry.getTransferProcessId()))); - - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - - var filter = Criterion.Builder.newInstance() - .operandLeft("agreementId") - .operator("=") - .operandRight(entry.getAgreementId()) - .build(); - - assertThat(getStore().queryForEntries(QuerySpec.Builder.newInstance().filter(filter).build())).containsOnly(entry); - } - - @Test - void queryEntries_contractNegotiationIdSpec() { - IntStream.range(0, 10) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i, "contractNegotiationId" + i)) - .forEach(entry -> getStore().save(entry, edr(entry.getTransferProcessId()))); - - var entry = edrEntry("assetId", "agreementId", "tpId", "contractNegotiationId"); - getStore().save(entry, edr("edrId")); - - var filter = Criterion.Builder.newInstance() - .operandLeft("contractNegotiationId") - .operator("=") - .operandRight(entry.getContractNegotiationId()) - .build(); - - assertThat(getStore().queryForEntries(QuerySpec.Builder.newInstance().filter(filter).build())).containsOnly(entry); - } - - @Test - void queryEntries_providerIdQuerySpec() { - IntStream.range(0, 10) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i)) - .forEach(entry -> getStore().save(entry, edr(entry.getTransferProcessId()))); - - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - - var filter = Criterion.Builder.newInstance() - .operandLeft("providerId") - .operator("=") - .operandRight(entry.getProviderId()) - .build(); - - assertThat(getStore().queryForEntries(QuerySpec.Builder.newInstance().filter(filter).build())).containsOnly(entry); - } - - @Test - void deleteByTransferProcessId_shouldDelete_WhenFound() { - - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - - assertThat(getStore().deleteByTransferProcessId(entry.getTransferProcessId())) - .extracting(StoreResult::getContent) - .isEqualTo(entry); - - assertThat(getStore().resolveReference(entry.getTransferProcessId())).isNull(); - assertThat(getStore().referencesForAsset(entry.getAssetId(), entry.getProviderId())).hasSize(0); - assertThat(getStore().queryForEntries(QuerySpec.max())).hasSize(0); - - } - - @Test - void deleteByTransferProcessId_shouldReturnError_whenNotFound() { - assertThat(getStore().deleteByTransferProcessId("notFound")) - .extracting(StoreResult::reason) - .isEqualTo(StoreFailure.Reason.NOT_FOUND); - } - - @Test - void nextNotLeased() { - var all = IntStream.range(0, 10) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i)) - .peek((entry -> getStore().save(entry, edr(entry.getTransferProcessId())))) - .toList(); - - assertThat(getStore().nextNotLeased(5, hasState(NEGOTIATED.code()))) - .hasSize(5) - .extracting(EndpointDataReferenceEntry::getTransferProcessId) - .isSubsetOf(all.stream().map(EndpointDataReferenceEntry::getTransferProcessId).collect(Collectors.toList())) - .allMatch(id -> isLockedBy(id, CONNECTOR_NAME)); - } - - @Test - void nextNotLeased_shouldOnlyReturnFreeItems() { - var all = IntStream.range(0, 10) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i)) - .peek((entry -> getStore().save(entry, edr(entry.getTransferProcessId())))) - .collect(Collectors.toList()); - - // lease a few - var leasedTp = all.stream().skip(5).peek(tp -> lockEntity(tp.getId(), CONNECTOR_NAME)).toList(); - - // should not contain leased TPs - assertThat(getStore().nextNotLeased(10, hasState(NEGOTIATED.code()))) - .hasSize(5) - .isSubsetOf(all) - .doesNotContainAnyElementsOf(leasedTp); - } - - @Test - void nextNotLeased_noFreeItem_shouldReturnEmpty() { - var state = NEGOTIATED; - range(0, 3) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i)) - .forEach((entry -> getStore().save(entry, edr(entry.getTransferProcessId())))); - - // first time works - assertThat(getStore().nextNotLeased(10, hasState(state.code()))).hasSize(3); - // second time returns empty list - assertThat(getStore().nextNotLeased(10, hasState(state.code()))).isEmpty(); - } - - @Test - void nextNotLeased_noneInDesiredState() { - range(0, 3) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i)) - .forEach((entry -> getStore().save(entry, edr(entry.getTransferProcessId())))); - - - var nextNotLeased = getStore().nextNotLeased(10, hasState(REFRESHING.code())); - - assertThat(nextNotLeased).isEmpty(); - } - - @Test - void nextNotLeased_batchSizeLimits() { - range(0, 10) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i)) - .forEach((entry -> getStore().save(entry, edr(entry.getTransferProcessId())))); - - - // first time works - var result = getStore().nextNotLeased(3, hasState(NEGOTIATED.code())); - assertThat(result).hasSize(3); - } - - @Test - void nextNotLeased_verifyTemporalOrdering() { - range(0, 10) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, String.valueOf(i))) - .peek(this::delayByTenMillis) - .forEach((entry -> getStore().save(entry, edr(entry.getTransferProcessId())))); - - assertThat(getStore().nextNotLeased(20, hasState(NEGOTIATED.code()))) - .extracting(EndpointDataReferenceEntry::getId) - .map(Integer::parseInt) - .isSortedAccordingTo(Integer::compareTo); - } - - @Test - void nextNotLeased_verifyMostRecentlyUpdatedIsLast() throws InterruptedException { - var all = range(0, 10) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i)) - .peek((entry -> getStore().save(entry, edr(entry.getTransferProcessId())))) - .toList(); - - Thread.sleep(100); - - var fourth = all.get(3); - fourth.updateStateTimestamp(); - getStore().update(fourth); - - var next = getStore().nextNotLeased(20, hasState(NEGOTIATED.code())); - assertThat(next.indexOf(fourth)).isEqualTo(9); - } - - @Test - @DisplayName("Verifies that calling nextNotLeased locks the TP for any subsequent calls") - void nextNotLeased_locksEntity() { - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - - getStore().nextNotLeased(100, hasState(NEGOTIATED.code())); - - assertThat(isLockedBy(entry.getId(), CONNECTOR_NAME)).isTrue(); - } - - @Test - void nextNotLeased_expiredLease() { - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - - lockEntity(entry.getId(), CONNECTOR_NAME, Duration.ofMillis(100)); - - await().atLeast(Duration.ofMillis(100)) - .atMost(Duration.ofMillis(500)) - .until(() -> getStore().nextNotLeased(10, hasState(NEGOTIATED.code())), hasSize(1)); - } - - @Test - void nextNotLeased_shouldLeaseEntityUntilUpdate() { - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - - var firstQueryResult = getStore().nextNotLeased(1, hasState(NEGOTIATED.code())); - assertThat(firstQueryResult).hasSize(1); - - var secondQueryResult = getStore().nextNotLeased(1, hasState(NEGOTIATED.code())); - assertThat(secondQueryResult).hasSize(0); - - var retrieved = firstQueryResult.get(0); - getStore().update(retrieved); - - var thirdQueryResult = getStore().nextNotLeased(1, hasState(NEGOTIATED.code())); - assertThat(thirdQueryResult).hasSize(1); - } - - @Test - void nextNotLeased_avoidsStarvation() throws InterruptedException { - - range(0, 10) - .mapToObj(i -> edrEntry("assetId" + i, "agreementId" + i, "tpId" + i)) - .forEach((entry -> getStore().save(entry, edr(entry.getTransferProcessId())))); - - var list1 = getStore().nextNotLeased(5, hasState(NEGOTIATED.code())); - Thread.sleep(50); //simulate a short delay to generate different timestamps - list1.forEach(tp -> { - tp.updateStateTimestamp(); - getStore().update(tp); - }); - var list2 = getStore().nextNotLeased(5, hasState(NEGOTIATED.code())); - assertThat(list1).isNotEqualTo(list2).doesNotContainAnyElementsOf(list2); - } - - @Test - @DisplayName("Verify that the lease on a TP is cleared by an update") - void update_shouldBreakLease() { - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - // acquire lease - lockEntity(entry.getId(), CONNECTOR_NAME); - - entry.transitionToRefreshing(); //modify - getStore().update(entry); - - // lease should be broken - var notLeased = getStore().nextNotLeased(10, hasState(REFRESHING.code())); - - assertThat(notLeased).usingRecursiveFieldByFieldElementComparator().containsExactly(entry); - } - - @Test - void update_leasedByOther_shouldThrowException() { - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - - lockEntity(entry.getId(), "someone"); - - entry.transitionToRefreshing(); - - // leased by someone else -> throw exception - assertThatThrownBy(() -> getStore().update(entry)).isInstanceOf(IllegalStateException.class); - } - - @Test - void delete_isLeasedBySelf_shouldThrowException() { - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - - lockEntity(entry.getId(), CONNECTOR_NAME); - - - assertThatThrownBy(() -> getStore().deleteByTransferProcessId(entry.getTransferProcessId())).isInstanceOf(IllegalStateException.class); - } - - @Test - void delete_isLeasedByOther_shouldThrowException() { - var entry = edrEntry("assetId", "agreementId", "tpId"); - getStore().save(entry, edr("edrId")); - - lockEntity(entry.getId(), "someone-else"); - - assertThatThrownBy(() -> getStore().deleteByTransferProcessId(entry.getTransferProcessId())).isInstanceOf(IllegalStateException.class); - } - - protected abstract EndpointDataReferenceCache getStore(); - - protected abstract void lockEntity(String negotiationId, String owner, Duration duration); - - protected void lockEntity(String negotiationId, String owner) { - lockEntity(negotiationId, owner, Duration.ofSeconds(60)); - } - - protected abstract boolean isLockedBy(String negotiationId, String owner); - - private void delayByTenMillis(EndpointDataReferenceEntry t) { - try { - Thread.sleep(10); - } catch (InterruptedException ignored) { - // noop - } - t.updateStateTimestamp(); - } -} diff --git a/spi/edr-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/TestFunctions.java b/spi/edr-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/TestFunctions.java deleted file mode 100644 index 00312f669..000000000 --- a/spi/edr-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/TestFunctions.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.edr.spi; - -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates; - -import java.util.UUID; - -import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.NEGOTIATED; - -public class TestFunctions { - - - public static EndpointDataReference edr(String id) { - return EndpointDataReference.Builder.newInstance() - .endpoint("http://test.com") - .contractId("test-contract-id") - .id(id) - .authCode("11111") - .authKey("authentication").build(); - } - - public static EndpointDataReferenceEntry edrEntry(String assetId, String agreementId, String transferProcessId) { - return edrEntry(assetId, agreementId, transferProcessId, NEGOTIATED); - } - - public static EndpointDataReferenceEntry edrEntry(String assetId, String agreementId, String transferProcessId, String contractNegotiationId) { - return edrEntry(assetId, agreementId, transferProcessId, NEGOTIATED, contractNegotiationId); - } - - public static EndpointDataReferenceEntry edrEntry(String assetId, String agreementId, String transferProcessId, EndpointDataReferenceEntryStates state) { - return edrEntry(assetId, agreementId, transferProcessId, state, null); - } - - public static EndpointDataReferenceEntry edrEntry(String assetId, String agreementId, String transferProcessId, EndpointDataReferenceEntryStates state, String contractNegotiationId) { - return EndpointDataReferenceEntry.Builder.newInstance() - .assetId(assetId) - .agreementId(agreementId) - .transferProcessId(transferProcessId) - .providerId(UUID.randomUUID().toString()) - .contractNegotiationId(contractNegotiationId) - .state(state.code()) - .build(); - } -} diff --git a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/SsiCredentialClient.java b/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/SsiCredentialClient.java deleted file mode 100644 index e422455f6..000000000 --- a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/SsiCredentialClient.java +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi; - -import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; -import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.TokenParameters; -import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.result.Result; - -/** - * Obtains client security tokens from an identity provider. - * Providers may implement different authorization protocols such as OAuth2. - */ - -@ExtensionPoint -public interface SsiCredentialClient { - - /** - * Obtains a client token encoded as a JWT. - * - * @param parameters parameter object defining the token properties. - * @return generated client token. - */ - - Result obtainClientCredentials(TokenParameters parameters); - - /** - * Verifies a JWT bearer token. - * - * @param tokenRepresentation A token representation including the token to verify. - * @return Result of the validation. - */ - - Result validate(TokenRepresentation tokenRepresentation); -} diff --git a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/CredentialsNamespaces.java b/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/CredentialsNamespaces.java deleted file mode 100644 index dee293b0d..000000000 --- a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/CredentialsNamespaces.java +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi.jsonld; - -/** - * Defines policy namespaces. - */ -public interface CredentialsNamespaces { - - String W3C_VC_PREFIX = "https://www.w3.org/2018/credentials"; - String W3C_VC_NS = W3C_VC_PREFIX + "/v1"; - String VP_PROPERTY = "vp"; - String CX_NS = "https://w3id.org/2023/catenax/credentials/"; - String CX_SUMMARY_NS = CX_NS + "summary"; - String CX_SUMMARY_NS_V1 = CX_SUMMARY_NS + "/v1"; - String SUMMARY_CREDENTIAL_TYPE = CX_SUMMARY_NS + "/SummaryCredential"; - String HOLDER_IDENTIFIER = CX_SUMMARY_NS + "/holderIdentifier"; - String CX_USE_CASE_NS = CX_NS + "usecase"; - String CX_USE_CASE_NS_V1 = CX_USE_CASE_NS + "/v1"; - String CX_SUMMARY_CREDENTIAL = "SummaryCredential"; - String CREDENTIAL_SUBJECT = W3C_VC_PREFIX + "#credentialSubject"; - String CREDENTIAL_ISSUER = W3C_VC_PREFIX + "#issuer"; - -} diff --git a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdFieldExtractor.java b/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdFieldExtractor.java deleted file mode 100644 index 1d9e6df46..000000000 --- a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdFieldExtractor.java +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi.jsonld; - -import jakarta.json.JsonObject; -import org.eclipse.edc.spi.result.Result; - -import java.util.Objects; - -import static jakarta.json.JsonValue.ValueType.ARRAY; -import static jakarta.json.JsonValue.ValueType.OBJECT; -import static java.lang.String.format; - -/** - * Extractor for field from a {@link JsonObject} with a customizable error reporting - */ -public class JsonLdFieldExtractor { - - private String fieldAlias; - private String errorPrefix = ""; - private String field; - - private JsonLdFieldExtractor() { - } - - /** - * Extract a field by name. If not found return an error. - */ - public Result extract(JsonObject root) { - var subjectArray = root.get(field); - if (subjectArray == null || subjectArray.getValueType() != ARRAY) { - return Result.failure(errorPrefix + format(" no %s found", fieldAlias)); - } - if (subjectArray.asJsonArray().size() != 1) { - return Result.failure(errorPrefix + format(" empty %s", fieldAlias)); - } - - var subjectValue = subjectArray.asJsonArray().get(0); - if (subjectValue == null || subjectValue.getValueType() != OBJECT) { - return Result.failure(errorPrefix + format(" invalid %s format", fieldAlias)); - } - return Result.success(subjectValue.asJsonObject()); - } - - public static class Builder { - - private final JsonLdFieldExtractor extractor; - - private Builder(JsonLdFieldExtractor extractor) { - this.extractor = extractor; - } - - public static Builder newInstance() { - return new Builder(new JsonLdFieldExtractor()); - } - - public Builder field(String field) { - this.extractor.field = field; - return this; - } - - public Builder fieldAlias(String fieldAlias) { - this.extractor.fieldAlias = fieldAlias; - return this; - } - - public Builder errorPrefix(String errorPrefix) { - this.extractor.errorPrefix = errorPrefix; - return this; - } - - public JsonLdFieldExtractor build() { - Objects.requireNonNull(extractor.field); - Objects.requireNonNull(extractor.fieldAlias); - Objects.requireNonNull(extractor.errorPrefix); - return extractor; - } - - } - -} diff --git a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdTypeFunctions.java b/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdTypeFunctions.java deleted file mode 100644 index e3b5f80d0..000000000 --- a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdTypeFunctions.java +++ /dev/null @@ -1,111 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi.jsonld; - -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import jakarta.json.JsonStructure; -import jakarta.json.JsonValue; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Stream; - -import static java.util.Collections.emptySet; -import static java.util.stream.Collectors.toSet; - -/** - * Provides functions for working with Json-Ld types. - */ -public class JsonLdTypeFunctions { - private static final String TYPE = "@type"; - private static final Stream EMPTY_STREAM = Stream.of(); - - private JsonLdTypeFunctions() { - } - - /** - * Returns a stream of objects that are of the given Json-Ld type starting at the root. - * - * @param typeValue the type to include - * @param root the root object to traverse - * @return the stream of types - */ - public static Stream extractObjectsOfType(String typeValue, JsonStructure root) { - if (root instanceof JsonObject rootObject) { - return matchTypeValue(typeValue, rootObject.get(TYPE)) ? Stream.of(rootObject) : - extractObjectsOfType(typeValue, rootObject.values().stream()); - } else if (root instanceof JsonArray rootArray) { - return extractObjectsOfType(typeValue, rootArray.stream()); - } - return EMPTY_STREAM; - } - - /** - * Returns a stream of objects that are of the given Json-Ld type in the stream. - * - * @param typeValue the type to include - * @param stream the stream of roots to traverse - * @return the stream of types - */ - public static Stream extractObjectsOfType(String typeValue, Stream stream) { - return stream.filter(v -> v instanceof JsonStructure) - .flatMap(v -> extractObjectsOfType(typeValue, (JsonStructure) v)).filter(Objects::nonNull); - } - - /** - * Partitions a stream of objects by their type, returning a type-to-collection mapping. - */ - public static Map> partitionByType(Stream stream) { - var partitions = new HashMap>(); - stream.forEach(object -> getTypes(object).forEach(type -> partitions.computeIfAbsent(type, k -> new ArrayList<>()).add(object))); - return partitions; - } - - /** - * Returns the types associated with the object - */ - private static Set getTypes(JsonObject object) { - var result = object.get(TYPE); - if (result instanceof JsonArray resultArray) { - return resultArray.stream().filter(e -> e instanceof JsonString).map(s -> ((JsonString) s).getString()).collect(toSet()); - } else if (result instanceof JsonString resultString) { - return Set.of(resultString.getString()); - } - return emptySet(); - } - - /** - * Returns true if the type value matches the Json value. - */ - private static boolean matchTypeValue(String typeValue, JsonValue jsonValue) { - if (jsonValue instanceof JsonString stringValue) { - return typeValue.equals(stringValue.getString()); - } else if (jsonValue instanceof JsonArray arrayValue) { - return arrayValue.stream().anyMatch(v -> v instanceof JsonString && typeValue.equals(((JsonString) v).getString())); - } - return false; - } -} diff --git a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdValueFunctions.java b/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdValueFunctions.java deleted file mode 100644 index e0abefdc7..000000000 --- a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdValueFunctions.java +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi.jsonld; - -import jakarta.json.JsonArray; -import jakarta.json.JsonNumber; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; -import org.jetbrains.annotations.Nullable; - -import static jakarta.json.JsonValue.ValueType; - -/** - * Functions for working with Json-ld values. - */ -public class JsonLdValueFunctions { - private static final String VALUE = "@value"; - - private JsonLdValueFunctions() { - } - - /** - * Extracts the value of a root node and converts it to a string representation. Note this method accepts null nodes as a convenience. - */ - @Nullable - public static String extractStringValue(@Nullable JsonValue root) { - if (root == null) { - return null; - } else if (root instanceof JsonArray rootArray) { - if (rootArray.isEmpty()) { - return null; - } - var jsonValue = rootArray.get(0); - return (jsonValue instanceof JsonObject elementObject) ? convertType(elementObject.get(VALUE)) : null; - } else if (root instanceof JsonObject rootObject) { - return convertType(rootObject.get(VALUE)); - } else { - return convertType(root); - } - } - - /** - * Converts the value to a string representation. - */ - @Nullable - private static String convertType(JsonValue value) { - if (value instanceof JsonString valueString) { - return valueString.getString(); - } else if (value instanceof JsonNumber valueNumber) { - return valueNumber.isIntegral() ? String.valueOf(valueNumber.longValue()) : String.valueOf(valueNumber.doubleValue()); - } else if (ValueType.TRUE == value.getValueType()) { - return "TRUE"; - } else if (ValueType.FALSE == value.getValueType()) { - return "FALSE"; - } - return null; - } -} diff --git a/spi/ssi-spi/src/test/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdFieldExtractorTest.java b/spi/ssi-spi/src/test/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdFieldExtractorTest.java deleted file mode 100644 index 9e7cb3b66..000000000 --- a/spi/ssi-spi/src/test/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdFieldExtractorTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi.jsonld; - -import jakarta.json.JsonObject; -import org.eclipse.edc.spi.result.Result; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CREDENTIAL_SUBJECT; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_SUMMARY_NS_V1; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.HOLDER_IDENTIFIER; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.SUMMARY_CREDENTIAL_TYPE; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.createObjectMapper; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.expand; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTypeFunctions.extractObjectsOfType; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP; - -public class JsonLdFieldExtractorTest { - - private static final Map CONTEXT_CACHE = Map.of(CX_SUMMARY_NS_V1, SummaryContext.SUMMARY_CONTEXT); - - @Test - void extract() throws Exception { - var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); - - var extractor = JsonLdFieldExtractor.Builder.newInstance() - .field(CREDENTIAL_SUBJECT) - .fieldAlias("credentialSubject") - .errorPrefix("prefix") - .build(); - - - var summaryCredential = extractObjectsOfType(SUMMARY_CREDENTIAL_TYPE, vp).findFirst().orElseThrow(); - - var subject = extractor.extract(summaryCredential); - assertThat(subject).matches(Result::succeeded).extracting(Result::getContent) - .satisfies(jsonObject -> assertThat(jsonObject.containsKey(HOLDER_IDENTIFIER)).isTrue()); - - } - - @Test - void extract_fail() throws Exception { - var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); - - var extractor = JsonLdFieldExtractor.Builder.newInstance() - .field(HOLDER_IDENTIFIER) - .fieldAlias("holderIdentifier") - .errorPrefix("prefix") - .build(); - - var summaryCredential = extractObjectsOfType(SUMMARY_CREDENTIAL_TYPE, vp).findFirst().orElseThrow(); - - var subject = extractor.extract(summaryCredential); - assertThat(subject).matches(Result::failed).extracting(Result::getFailureDetail) - .satisfies(errorMessage -> { - assertThat(errorMessage).isEqualTo("prefix no holderIdentifier found"); - }); - - } -} diff --git a/spi/ssi-spi/src/test/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdTypeFunctionsTest.java b/spi/ssi-spi/src/test/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdTypeFunctionsTest.java deleted file mode 100644 index a8acea1a9..000000000 --- a/spi/ssi-spi/src/test/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdTypeFunctionsTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi.jsonld; - -import com.fasterxml.jackson.core.JsonProcessingException; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.Map; - -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.W3C_VC_PREFIX; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.createObjectMapper; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.expand; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTypeFunctions.extractObjectsOfType; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTypeFunctions.partitionByType; - -class JsonLdTypeFunctionsTest { - private static final String TYPE = "@type"; - private static final String VC_TYPE = W3C_VC_PREFIX + "#VerifiableCredential"; - - private static final String BAR_CREDENTIAL_TYPE = "BarCredential"; - private static final String FOO_CREDENTIAL_TYPE = "FooCredential"; - private static final String FOO_CREDENTIAL = """ - { - "type": "VerifiablePresentation", - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", - "type": [ - "VerifiableCredential", - "FooCredential" - ] - } - ] - }"""; - private static final String BAR_CREDENTIAL = """ - { - "type": "VerifiablePresentation", - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": [ - "VerifiableCredential", - "BarCredential" - ] - } - ] - }"""; - private static final String MULTIPLE_VCS_CLAIM = format(""" - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - { - "vp":"test:vp" - } - ], - "vp": [%s,%s] - }""", FOO_CREDENTIAL, BAR_CREDENTIAL); - private static final String SINGLE_VC_CLAIM = format(""" - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - { - "vp":"test:vp" - } - ], - "vp": %s - }""", FOO_CREDENTIAL); - - @Test - void verify_credential_extraction() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(SINGLE_VC_CLAIM, JsonObject.class), Map.of()); - - var credentials = extractObjectsOfType(VC_TYPE, vp).toList(); - - assertThat(credentials.size()).isEqualTo(1); - assertAllOfType(FOO_CREDENTIAL_TYPE, credentials); - } - - @Test - void verify_partitions_based_on_type() throws JsonProcessingException { - var vp = expand(createObjectMapper().readValue(MULTIPLE_VCS_CLAIM, JsonObject.class), Map.of()); - - var credentials = extractObjectsOfType(VC_TYPE, vp); - var partitions = partitionByType(credentials); - - assertThat(partitions.size()).isEqualTo(3); - - assertAllOfType(FOO_CREDENTIAL_TYPE, partitions.get(FOO_CREDENTIAL_TYPE)); - assertAllOfType(BAR_CREDENTIAL_TYPE, partitions.get(BAR_CREDENTIAL_TYPE)); - assertThat(partitions.get(VC_TYPE).size()).isEqualTo(2); - } - - /** - * Asserts that all objects in the collection are of a given type. - */ - private void assertAllOfType(String type, List objects) { - assertThat(objects.stream() - .flatMap(object -> object.get(TYPE).asJsonArray().stream()) - .filter(value -> value instanceof JsonString) - .filter(entryType -> type.equals(((JsonString) entryType).getString())) - .count()).isEqualTo(objects.size()); - } - - -} diff --git a/spi/ssi-spi/src/test/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdValueFunctionsTest.java b/spi/ssi-spi/src/test/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdValueFunctionsTest.java deleted file mode 100644 index d1e0ff98c..000000000 --- a/spi/ssi-spi/src/test/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdValueFunctionsTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi.jsonld; - -import org.junit.jupiter.api.Test; - -import static jakarta.json.Json.createArrayBuilder; -import static jakarta.json.Json.createObjectBuilder; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdValueFunctions.extractStringValue; - -class JsonLdValueFunctionsTest { - private static final String VALUE = "@value"; - - @Test - void validate_nested_array() { - var root = createArrayBuilder() - .add(createObjectBuilder().add(VALUE, "test").build()) - .build(); - - assertThat(extractStringValue(root)).isEqualTo("test"); - } - - @Test - void validate_empty_array() { - var root = createArrayBuilder().build(); - assertThat(extractStringValue(root)).isNull(); - } - - @Test - void validate_object() { - var root = createObjectBuilder().add(VALUE, "test").build(); - assertThat(extractStringValue(root)).isEqualTo("test"); - } - - @Test - void validate_object_int() { - var root = createObjectBuilder().add(VALUE, 1).build(); - assertThat(extractStringValue(root)).isEqualTo("1"); - } - - @Test - void validate_object_double() { - var root = createObjectBuilder().add(VALUE, 1.1d).build(); - assertThat(extractStringValue(root)).isEqualTo("1.1"); - } - - @Test - void validate_null() { - assertThat(extractStringValue(null)).isNull(); - } - -} diff --git a/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdTextFixtures.java b/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdTextFixtures.java deleted file mode 100644 index cfe9216d3..000000000 --- a/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/JsonLdTextFixtures.java +++ /dev/null @@ -1,95 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi.jsonld; - -import com.apicatalog.jsonld.JsonLdError; -import com.apicatalog.jsonld.JsonLdOptions; -import com.apicatalog.jsonld.document.JsonDocument; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.datatype.jsonp.JSONPModule; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import jakarta.json.Json; -import jakarta.json.JsonObject; - -import java.io.StringReader; -import java.util.HashMap; -import java.util.Map; - -import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.W3C_VC_NS; -import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.W3cVcContext.W3C_VC_CONTEXT; - -/** - * Test helpers for processing Json-Ld. - */ -public class JsonLdTextFixtures { - - /** - * Creates a mapper configured to support Json-Ld processing. - */ - public static ObjectMapper createObjectMapper() { - var mapper = new ObjectMapper(); - mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); - mapper.registerModule(new JavaTimeModule()); - mapper.registerModule(new JSONPModule()); - var module = new SimpleModule() { - @Override - public void setupModule(SetupContext context) { - super.setupModule(context); - } - }; - mapper.registerModule(module); - return mapper; - } - - /** - * Performs Json-Ld compaction on an object. - */ - public static JsonObject compact(JsonObject json) { - try { - var document = JsonDocument.of(json); - var jsonFactory = Json.createBuilderFactory(Map.of()); - var contextDocument = JsonDocument.of(jsonFactory.createObjectBuilder().build()); - return com.apicatalog.jsonld.JsonLd.compact(document, contextDocument).get(); - } catch (JsonLdError e) { - throw new AssertionError(e); - } - } - - /** - * Expands the document using the provided cache for resolving referenced contexts. The {@link CredentialsNamespaces#W3C_VC_NS} context is implicitly added to the cache. - */ - public static JsonObject expand(JsonObject json, Map contextCache) { - var map = new HashMap<>(contextCache); - map.put(W3C_VC_NS, W3C_VC_CONTEXT); - try { - var document = JsonDocument.of(json); - var options = new JsonLdOptions((url, options1) -> JsonDocument.of(new StringReader(map.get(url.toString())))); - var expanded = com.apicatalog.jsonld.JsonLd.expand(document).options(options).get(); - if (expanded.size() > 0) { - return expanded.getJsonObject(0); - } - return Json.createObjectBuilder().build(); - } catch (JsonLdError e) { - throw new AssertionError(e); - } - } -} diff --git a/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/SummaryContext.java b/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/SummaryContext.java deleted file mode 100644 index 2b8d33063..000000000 --- a/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/SummaryContext.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi.jsonld; - -/** - * Defines the summary context. - */ -public interface SummaryContext { - String SUMMARY_CONTEXT = """ - { - "@context": { - "@version": 1.1, - "@protected": true, - "summary": "https://w3id.org/2023/catenax/credentials/summary/", - "id": "@id", - "type": "@type", - "SummaryCredential" : { - "@id":"summary:SummaryCredential" - }, - "holderIdentifier": { - "@id": "summary:holderIdentifier" - }, - "name": { - "@id": "summary:name", - "@type": "https://schema.org/Text" - }, - "items": { - "@id": "summary:items", - "@type": "https://schema.org/Text" - }, - "contract-template": { - "@id": "summary:contract-template", - "@type": "https://schema.org/Text" - } - } - }"""; -} diff --git a/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/SummaryCredential.java b/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/SummaryCredential.java deleted file mode 100644 index 66266823b..000000000 --- a/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/SummaryCredential.java +++ /dev/null @@ -1,153 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi.jsonld; - -/** - * Sample summary credential. - */ -public interface SummaryCredential { - String SUMMARY_VP = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": "VerifiablePresentation", - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/2023/catenax/credentials/summary/v1" - ], - "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", - "type": [ - "VerifiableCredential", - "SummaryCredential" - ], - "issuer": "did:web:issuer-example.com", - "issuanceDate": "2023-06-02T12:00:00Z", - "expirationDate": "2022-06-16T18:56:59Z", - "credentialSubject": { - "id": "did:web:example.com", - "holderIdentifier": "BPN of holder", - "type": "Summary-List", - "name": "CX-Credentials", - "items": [ - "MembershipCredential", - "DismantlerCredential", - "PcfCredential", - "SustainabilityCredential", - "QualityCredential", - "TraceabilityCredential", - "BehaviorTwinCredential", - "BpnCredential" - ], - "contractTemplates": "https://public.catena-x.org/contracts/" - }, - "proof": { - "type": "Ed25519Signature2018", - "created": "2023-06-02T12:00:00Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:web:example.com#key-1", - "jws": "xxxx" - } - } - ] - } - """; - - String SIMPLE_VP = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": "VerifiablePresentation", - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", - "type": [ - "VerifiableCredential" - ], - "issuer": "did:web:example.com", - "issuanceDate": "2023-06-02T12:00:00Z", - "expirationDate": "2022-06-16T18:56:59Z", - "credentialSubject": { - "id": "did:web:example.com" - } - } - ] - } - """; - - String SUMMARY_VP_NO_HOLDER = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": "VerifiablePresentation", - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/2023/catenax/credentials/summary/v1" - ], - "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", - "type": [ - "VerifiableCredential", - "SummaryCredential" - ], - "issuer": "did:web:no-holder.example.com:BPNL000000000000", - "issuanceDate": "2023-06-02T12:00:00Z", - "expirationDate": "2022-06-16T18:56:59Z", - "credentialSubject": { - "id": "did:web:example.com" - } - } - ] - } - """; - - String SUMMARY_VP_NO_SUBJECT = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": "VerifiablePresentation", - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/2023/catenax/credentials/summary/v1" - ], - "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", - "type": [ - "VerifiableCredential", - "SummaryCredential" - ], - "issuer": "did:web:example.com", - "issuanceDate": "2023-06-02T12:00:00Z", - "expirationDate": "2022-06-16T18:56:59Z" - } - ] - } - """; -} diff --git a/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/W3cVcContext.java b/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/W3cVcContext.java deleted file mode 100644 index 4df64ba35..000000000 --- a/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/W3cVcContext.java +++ /dev/null @@ -1,268 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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.edc.iam.ssi.spi.jsonld; - -/** - * Local copy of the W3C VC data model context for testing, obtained from {@code https://www.w3.org/ns/credentials/v2}. - */ -public interface W3cVcContext { - - String W3C_VC_CONTEXT = """ - { - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "VerifiableCredential": { - "@id": "https://www.w3.org/2018/credentials#VerifiableCredential", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "cred": "https://www.w3.org/2018/credentials#", - "sec": "https://w3id.org/security#", - "xsd": "http://www.w3.org/2001/XMLSchema#", - - "credentialSchema": { - "@id": "cred:credentialSchema", - "@type": "@id", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "cred": "https://www.w3.org/2018/credentials#", - - "JsonSchemaValidator2018": "cred:JsonSchemaValidator2018" - } - }, - "credentialStatus": {"@id": "cred:credentialStatus", "@type": "@id"}, - "credentialSubject": {"@id": "cred:credentialSubject", "@type": "@id"}, - "evidence": {"@id": "cred:evidence", "@type": "@id"}, - "expirationDate": {"@id": "cred:expirationDate", "@type": "xsd:dateTime"}, - "holder": {"@id": "cred:holder", "@type": "@id"}, - "issued": {"@id": "cred:issued", "@type": "xsd:dateTime"}, - "issuer": {"@id": "cred:issuer", "@type": "@id"}, - "issuanceDate": {"@id": "cred:issuanceDate", "@type": "xsd:dateTime"}, - "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, - "refreshService": { - "@id": "cred:refreshService", - "@type": "@id", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "cred": "https://www.w3.org/2018/credentials#", - - "ManualRefreshService2018": "cred:ManualRefreshService2018" - } - }, - "termsOfUse": {"@id": "cred:termsOfUse", "@type": "@id"}, - "validFrom": {"@id": "cred:validFrom", "@type": "xsd:dateTime"}, - "validUntil": {"@id": "cred:validUntil", "@type": "xsd:dateTime"} - } - }, - - "VerifiablePresentation": { - "@id": "https://www.w3.org/2018/credentials#VerifiablePresentation", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "cred": "https://www.w3.org/2018/credentials#", - "sec": "https://w3id.org/security#", - - "holder": {"@id": "cred:holder", "@type": "@id"}, - "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, - "verifiableCredential": {"@id": "cred:verifiableCredential", "@type": "@id", "@container": "@graph"} - } - }, - - "EcdsaSecp256k1Signature2019": { - "@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - "xsd": "http://www.w3.org/2001/XMLSchema#", - - "challenge": "sec:challenge", - "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, - "domain": "sec:domain", - "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, - "jws": "sec:jws", - "nonce": "sec:nonce", - "proofPurpose": { - "@id": "sec:proofPurpose", - "@type": "@vocab", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - - "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, - "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} - } - }, - "proofValue": "sec:proofValue", - "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} - } - }, - - "EcdsaSecp256r1Signature2019": { - "@id": "https://w3id.org/security#EcdsaSecp256r1Signature2019", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - "xsd": "http://www.w3.org/2001/XMLSchema#", - - "challenge": "sec:challenge", - "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, - "domain": "sec:domain", - "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, - "jws": "sec:jws", - "nonce": "sec:nonce", - "proofPurpose": { - "@id": "sec:proofPurpose", - "@type": "@vocab", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - - "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, - "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} - } - }, - "proofValue": "sec:proofValue", - "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} - } - }, - - "Ed25519Signature2018": { - "@id": "https://w3id.org/security#Ed25519Signature2018", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - "xsd": "http://www.w3.org/2001/XMLSchema#", - - "challenge": "sec:challenge", - "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, - "domain": "sec:domain", - "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, - "jws": "sec:jws", - "nonce": "sec:nonce", - "proofPurpose": { - "@id": "sec:proofPurpose", - "@type": "@vocab", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - - "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, - "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} - } - }, - "proofValue": "sec:proofValue", - "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} - } - }, - - "RsaSignature2018": { - "@id": "https://w3id.org/security#RsaSignature2018", - "@context": { - "@version": 1.1, - "@protected": true, - - "challenge": "sec:challenge", - "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, - "domain": "sec:domain", - "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, - "jws": "sec:jws", - "nonce": "sec:nonce", - "proofPurpose": { - "@id": "sec:proofPurpose", - "@type": "@vocab", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - - "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, - "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} - } - }, - "proofValue": "sec:proofValue", - "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} - } - }, - - "proof": {"@id": "https://w3id.org/security#proof", "@type": "@id", "@container": "@graph"} - } - } - """; - - -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/build.gradle.kts b/spi/tokenrefresh-spi/build.gradle.kts similarity index 94% rename from edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/build.gradle.kts rename to spi/tokenrefresh-spi/build.gradle.kts index e010535f9..3560fe265 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-provider-spi/build.gradle.kts +++ b/spi/tokenrefresh-spi/build.gradle.kts @@ -19,9 +19,10 @@ plugins { `java-library` + `java-test-fixtures` } dependencies { implementation(libs.edc.spi.core) + implementation(libs.edc.spi.jwt) } - diff --git a/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/common/TokenRefreshHandler.java b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/common/TokenRefreshHandler.java new file mode 100644 index 000000000..952b40dda --- /dev/null +++ b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/common/TokenRefreshHandler.java @@ -0,0 +1,45 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.spi.tokenrefresh.common; + +import org.eclipse.edc.spi.result.ServiceResult; +import org.eclipse.edc.spi.types.domain.DataAddress; + +/** + * Handles token refreshing against an OAuth2-compliant token refresh endpoint. + */ +public interface TokenRefreshHandler { + /** + * Refreshes a token identified by the token ID and returns the refreshed token. + * + * @param tokenId The ID of the token, e.g. a {@code jti} claim in JWT tokens. + * @return An updated access+refresh token pair. + */ + ServiceResult refreshToken(String tokenId); + + /** + * Refreshes a token identified by the token ID and returns the refreshed token. + * + * @param tokenId The ID of the token, e.g. a {@code jti} claim in JWT tokens. + * @param edr The {@link DataAddress} containing the EDR + * @return An updated access+refresh token pair. + */ + ServiceResult refreshToken(String tokenId, DataAddress edr); +} diff --git a/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/DataPlaneTokenRefreshService.java b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/DataPlaneTokenRefreshService.java new file mode 100644 index 000000000..a5d04b2bf --- /dev/null +++ b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/DataPlaneTokenRefreshService.java @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.spi.tokenrefresh.dataplane; + +import org.eclipse.edc.spi.result.Result; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; + +/** + * This service receives an incoming token refresh request, validates it and generates a new token pair (access token + refresh token). + */ +public interface DataPlaneTokenRefreshService { + /** + * Generates a new token pair (access and refresh token) based on an existing refresh token and access token. + * + * @param refreshToken The refresh token that was issued in the original/previous token request. + * @param authenticationToken A client authentication token + * @return A result that contains the new access and refresh token, or a failure. + */ + Result refreshToken(String refreshToken, String authenticationToken); +} diff --git a/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/model/TokenResponse.java b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/model/TokenResponse.java new file mode 100644 index 000000000..148bf95d8 --- /dev/null +++ b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/model/TokenResponse.java @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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.edc.spi.tokenrefresh.dataplane.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record TokenResponse(@JsonProperty("access_token") String accessToken, + @JsonProperty("refresh_token") String refreshToken, + @JsonProperty("expires") Long expiresInSeconds, + @JsonProperty("token_type") String tokenType) { +}