diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml index 22b20f283..4a887293f 100644 --- a/.github/actions/publish-docker-image/action.yml +++ b/.github/actions/publish-docker-image/action.yml @@ -50,6 +50,14 @@ runs: steps: - uses: actions/checkout@v3.3.0 + - name: Download OpenTelemetry + shell: bash + run: |- + # "jq -r" removes the quotation marks, that would trip up "wget" + URL=$(grep "val openTelemetryAgentUrl = " build.gradle.kts | awk -F'= ' '{print $2}' | jq -r) + wget -O ${{ inputs.rootDir }}/opentelemetry-javaagent.jar -q $URL + + ##################### # Login to DockerHub ##################### @@ -97,6 +105,7 @@ runs: file: ${{ inputs.rootDir }}/src/main/docker/Dockerfile build-args: | JAR=${{ inputs.rootDir }}/build/libs/${{ inputs.imagename }}.jar + OTEL_JAR=${{ inputs.rootDir }}/opentelemetry-javaagent.jar push: ${{ inputs.do_push == 'true' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e3324e771..79b5d3173 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -29,7 +29,7 @@ updates: - "dependabot" - "dependencies" schedule: - interval: "daily" + interval: "weekly" # Github Actions - @@ -40,7 +40,7 @@ updates: - "dependabot" - "github-actions" schedule: - interval: "daily" + interval: "weekly" # Docker - @@ -51,7 +51,7 @@ updates: - "dependabot" - "docker" schedule: - interval: "daily" + interval: "weekly" - package-ecosystem: "docker" target-branch: main @@ -60,7 +60,7 @@ updates: - "dependabot" - "docker" schedule: - interval: "daily" + interval: "weekly" - package-ecosystem: "docker" target-branch: main @@ -69,7 +69,7 @@ updates: - "dependabot" - "docker" schedule: - interval: "daily" + interval: "weekly" - package-ecosystem: "docker" target-branch: main @@ -78,7 +78,7 @@ updates: - "dependabot" - "docker" schedule: - interval: "daily" + interval: "weekly" - package-ecosystem: "docker" target-branch: main @@ -87,4 +87,4 @@ updates: - "dependabot" - "docker" schedule: - interval: "daily" + interval: "weekly" diff --git a/.github/workflows/deployment-test.yaml b/.github/workflows/deployment-test.yaml index 646e4ef0c..3060eb940 100644 --- a/.github/workflows/deployment-test.yaml +++ b/.github/workflows/deployment-test.yaml @@ -73,7 +73,7 @@ jobs: kubectl rollout status deployment tx-inmem # execute the helm test - helm test tx-inmem --logs + helm test tx-inmem test-hashicorp-postgres: runs-on: ubuntu-latest @@ -98,7 +98,7 @@ jobs: kubectl rollout status deployment tx-prod-dataplane # execute the helm test - helm test tx-prod --logs + helm test tx-prod test-azure-vault-postgres: runs-on: ubuntu-latest @@ -135,4 +135,4 @@ jobs: kubectl rollout status deployment tx-prod-dataplane # execute the helm test - helm test tx-prod --logs + helm test tx-prod diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 8a18e3aae..221ff3e21 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -53,7 +53,7 @@ jobs: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: Bump version in /charts - uses: mikefarah/yq@v4.34.1 + uses: mikefarah/yq@v4.35.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/kics.yml b/.github/workflows/kics.yml index c276bad00..0d824632e 100644 --- a/.github/workflows/kics.yml +++ b/.github/workflows/kics.yml @@ -50,6 +50,7 @@ jobs: disable_secrets: true output_path: kicsResults/ output_formats: "json,sarif" + exclude_paths: "edc-tests/miw-tests/src/test/resources/docker-environment/docker-compose.yaml" - name: Upload SARIF file for GitHub Advanced Security Dashboard if: always() diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 428b2065d..7e000da16 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -239,3 +239,13 @@ jobs: git commit --message "Introduce new snapshot version $SNAPSHOT_VERSION" git push origin main + + publish-to-swaggerhub: + name: "Publish OpenAPI spec to Swaggerhub" + permissions: + contents: read + needs: [ release-version ] + uses: ./.github/workflows/publish-swaggerhub.yaml + with: + downstream-version: ${{ needs.release-version.outputs.RELEASE_VERSION }} + secrets: inherit diff --git a/.github/workflows/publish-swaggerhub.yaml b/.github/workflows/publish-swaggerhub.yaml new file mode 100644 index 000000000..2529a093d --- /dev/null +++ b/.github/workflows/publish-swaggerhub.yaml @@ -0,0 +1,113 @@ +# +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# 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: "Publish OpenAPI to Swaggerhub" + +on: + workflow_call: + inputs: + downstream-version: + required: false + type: string + upstream-version: + required: false + type: string + + workflow_dispatch: + inputs: + downstream-version: + required: false + description: "Version of the Tractus-X EDC API to be should be published" + type: string + upstream-version: + required: false + description: "Version of upstream EDC which is to be used" + type: string + +jobs: + swagger-api: + runs-on: ubuntu-latest + env: + SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }} + SWAGGERHUB_USER: ${{ secrets.SWAGGERHUB_USER }} + steps: + - uses: actions/checkout@v3 + + - name: Setup JDK 17 + uses: actions/setup-java@v3.12.0 + with: + java-version: '17' + distribution: 'temurin' + cache: 'gradle' + + - name: Setup node + uses: actions/setup-node@v3 + + - name: Install Swagger CLI + run: | + npm i -g swaggerhub-cli + + - name: Extract versions + run: | + if [ -z ${{ inputs.downstream-version }} ]; then + export DOWNSTREAM_VERSION=$(grep "version" gradle.properties | awk -F= '{print $2}') + else + export DOWNSTREAM_VERSION=${{ inputs.downstream-version }} + fi + + if [ -z ${{ inputs.upstream-version }} ]; then + export UPSTREAM_VERSION=$(grep "edc = " gradle/libs.versions.toml | awk -F= '{print $2}' | jq -r) + else + export UPSTREAM_VERSION=${{ inputs.upstream-version }} + fi + echo "DOWNSTREAM_VERSION=$DOWNSTREAM_VERSION" >> "$GITHUB_ENV" + echo "UPSTREAM_VERSION=$UPSTREAM_VERSION" >> "$GITHUB_ENV" + + - name: Resolve TX EDC API Spec + shell: bash + run: | + ./gradlew resolve + + - name: Download upstream API specs + run: | + curl -X GET https://api.swaggerhub.com/apis/eclipse-edc-bot/management-api/${{ env.UPSTREAM_VERSION }}/swagger.yaml > resources/openapi/yaml/upstream-management-api.yaml + curl -X GET https://api.swaggerhub.com/apis/eclipse-edc-bot/control-api/${{ env.UPSTREAM_VERSION }}/swagger.yaml > resources/openapi/yaml/upstream-control-api.yaml + + - name: Merge API specs + run: | + ./gradlew -PapiTitle="Tractus-X EDC REST API" -PapiDescription="Tractus-X EDC API Documentation" :mergeApiSpec --input=./resources/openapi/yaml --output=./resources/openapi/yaml/tractusx-edc-api.yaml + + # create API, will fail if exists + - name: Create API + continue-on-error: true + run: | + swaggerhub api:create ${{ env.SWAGGERHUB_USER }}/tractusx-edc/${{ env.DOWNSTREAM_VERSION }} -f ./resources/openapi/yaml/tractusx-edc-api.yaml --visibility=public --published=unpublish + + # Post the API to SwaggerHub as "unpublished", because published APIs cannot be overwritten + - name: Publish API Specs to SwaggerHub + run: | + if [[ ${{ env.DOWNSTREAM_VERSION }} != *-SNAPSHOT ]]; then + echo "no snapshot, will set the API to 'published'"; + swaggerhub api:update ${{ env.SWAGGERHUB_USER }}/tractusx-edc/${{ env.DOWNSTREAM_VERSION }} -f ./resources/openapi/yaml/tractusx-edc-api.yaml --visibility=public --published=publish + else + echo "snapshot, will set the API to 'unpublished'"; + swaggerhub api:update ${{ env.SWAGGERHUB_USER }}/tractusx-edc/${{ env.DOWNSTREAM_VERSION }} -f ./resources/openapi/yaml/tractusx-edc-api.yaml --visibility=public --published=unpublish + fi \ No newline at end of file diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index dcd78346d..a61da2b23 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -50,6 +50,7 @@ jobs: outputs: DOCKER_HUB_TOKEN: ${{ steps.secret-presence.outputs.DOCKER_HUB_TOKEN }} HAS_OSSRH: ${{ steps.secret-presence.outputs.HAS_OSSRH }} + HAS_SWAGGER: ${{ steps.secret-presence.outputs.HAS_SWAGGER }} steps: - name: Check whether secrets exist id: secret-presence @@ -58,8 +59,9 @@ jobs: [ ! -z "${{ secrets.ORG_GPG_PASSPHRASE }}" ] && [ ! -z "${{ secrets.ORG_GPG_PRIVATE_KEY }}" ] && [ ! -z "${{ secrets.ORG_OSSRH_USERNAME }}" ] && - [ ! -z "${{ secrets.ORG_OSSRH_PASSWORD }}" ] && - echo "HAS_OSSRH=true" >> $GITHUB_OUTPUT + [ ! -z "${{ secrets.ORG_OSSRH_PASSWORD }}" ] && echo "HAS_OSSRH=true" >> $GITHUB_OUTPUT + [ ! -z "${{ secrets.SWAGGERHUB_API_KEY }}" ] && + [ ! -z "${{ secrets.SWAGGERHUB_USER }}" ] && echo "HAS_SWAGGER=true" >> $GITHUB_OUTPUT exit 0 build-docker-images: @@ -126,4 +128,13 @@ jobs: cmd="closeAndReleaseSonatypeStagingRepository"; fi echo "Publishing Version $VERSION to Sonatype" - ./gradlew publishToSonatype ${cmd} --no-parallel -Pversion=$VERSION -Psigning.gnupg.executable=gpg -Psigning.gnupg.passphrase="${{ secrets.ORG_GPG_PASSPHRASE }}" \ No newline at end of file + ./gradlew publishToSonatype ${cmd} --no-parallel -Pversion=$VERSION -Psigning.gnupg.executable=gpg -Psigning.gnupg.passphrase="${{ secrets.ORG_GPG_PASSPHRASE }}" + + publish-to-swaggerhub: + name: "Publish OpenAPI spec to Swaggerhub" + permissions: + contents: read + needs: [ secret-presence ] + if: needs.secret-presence.outputs.HAS_SWAGGER + uses: ./.github/workflows/publish-swaggerhub.yaml + secrets: inherit diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-all-tests.yml index 8378e44af..2cfadff55 100644 --- a/.github/workflows/run-all-tests.yml +++ b/.github/workflows/run-all-tests.yml @@ -34,8 +34,6 @@ on: types: - published pull_request: - paths-ignore: - - 'charts/**' workflow_dispatch: concurrency: diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index bed418e1a..b6e6858da 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -64,31 +64,7 @@ jobs: fi verify-dependencies: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/setup-java - - name: Download latest Eclipse Dash - run: | - curl -L https://repo.eclipse.org/service/local/artifact/maven/redirect\?r\=dash-licenses\&g\=org.eclipse.dash\&a\=org.eclipse.dash.licenses\&v\=LATEST --output dash.jar - - name: Regenerate DEPENDENCIES - run: | - # dash returns a nonzero exit code if there are libs that need review. the "|| true" avoids that - ./gradlew allDependencies | grep -Poh "(?<=\s)[\w.-]+:[\w.-]+:[^:\s\[\]]+" | sort | uniq | java -jar dash.jar - -summary DEPENDENCIES-gen || true - - # log warning if restricted deps are found - grep -E 'restricted' DEPENDENCIES | if test $(wc -l) -gt 0; then - echo "::warning file=DEPENDENCIES,title=Restricted Dependencies found::Some dependencies are marked 'restricted' - please review them" - fi - - # log error and fail job if rejected deps are found - grep -E 'rejected' DEPENDENCIES | if test $(wc -l) -gt 0; then - echo "::error file=DEPENDENCIES,title=Rejected Dependencies found::Some dependencies are marked 'rejected', they cannot be used" - exit 1 - fi - - name: Check for differences - run: | - diff DEPENDENCIES DEPENDENCIES-gen + uses: eclipse-edc/.github/.github/workflows/dependency-check.yml@main verify-formatting: runs-on: ubuntu-latest @@ -161,6 +137,7 @@ jobs: postgres-tests: runs-on: ubuntu-latest + needs: [ verify-formatting, verify-license-headers ] services: @@ -175,17 +152,8 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/setup-java - # create non-default schema name to test usage of non-default schema - - name: setup postgres schema - run: | - sudo apt update - sudo apt install --yes postgresql-client - psql -h localhost -d postgres -U postgres -c 'CREATE SCHEMA testschema;' - env: - PGPASSWORD: password - - name: Run Postgresql E2E tests - run: ./gradlew test -DincludeTags="PostgresqlIntegrationTest" + run: ./gradlew test -DincludeTags="PostgresqlIntegrationTest" -PverboseTest=true miw-integration-tests: runs-on: ubuntu-latest @@ -195,7 +163,7 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/setup-java - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Starting MIW, Keycloak and Postgres Servers run: | cd edc-tests/miw-tests/src/test/resources/docker-environment @@ -224,4 +192,4 @@ jobs: run: | pwd ./gradlew compileJava compileTestJava - ./gradlew -p edc-tests/e2e-tests test -DincludeTags="MiwIntegrationTest" -PverboseTest=true \ No newline at end of file + ./gradlew -p edc-tests/e2e-tests test -DincludeTags="MiwIntegrationTest" -PverboseTest=true diff --git a/DEPENDENCIES b/DEPENDENCIES index eba0220c8..e331a8fd6 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -3,15 +3,14 @@ maven/mavencentral/com.apicatalog/iron-verifiable-credentials/0.8.1, Apache-2.0, 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.2, Apache-2.0, approved, #8912 -maven/mavencentral/com.azure/azure-core-http-netty/1.13.3, MIT AND Apache-2.0, approved, #7948 -maven/mavencentral/com.azure/azure-core-http-netty/1.13.4, MIT AND Apache-2.0, approved, #7948 -maven/mavencentral/com.azure/azure-core/1.39.0, MIT, approved, clearlydefined -maven/mavencentral/com.azure/azure-core/1.40.0, MIT, approved, clearlydefined -maven/mavencentral/com.azure/azure-identity/1.9.0, MIT, approved, clearlydefined -maven/mavencentral/com.azure/azure-identity/1.9.1, MIT, approved, clearlydefined +maven/mavencentral/com.azure/azure-core-http-netty/1.13.5, MIT AND Apache-2.0, approved, #7948 +maven/mavencentral/com.azure/azure-core-http-netty/1.13.6, MIT AND Apache-2.0, approved, #7948 +maven/mavencentral/com.azure/azure-core/1.41.0, MIT AND Apache-2.0, approved, #9648 +maven/mavencentral/com.azure/azure-core/1.42.0, MIT AND Apache-2.0, approved, #10089 +maven/mavencentral/com.azure/azure-identity/1.10.0, MIT AND Apache-2.0, approved, #10086 +maven/mavencentral/com.azure/azure-identity/1.9.2, MIT AND Apache-2.0, approved, #9686 maven/mavencentral/com.azure/azure-json/1.0.1, MIT AND Apache-2.0, approved, #7933 -maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.6.2, MIT, approved, #7940 -maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.6.3, MIT, approved, #7940 +maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.6.4, MIT, approved, #7940 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.3, Apache-2.0, approved, clearlydefined maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.13.5, Apache-2.0, approved, clearlydefined @@ -27,6 +26,8 @@ maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.5, Apache-2. 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.2, Apache-2.0, approved, #7934 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.14.0, Apache-2.0 AND BSD-3-Clause AND MIT AND Apache-2.0, approved, #7943 +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-xml/2.14.2, Apache-2.0, approved, #4300 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.15.2, Apache-2.0, approved, #9237 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.13.3, Apache-2.0, approved, #2566 @@ -51,20 +52,17 @@ maven/mavencentral/com.github.docker-java/docker-java-transport/3.3.0, Apache-2. maven/mavencentral/com.github.stephenc.jcip/jcip-annotations/1.0-1, Apache-2.0, approved, CQ21949 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.crypto.tink/tink/1.9.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.crypto.tink/tink/1.10.0, Apache-2.0, approved, #9845 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.5.1, Apache-2.0, approved, clearlydefined maven/mavencentral/com.google.errorprone/error_prone_annotations/2.7.1, Apache-2.0, approved, clearlydefined maven/mavencentral/com.google.guava/failureaccess/1.0.1, Apache-2.0, approved, CQ22654 -maven/mavencentral/com.google.guava/guava/29.0-android, Apache-2.0, approved, clearlydefined -maven/mavencentral/com.google.guava/guava/30.1.1-android, Apache-2.0 AND CC0-1.0 AND LicenseRef-Public-Domain, approved, CQ23244 maven/mavencentral/com.google.guava/guava/31.0.1-jre, Apache-2.0, approved, clearlydefined maven/mavencentral/com.google.guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava, Apache-2.0, approved, CQ22657 -maven/mavencentral/com.google.http-client/google-http-client/1.43.1, 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.19.6, BSD-3-Clause, approved, clearlydefined maven/mavencentral/com.microsoft.azure/msal4j-persistence-extension/1.2.0, MIT, approved, clearlydefined maven/mavencentral/com.microsoft.azure/msal4j/1.13.8, MIT, approved, clearlydefined +maven/mavencentral/com.microsoft.azure/msal4j/1.13.9, MIT, approved, clearlydefined maven/mavencentral/com.microsoft.azure/msal4j/1.4.0, MIT, approved, clearlydefined maven/mavencentral/com.nimbusds/content-type/2.2, Apache-2.0, approved, clearlydefined maven/mavencentral/com.nimbusds/lang-tag/1.7, Apache-2.0, approved, clearlydefined @@ -93,88 +91,53 @@ maven/mavencentral/dev.failsafe/failsafe/3.3.2, Apache-2.0, approved, #9268 maven/mavencentral/info.picocli/picocli/4.6.3, Apache-2.0, approved, clearlydefined maven/mavencentral/io.github.classgraph/classgraph/4.8.138, MIT, approved, CQ22530 maven/mavencentral/io.github.classgraph/classgraph/4.8.154, MIT, approved, CQ22530 -maven/mavencentral/io.grpc/grpc-context/1.27.2, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.micrometer/micrometer-commons/1.11.1, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9243 -maven/mavencentral/io.micrometer/micrometer-core/1.11.1, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9238 -maven/mavencentral/io.micrometer/micrometer-observation/1.11.1, Apache-2.0, approved, #9242 -maven/mavencentral/io.netty/netty-buffer/4.1.86.Final, Apache-2.0, approved, CQ21842 -maven/mavencentral/io.netty/netty-buffer/4.1.89.Final, Apache-2.0, approved, CQ21842 -maven/mavencentral/io.netty/netty-buffer/4.1.91.Final, Apache-2.0, approved, CQ21842 +maven/mavencentral/io.micrometer/micrometer-commons/1.11.3, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9243 +maven/mavencentral/io.micrometer/micrometer-core/1.11.3, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9238 +maven/mavencentral/io.micrometer/micrometer-observation/1.11.3, Apache-2.0, approved, #9242 +maven/mavencentral/io.netty/netty-buffer/4.1.93.Final, Apache-2.0, approved, CQ21842 maven/mavencentral/io.netty/netty-buffer/4.1.94.Final, Apache-2.0, approved, CQ21842 -maven/mavencentral/io.netty/netty-codec-dns/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-dns/4.1.91.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-http/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-http/4.1.91.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-dns/4.1.93.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http/4.1.93.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-codec-http/4.1.94.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-http2/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-http2/4.1.91.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http2/4.1.93.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-codec-http2/4.1.94.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-socks/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-socks/4.1.91.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-codec/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec/4.1.91.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-socks/4.1.94.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec/4.1.93.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-codec/4.1.94.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -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-common/4.1.89.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 -maven/mavencentral/io.netty/netty-common/4.1.91.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 +maven/mavencentral/io.netty/netty-common/4.1.93.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 maven/mavencentral/io.netty/netty-common/4.1.94.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 -maven/mavencentral/io.netty/netty-handler-proxy/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-handler-proxy/4.1.91.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-handler/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-handler/4.1.91.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-handler-proxy/4.1.93.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-handler-proxy/4.1.94.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-handler/4.1.93.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-handler/4.1.94.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-resolver-dns-classes-macos/4.1.89.Final, Apache-2.0, approved, #6367 -maven/mavencentral/io.netty/netty-resolver-dns-classes-macos/4.1.91.Final, Apache-2.0, approved, #6367 -maven/mavencentral/io.netty/netty-resolver-dns-native-macos/4.1.89.Final, Apache-2.0, approved, #7004 -maven/mavencentral/io.netty/netty-resolver-dns-native-macos/4.1.91.Final, Apache-2.0, approved, #7004 -maven/mavencentral/io.netty/netty-resolver-dns/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-resolver-dns/4.1.91.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-resolver/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-resolver/4.1.91.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver-dns-classes-macos/4.1.93.Final, Apache-2.0, approved, #6367 +maven/mavencentral/io.netty/netty-resolver-dns-native-macos/4.1.93.Final, Apache-2.0, approved, #7004 +maven/mavencentral/io.netty/netty-resolver-dns/4.1.93.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver/4.1.93.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-resolver/4.1.94.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.59.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.59.Final, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.86.Final, Apache-2.0, approved, #6366 -maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.89.Final, Apache-2.0, approved, #6366 -maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.91.Final, Apache-2.0, approved, #6366 +maven/mavencentral/io.netty/netty-tcnative-boringssl-static/2.0.61.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.61.Final, Apache-2.0, approved, clearlydefined maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.94.Final, Apache-2.0, approved, #6366 -maven/mavencentral/io.netty/netty-transport-classes-kqueue/4.1.89.Final, Apache-2.0, approved, #4107 -maven/mavencentral/io.netty/netty-transport-classes-kqueue/4.1.91.Final, Apache-2.0, approved, #4107 -maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.91.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport-native-kqueue/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport-native-kqueue/4.1.91.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-native-unix-common/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.91.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-classes-kqueue/4.1.94.Final, Apache-2.0, approved, #4107 +maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.93.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.94.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-kqueue/4.1.94.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.93.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.94.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.netty/netty-transport/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport/4.1.91.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport/4.1.93.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-transport/4.1.94.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.opencensus/opencensus-api/0.31.1, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.opencensus/opencensus-contrib-http-util/0.31.1, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.opentelemetry.instrumentation/opentelemetry-instrumentation-annotations/1.27.0, Apache-2.0, approved, #9270 -maven/mavencentral/io.opentelemetry/opentelemetry-api/1.27.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.opentelemetry/opentelemetry-context/1.27.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.projectreactor.netty/reactor-netty-core/1.0.28, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.projectreactor.netty/reactor-netty-core/1.0.31, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.28, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.31, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.projectreactor/reactor-core/3.4.27, Apache-2.0, approved, #7517 -maven/mavencentral/io.projectreactor/reactor-core/3.4.29, Apache-2.0, approved, #7517 +maven/mavencentral/io.opentelemetry.instrumentation/opentelemetry-instrumentation-annotations/1.29.0, Apache-2.0, approved, #10087 +maven/mavencentral/io.opentelemetry/opentelemetry-api/1.29.0, Apache-2.0, approved, #10088 +maven/mavencentral/io.opentelemetry/opentelemetry-context/1.29.0, Apache-2.0, approved, #10090 +maven/mavencentral/io.projectreactor.netty/reactor-netty-core/1.0.33, Apache-2.0, approved, #9687 +maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.33, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.projectreactor/reactor-core/3.4.30, Apache-2.0, approved, #7517 maven/mavencentral/io.rest-assured/json-path/5.3.1, Apache-2.0, approved, #9261 maven/mavencentral/io.rest-assured/rest-assured-common/5.3.1, Apache-2.0, approved, #9264 maven/mavencentral/io.rest-assured/rest-assured/5.3.1, Apache-2.0, approved, #9262 maven/mavencentral/io.rest-assured/xml-path/5.3.1, Apache-2.0, approved, #9267 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.2, Apache-2.0, approved, #5947 maven/mavencentral/io.swagger.core.v3/swagger-annotations/2.2.10, Apache-2.0, approved, clearlydefined maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.2, Apache-2.0, approved, #5929 @@ -182,7 +145,7 @@ maven/mavencentral/io.swagger.core.v3/swagger-core/2.2.10, Apache-2.0, approved, maven/mavencentral/io.swagger.core.v3/swagger-integration-jakarta/2.2.2, Apache-2.0, approved, clearlydefined maven/mavencentral/io.swagger.core.v3/swagger-integration/2.2.10, Apache-2.0, approved, clearlydefined maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2-jakarta/2.2.2, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2/2.2.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2/2.2.10, Apache-2.0, approved, #9814 maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.2, Apache-2.0, approved, #5919 maven/mavencentral/io.swagger.core.v3/swagger-models/2.2.10, Apache-2.0, approved, clearlydefined 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 @@ -200,7 +163,6 @@ maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/3.0.0, BSD-3-Clause, ap 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.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.12.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/1.12.21, Apache-2.0 AND BSD-3-Clause, approved, #1811 @@ -209,9 +171,9 @@ maven/mavencentral/net.java.dev.jna/jna-platform/5.13.0, Apache-2.0 OR LGPL-2.1- 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.12.1, Apache-2.0 OR LGPL-2.1-or-later, approved, #3217 maven/mavencentral/net.java.dev.jna/jna/5.13.0, Apache-2.0 AND LGPL-2.1-or-later, approved, #6709 -maven/mavencentral/net.minidev/accessors-smart/2.5.0, , restricted, clearlydefined +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.5.0, , restricted, clearlydefined +maven/mavencentral/net.minidev/json-smart/2.5.0, Apache-2.0, approved, clearlydefined maven/mavencentral/net.sf.saxon/Saxon-HE/10.6, MPL-2.0 AND W3C, approved, #7945 maven/mavencentral/org.antlr/antlr4-runtime/4.9.3, BSD-3-Clause, approved, #322 maven/mavencentral/org.apache.commons/commons-compress/1.23.0, Apache-2.0 AND BSD-3-Clause, approved, #7506 @@ -223,9 +185,7 @@ maven/mavencentral/org.apache.groovy/groovy-json/4.0.11, Apache-2.0, approved, # maven/mavencentral/org.apache.groovy/groovy-xml/4.0.11, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.groovy/groovy/4.0.11, Apache-2.0 AND BSD-3-Clause AND MIT, approved, #1742 maven/mavencentral/org.apache.httpcomponents/httpclient/4.5.13, Apache-2.0 AND LicenseRef-Public-Domain, approved, CQ23527 -maven/mavencentral/org.apache.httpcomponents/httpclient/4.5.14, Apache-2.0 AND LicenseRef-Public-Domain, approved, CQ23527 maven/mavencentral/org.apache.httpcomponents/httpcore/4.4.13, Apache-2.0, approved, CQ23528 -maven/mavencentral/org.apache.httpcomponents/httpcore/4.4.16, Apache-2.0, approved, CQ23528 maven/mavencentral/org.apache.httpcomponents/httpmime/4.5.13, Apache-2.0, approved, CQ11718 maven/mavencentral/org.apache.sshd/sshd-common/2.10.0, Apache-2.0 AND ISC, approved, #8454 maven/mavencentral/org.apache.sshd/sshd-core/2.10.0, Apache-2.0, approved, #8455 @@ -233,124 +193,123 @@ maven/mavencentral/org.apache.sshd/sshd-sftp/2.10.0, Apache-2.0, approved, #8457 maven/mavencentral/org.apiguardian/apiguardian-api/1.1.2, Apache-2.0, approved, clearlydefined maven/mavencentral/org.assertj/assertj-core/3.24.2, Apache-2.0, approved, #6161 maven/mavencentral/org.awaitility/awaitility/4.2.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.bouncycastle/bcpkix-jdk18on/1.75, MIT, approved, #9166 -maven/mavencentral/org.bouncycastle/bcprov-jdk18on/1.75, MIT AND CC0-1.0, approved, #9167 -maven/mavencentral/org.bouncycastle/bcutil-jdk18on/1.75, MIT, approved, #9170 +maven/mavencentral/org.bouncycastle/bcpkix-jdk18on/1.76, MIT, approved, #9825 +maven/mavencentral/org.bouncycastle/bcprov-jdk18on/1.76, MIT AND CC0-1.0, approved, #9827 +maven/mavencentral/org.bouncycastle/bcutil-jdk18on/1.76, MIT, approved, #9828 maven/mavencentral/org.ccil.cowan.tagsoup/tagsoup/1.2.1, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.checkerframework/checker-compat-qual/2.5.5, MIT, approved, clearlydefined maven/mavencentral/org.checkerframework/checker-qual/3.12.0, MIT, approved, clearlydefined maven/mavencentral/org.checkerframework/checker-qual/3.31.0, MIT, approved, clearlydefined maven/mavencentral/org.codehaus.woodstox/stax2-api/4.2.1, BSD-2-Clause, approved, #2670 -maven/mavencentral/org.eclipse.edc/aggregate-service-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/api-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/api-observability/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/asset-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/asset-index-sql/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/asset-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/auth-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/auth-tokenbased/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/autodoc-processor/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/aws-s3-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/boot/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/callback-event-dispatcher/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/callback-http-dispatcher/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/catalog-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/catalog-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/catalog-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/configuration-filesystem/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/connector-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-agreement-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-definition-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-definition-store-sql/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-negotiation-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-negotiation-store-sql/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-api-configuration/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-aggregate-services/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-api-client-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/core-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-aws-s3/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-client/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-framework/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-http-oauth2-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-http-oauth2/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-http-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-http/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-selector-client/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-selector-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-selector-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-util/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-api-configuration/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-catalog-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-catalog-http-dispatcher/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-catalog-transform/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-catalog/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-http-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-http-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-negotiation-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-negotiation-http-dispatcher/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-negotiation-transform/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-negotiation/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-transfer-process-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-transfer-process-http-dispatcher/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-transfer-process-transform/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-transfer-process/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp-transform/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/dsp/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/http-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/http/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jersey-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jersey-micrometer/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jersey-providers/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jetty-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jetty-micrometer/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/json-ld-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/json-ld/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/junit/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jwt-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jwt-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/management-api-configuration/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/management-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/micrometer-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/oauth2-client/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/oauth2-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/oauth2-daps/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/oauth2-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-definition-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-definition-store-sql/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-engine-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-engine/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-evaluator/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-model/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/sql-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/sql-lease/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/sql-pool-apache-commons/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/state-machine/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-datasource-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-local/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-data-plane-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-data-plane/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-process-api/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-process-store-sql/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-pull-http-dynamic-receiver/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transform-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/util/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/validator-core/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/validator-spi/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/vault-azure/0.1.3, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/web-spi/0.1.3, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/aggregate-service-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/api-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/api-observability/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/asset-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/asset-index-sql/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/asset-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/auth-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/auth-tokenbased/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/autodoc-processor/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/aws-s3-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/boot/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/callback-event-dispatcher/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/callback-http-dispatcher/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/catalog-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/catalog-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/catalog-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/configuration-filesystem/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/connector-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-agreement-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-definition-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-definition-store-sql/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-negotiation-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-negotiation-store-sql/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-api-configuration/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-aggregate-services/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-api-client-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/core-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-aws-s3/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-client/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-framework/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http-oauth2-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http-oauth2/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-selector-client/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-selector-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-selector-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-util/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-api-configuration/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-catalog-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-catalog-http-dispatcher/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-catalog-transform/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-catalog/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-http-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-http-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-negotiation-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-negotiation-http-dispatcher/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-negotiation-transform/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-negotiation/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-transfer-process-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-transfer-process-http-dispatcher/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-transfer-process-transform/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp-transfer-process/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/dsp/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/http-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/http/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/iam-mock/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jersey-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jersey-micrometer/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jersey-providers/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jetty-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jetty-micrometer/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/json-ld-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/json-ld/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/junit/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jwt-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jwt-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/management-api-configuration/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/management-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/micrometer-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/oauth2-client/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/oauth2-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-definition-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-definition-store-sql/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-engine-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-engine/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-evaluator/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-model/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/sql-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/sql-lease/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/sql-pool-apache-commons/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/state-machine/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-datasource-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-local/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-data-plane-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-data-plane/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-process-api/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-process-store-sql/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-pull-http-dynamic-receiver/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transform-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transform-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/util/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/validator-core/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/validator-spi/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/vault-azure/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/vault-hashicorp/0.2.1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/web-spi/0.2.1, 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.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty @@ -373,21 +332,21 @@ maven/mavencentral/org.eclipse.jetty/jetty-servlet/11.0.15, EPL-2.0 OR Apache-2. maven/mavencentral/org.eclipse.jetty/jetty-util/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-webapp/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-xml/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.flywaydb/flyway-core/9.20.0, Apache-2.0, approved, #9244 +maven/mavencentral/org.flywaydb/flyway-core/9.21.1, Apache-2.0, approved, #9846 maven/mavencentral/org.glassfish.hk2.external/aopalliance-repackaged/3.0.4, 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.4, 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.4, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish maven/mavencentral/org.glassfish.hk2/hk2-utils/3.0.4, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish maven/mavencentral/org.glassfish.hk2/osgi-resource-locator/1.0.3, CDDL-1.0, approved, CQ10889 -maven/mavencentral/org.glassfish.jersey.containers/jersey-container-servlet-core/3.1.2, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.containers/jersey-container-servlet/3.1.2, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.core/jersey-client/3.1.2, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.core/jersey-common/3.1.2, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.core/jersey-server/3.1.2, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.ext/jersey-entity-filtering/3.1.2, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.inject/jersey-hk2/3.1.2, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.media/jersey-media-json-jackson/3.1.2, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.media/jersey-media-multipart/3.1.2, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.containers/jersey-container-servlet-core/3.1.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.containers/jersey-container-servlet/3.1.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.core/jersey-client/3.1.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.core/jersey-common/3.1.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.core/jersey-server/3.1.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.ext/jersey-entity-filtering/3.1.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.inject/jersey-hk2/3.1.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.media/jersey-media-json-jackson/3.1.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.media/jersey-media-multipart/3.1.3, 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/2.1, BSD-3-Clause, approved, clearlydefined @@ -399,7 +358,6 @@ maven/mavencentral/org.jacoco/org.jacoco.core/0.8.8, EPL-2.0, approved, CQ23283 maven/mavencentral/org.jacoco/org.jacoco.report/0.8.8, EPL-2.0 AND Apache-2.0, approved, CQ23284 maven/mavencentral/org.javassist/javassist/3.25.0-GA, MPL-1.1 OR LGPL-2.1-or-later OR Apache-2.0, approved, CQ19885 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.0-GA, Apache-2.0 AND LGPL-2.1-or-later AND MPL-1.1, approved, #6023 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.6.20, Apache-2.0, approved, clearlydefined maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.7.10, Apache-2.0, approved, clearlydefined @@ -414,20 +372,20 @@ maven/mavencentral/org.jetbrains/annotations/13.0, Apache-2.0, approved, clearly maven/mavencentral/org.jetbrains/annotations/17.0.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.jetbrains/annotations/24.0.1, Apache-2.0, approved, #7417 maven/mavencentral/org.junit-pioneer/junit-pioneer/2.0.1, EPL-2.0, approved, clearlydefined -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.9.3, EPL-2.0, approved, #3125 -maven/mavencentral/org.junit.jupiter/junit-jupiter-params/5.9.3, EPL-2.0, approved, #3134 -maven/mavencentral/org.junit.platform/junit-platform-commons/1.9.3, EPL-2.0, approved, #3130 -maven/mavencentral/org.junit.platform/junit-platform-engine/1.9.3, EPL-2.0, approved, #3128 -maven/mavencentral/org.junit.platform/junit-platform-launcher/1.9.3, EPL-2.0, approved, #3132 +maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.10.0, EPL-2.0, approved, #9714 +maven/mavencentral/org.junit.jupiter/junit-jupiter-engine/5.10.0, EPL-2.0, approved, #9711 +maven/mavencentral/org.junit.jupiter/junit-jupiter-params/5.10.0, EPL-2.0, approved, #9708 +maven/mavencentral/org.junit.platform/junit-platform-commons/1.10.0, EPL-2.0, approved, #9715 +maven/mavencentral/org.junit.platform/junit-platform-engine/1.10.0, EPL-2.0, approved, #9709 +maven/mavencentral/org.junit.platform/junit-platform-launcher/1.10.0, EPL-2.0, approved, #9704 +maven/mavencentral/org.junit/junit-bom/5.10.0, EPL-2.0, approved, #9844 maven/mavencentral/org.junit/junit-bom/5.9.2, EPL-2.0, approved, #4711 -maven/mavencentral/org.junit/junit-bom/5.9.3, 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.mockito/mockito-core/5.2.0, MIT AND (Apache-2.0 AND MIT) AND Apache-2.0, approved, #7401 maven/mavencentral/org.mockito/mockito-inline/5.2.0, MIT, approved, clearlydefined maven/mavencentral/org.objenesis/objenesis/3.3, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.opentest4j/opentest4j/1.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.opentest4j/opentest4j/1.3.0, Apache-2.0, approved, #9713 maven/mavencentral/org.ow2.asm/asm-analysis/9.2, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.ow2.asm/asm-commons/9.2, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.ow2.asm/asm-commons/9.5, BSD-3-Clause, approved, #7553 @@ -450,51 +408,54 @@ 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.5, MIT, approved, #5915 maven/mavencentral/org.slf4j/slf4j-api/2.0.7, MIT, approved, #5915 +maven/mavencentral/org.testcontainers/database-commons/1.18.3, MIT, approved, clearlydefined +maven/mavencentral/org.testcontainers/jdbc/1.18.3, MIT, approved, clearlydefined maven/mavencentral/org.testcontainers/junit-jupiter/1.18.3, MIT, approved, #7941 +maven/mavencentral/org.testcontainers/postgresql/1.18.3, MIT, approved, #9332 maven/mavencentral/org.testcontainers/testcontainers/1.18.3, MIT, approved, #7938 -maven/mavencentral/org.testcontainers/vault/1.18.3, MIT, approved, #7927 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/software.amazon.awssdk/annotations/2.20.91, Apache-2.0, approved, #8598 -maven/mavencentral/software.amazon.awssdk/annotations/2.20.98, Apache-2.0, approved, #8598 -maven/mavencentral/software.amazon.awssdk/apache-client/2.20.91, Apache-2.0, approved, #8609 -maven/mavencentral/software.amazon.awssdk/apache-client/2.20.98, Apache-2.0, approved, #8609 -maven/mavencentral/software.amazon.awssdk/arns/2.20.91, Apache-2.0, approved, #8616 -maven/mavencentral/software.amazon.awssdk/arns/2.20.98, Apache-2.0, approved, #8616 -maven/mavencentral/software.amazon.awssdk/auth/2.20.91, Apache-2.0, approved, #8602 -maven/mavencentral/software.amazon.awssdk/auth/2.20.98, Apache-2.0, approved, #8602 -maven/mavencentral/software.amazon.awssdk/aws-core/2.20.91, Apache-2.0, approved, #8612 -maven/mavencentral/software.amazon.awssdk/aws-core/2.20.98, Apache-2.0, approved, #8612 -maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.20.91, Apache-2.0, approved, #8629 -maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.20.98, Apache-2.0, approved, #8629 -maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.20.91, Apache-2.0, approved, #8624 -maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.20.98, Apache-2.0, approved, #8624 -maven/mavencentral/software.amazon.awssdk/crt-core/2.20.91, Apache-2.0, approved, #8627 -maven/mavencentral/software.amazon.awssdk/crt-core/2.20.98, Apache-2.0, approved, #8627 -maven/mavencentral/software.amazon.awssdk/endpoints-spi/2.20.91, Apache-2.0, approved, #8604 -maven/mavencentral/software.amazon.awssdk/endpoints-spi/2.20.98, Apache-2.0, approved, #8604 -maven/mavencentral/software.amazon.awssdk/http-client-spi/2.20.91, Apache-2.0, approved, #8608 -maven/mavencentral/software.amazon.awssdk/http-client-spi/2.20.98, Apache-2.0, approved, #8608 -maven/mavencentral/software.amazon.awssdk/iam/2.20.91, Apache-2.0, approved, #9271 -maven/mavencentral/software.amazon.awssdk/json-utils/2.20.91, Apache-2.0, approved, #8614 -maven/mavencentral/software.amazon.awssdk/json-utils/2.20.98, Apache-2.0, approved, #8614 -maven/mavencentral/software.amazon.awssdk/metrics-spi/2.20.91, Apache-2.0, approved, #8636 -maven/mavencentral/software.amazon.awssdk/metrics-spi/2.20.98, Apache-2.0, approved, #8636 -maven/mavencentral/software.amazon.awssdk/netty-nio-client/2.20.91, Apache-2.0, approved, #8613 -maven/mavencentral/software.amazon.awssdk/netty-nio-client/2.20.98, Apache-2.0, approved, #8613 -maven/mavencentral/software.amazon.awssdk/profiles/2.20.91, Apache-2.0, approved, #8600 -maven/mavencentral/software.amazon.awssdk/profiles/2.20.98, Apache-2.0, approved, #8600 -maven/mavencentral/software.amazon.awssdk/protocol-core/2.20.91, Apache-2.0, approved, #8635 -maven/mavencentral/software.amazon.awssdk/protocol-core/2.20.98, Apache-2.0, approved, #8635 -maven/mavencentral/software.amazon.awssdk/regions/2.20.91, Apache-2.0, approved, #8632 -maven/mavencentral/software.amazon.awssdk/regions/2.20.98, Apache-2.0, approved, #8632 -maven/mavencentral/software.amazon.awssdk/s3/2.20.91, Apache-2.0, approved, #8623 -maven/mavencentral/software.amazon.awssdk/s3/2.20.98, Apache-2.0, approved, #8623 -maven/mavencentral/software.amazon.awssdk/sdk-core/2.20.91, Apache-2.0, approved, #8611 -maven/mavencentral/software.amazon.awssdk/sdk-core/2.20.98, Apache-2.0, approved, #8611 -maven/mavencentral/software.amazon.awssdk/sts/2.20.91, Apache-2.0, approved, #9269 -maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.20.91, Apache-2.0, approved, #8622 -maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.20.98, Apache-2.0, approved, #8622 -maven/mavencentral/software.amazon.awssdk/utils/2.20.91, Apache-2.0, approved, #8625 -maven/mavencentral/software.amazon.awssdk/utils/2.20.98, Apache-2.0, approved, #8625 +maven/mavencentral/org.yaml/snakeyaml/2.1, Apache-2.0, approved, #9847 +maven/mavencentral/software.amazon.awssdk/annotations/2.20.123, Apache-2.0, approved, #8598 +maven/mavencentral/software.amazon.awssdk/annotations/2.20.130, Apache-2.0, approved, #8598 +maven/mavencentral/software.amazon.awssdk/apache-client/2.20.123, Apache-2.0, approved, #8609 +maven/mavencentral/software.amazon.awssdk/apache-client/2.20.130, Apache-2.0, approved, #8609 +maven/mavencentral/software.amazon.awssdk/arns/2.20.123, Apache-2.0, approved, #8616 +maven/mavencentral/software.amazon.awssdk/arns/2.20.130, Apache-2.0, approved, #8616 +maven/mavencentral/software.amazon.awssdk/auth/2.20.123, Apache-2.0, approved, #8602 +maven/mavencentral/software.amazon.awssdk/auth/2.20.130, Apache-2.0, approved, #8602 +maven/mavencentral/software.amazon.awssdk/aws-core/2.20.123, Apache-2.0, approved, #8612 +maven/mavencentral/software.amazon.awssdk/aws-core/2.20.130, Apache-2.0, approved, #8612 +maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.20.123, Apache-2.0, approved, #8629 +maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.20.130, Apache-2.0, approved, #8629 +maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.20.123, Apache-2.0, approved, #8624 +maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.20.130, Apache-2.0, approved, #8624 +maven/mavencentral/software.amazon.awssdk/crt-core/2.20.123, Apache-2.0, approved, #8627 +maven/mavencentral/software.amazon.awssdk/crt-core/2.20.130, Apache-2.0, approved, #8627 +maven/mavencentral/software.amazon.awssdk/endpoints-spi/2.20.123, Apache-2.0, approved, #8604 +maven/mavencentral/software.amazon.awssdk/endpoints-spi/2.20.130, Apache-2.0, approved, #8604 +maven/mavencentral/software.amazon.awssdk/http-client-spi/2.20.123, Apache-2.0, approved, #8608 +maven/mavencentral/software.amazon.awssdk/http-client-spi/2.20.130, Apache-2.0, approved, #8608 +maven/mavencentral/software.amazon.awssdk/iam/2.20.123, Apache-2.0, approved, #9271 +maven/mavencentral/software.amazon.awssdk/json-utils/2.20.123, Apache-2.0, approved, #8614 +maven/mavencentral/software.amazon.awssdk/json-utils/2.20.130, Apache-2.0, approved, #8614 +maven/mavencentral/software.amazon.awssdk/metrics-spi/2.20.123, Apache-2.0, approved, #8636 +maven/mavencentral/software.amazon.awssdk/metrics-spi/2.20.130, Apache-2.0, approved, #8636 +maven/mavencentral/software.amazon.awssdk/netty-nio-client/2.20.123, Apache-2.0, approved, #8613 +maven/mavencentral/software.amazon.awssdk/netty-nio-client/2.20.130, Apache-2.0, approved, #8613 +maven/mavencentral/software.amazon.awssdk/profiles/2.20.123, Apache-2.0, approved, #8600 +maven/mavencentral/software.amazon.awssdk/profiles/2.20.130, Apache-2.0, approved, #8600 +maven/mavencentral/software.amazon.awssdk/protocol-core/2.20.123, Apache-2.0, approved, #8635 +maven/mavencentral/software.amazon.awssdk/protocol-core/2.20.130, Apache-2.0, approved, #8635 +maven/mavencentral/software.amazon.awssdk/regions/2.20.123, Apache-2.0, approved, #8632 +maven/mavencentral/software.amazon.awssdk/regions/2.20.130, Apache-2.0, approved, #8632 +maven/mavencentral/software.amazon.awssdk/s3/2.20.123, Apache-2.0, approved, #8623 +maven/mavencentral/software.amazon.awssdk/s3/2.20.130, Apache-2.0, approved, #8623 +maven/mavencentral/software.amazon.awssdk/sdk-core/2.20.123, Apache-2.0, approved, #8611 +maven/mavencentral/software.amazon.awssdk/sdk-core/2.20.130, Apache-2.0, approved, #8611 +maven/mavencentral/software.amazon.awssdk/sts/2.20.123, Apache-2.0, approved, #9269 +maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.20.123, Apache-2.0, approved, #8622 +maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.20.130, Apache-2.0, approved, #8622 +maven/mavencentral/software.amazon.awssdk/utils/2.20.123, Apache-2.0, approved, #8625 +maven/mavencentral/software.amazon.awssdk/utils/2.20.130, Apache-2.0, approved, #8625 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 83f1dd213..076c089ba 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,9 +25,9 @@ plugins { `java-library` `maven-publish` `jacoco-report-aggregation` - id("com.diffplug.spotless") version "6.19.0" + id("com.diffplug.spotless") version "6.20.0" id("com.github.johnrengelman.shadow") version "8.1.1" - id("com.bmuschko.docker-remote-api") version "9.3.1" + id("com.bmuschko.docker-remote-api") version "9.3.2" id("io.github.gradle-nexus.publish-plugin") version "1.3.0" } @@ -65,10 +65,10 @@ allprojects { implementation("org.slf4j:slf4j-api:2.0.7") // this is used to counter version conflicts between the JUnit version pulled in by the plugin, // and the one expected by IntelliJ - testImplementation(platform("org.junit:junit-bom:5.9.3")) + testImplementation(platform("org.junit:junit-bom:5.10.0")) constraints { - implementation("org.yaml:snakeyaml:2.0") { + implementation("org.yaml:snakeyaml:2.1") { because("version 1.33 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-1471.") } implementation("net.minidev:json-smart:2.5.0") { @@ -81,6 +81,10 @@ allprojects { configure { processorVersion.set(annotationProcessorVersion) outputDirectory.set(project.buildDir) + // uncomment the following lines to enable the Autodoc-2-Markdown converter + // only available with EDC 0.2.1 SNAPSHOT + // additionalInputDirectory.set(downloadDir.asFile) + // downloadDirectory.set(downloadDir.asFile) } configure { @@ -149,24 +153,44 @@ subprojects { file("${project.projectDir}/src/main/docker/Dockerfile").exists() ) { - //actually apply the plugin to the (sub-)project + val agentFile = project.buildDir.resolve("opentelemetry-javaagent.jar") + // create task to download the opentelemetry agent + val openTelemetryAgentUrl = "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.27.0/opentelemetry-javaagent.jar" + val downloadOtel = tasks.create("downloadOtel") { + // only execute task if the opentelemetry agent does not exist. invoke the "clean" task to force + onlyIf { + !agentFile.exists() + } + // this task could be the first in the graph, so "build/" may not yet exist. Let's be defensive + doFirst { + project.buildDir.mkdirs() + } + // download the jar file + doLast { + val download = { url: String, destFile: File -> ant.invokeMethod("get", mapOf("src" to url, "dest" to destFile)) } + logger.lifecycle("Downloading OpenTelemetry Agent") + download(openTelemetryAgentUrl, agentFile) + } + } + //actually apply the plugin to the (sub-)project apply(plugin = "com.bmuschko.docker-remote-api") - // configure the "dockerize" task - val dockerTask = tasks.create("dockerize", DockerBuildImage::class) { - dockerFile.set(file("${project.projectDir}/src/main/docker/Dockerfile")) + val dockerTask: DockerBuildImage = tasks.create("dockerize", DockerBuildImage::class) { + val dockerContextDir = project.projectDir + dockerFile.set(file("$dockerContextDir/src/main/docker/Dockerfile")) images.add("${project.name}:${project.version}") images.add("${project.name}:latest") // specify platform with the -Dplatform flag: if (System.getProperty("platform") != null) platform.set(System.getProperty("platform")) buildArgs.put("JAR", "build/libs/${project.name}.jar") - inputDir.set(file(project.projectDir)) + buildArgs.put("OTEL_JAR", agentFile.relativeTo(dockerContextDir).path) + inputDir.set(file(dockerContextDir)) } - - // make sure "shadowJar" always runs before "dockerize" - dockerTask.dependsOn(tasks.findByName(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME)) + // make sure always runs after "dockerize" and after "copyOtel" + dockerTask.dependsOn(tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME)) + .dependsOn(downloadOtel) } } } diff --git a/charts/tractusx-connector-azure-vault/Chart.yaml b/charts/tractusx-connector-azure-vault/Chart.yaml index ad09921a6..8383cfb89 100644 --- a/charts/tractusx-connector-azure-vault/Chart.yaml +++ b/charts/tractusx-connector-azure-vault/Chart.yaml @@ -40,12 +40,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.5.0 +version: 0.5.1 # 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.5.0" +appVersion: "0.5.1" 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 diff --git a/charts/tractusx-connector-azure-vault/LICENSE b/charts/tractusx-connector-azure-vault/LICENSE new file mode 100644 index 000000000..c815b0d05 --- /dev/null +++ b/charts/tractusx-connector-azure-vault/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Catena-X + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/tractusx-connector-azure-vault/README.md b/charts/tractusx-connector-azure-vault/README.md index b0d44012f..1b67b817c 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.5.0](https://img.shields.io/badge/Version-0.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.0](https://img.shields.io/badge/AppVersion-0.5.0-informational?style=flat-square) +![Version: 0.5.1](https://img.shields.io/badge/Version-0.5.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.1](https://img.shields.io/badge/AppVersion-0.5.1-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.5.0 \ +helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0.5.1 \ -f /tractusx-connector-azure-vault-test.yaml \ --set vault.azure.name=$AZURE_VAULT_NAME \ --set vault.azure.client=$AZURE_CLIENT_ID \ @@ -92,7 +92,7 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | controlplane.endpoints.metrics | object | `{"path":"/metrics","port":9090}` | metrics api, used for application metrics, must not be internet facing | | controlplane.endpoints.metrics.path | string | `"/metrics"` | path for incoming api calls | | controlplane.endpoints.metrics.port | int | `9090` | port for incoming api calls | -| controlplane.endpoints.protocol | object | `{"path":"/api/v1/dsp","port":8084}` | ids api, used for inter connector communication and must be internet facing | +| controlplane.endpoints.protocol | object | `{"path":"/api/v1/dsp","port":8084}` | dsp api, used for inter connector communication and must be internet facing | | controlplane.endpoints.protocol.path | string | `"/api/v1/dsp"` | path for incoming api calls | | controlplane.endpoints.protocol.port | int | `8084` | port for incoming api calls | | controlplane.env | object | `{}` | | @@ -123,12 +123,6 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | controlplane.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | | controlplane.ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | | controlplane.initContainers | list | `[]` | | -| controlplane.internationalDataSpaces.catalogId | string | `"TXDC-Catalog"` | | -| controlplane.internationalDataSpaces.curator | string | `""` | | -| controlplane.internationalDataSpaces.description | string | `"Tractus-X Eclipse IDS Data Space Connector"` | | -| controlplane.internationalDataSpaces.id | string | `"TXDC"` | | -| controlplane.internationalDataSpaces.maintainer | string | `""` | | -| controlplane.internationalDataSpaces.title | string | `""` | | | controlplane.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | | controlplane.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | | controlplane.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | @@ -161,16 +155,17 @@ 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 | `""` | | -| controlplane.ssi.miw.url | string | `""` | | -| controlplane.ssi.oauth.client.id | string | `""` | | -| controlplane.ssi.oauth.client.secretAlias | string | `"client-secret"` | | -| controlplane.ssi.oauth.tokenurl | string | `""` | | +| 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.ids | string | `""` | Explicitly declared url for reaching the ids api (e.g. if ingresses not used) | +| controlplane.url.protocol | string | `""` | Explicitly declared url for reaching the dsp api (e.g. if ingresses not used) | | controlplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | | controlplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | -| customLabels | object | `{}` | | +| customCaCerts | object | `{}` | Add custom ca certificates to the truststore | +| customLabels | object | `{}` | To add some custom labels | | dataplane.affinity | object | `{}` | | | dataplane.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/) | | dataplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | @@ -251,7 +246,7 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | 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 | `""` | | -| participant.id | string | `""` | | +| participant.id | string | `""` | BPN Number | | postgresql.auth.database | string | `"edc"` | | | postgresql.auth.password | string | `"password"` | | | postgresql.auth.username | string | `"user"` | | diff --git a/charts/tractusx-connector-azure-vault/templates/_helpers.tpl b/charts/tractusx-connector-azure-vault/templates/_helpers.tpl index 701e6fc75..c579ca6d6 100644 --- a/charts/tractusx-connector-azure-vault/templates/_helpers.tpl +++ b/charts/tractusx-connector-azure-vault/templates/_helpers.tpl @@ -108,12 +108,12 @@ Create the name of the service account to use {{- end }} {{/* -Control IDS URL +Control DSP URL */}} {{- define "txdc.controlplane.url.protocol" -}} -{{- if .Values.controlplane.url.protocol }}{{/* if ids api url has been specified explicitly */}} +{{- if .Values.controlplane.url.protocol }}{{/* if dsp api url has been specified explicitly */}} {{- .Values.controlplane.url.protocol }} -{{- else }}{{/* else when ids api url has not been specified explicitly */}} +{{- else }}{{/* else when dsp api url has not been specified explicitly */}} {{- with (index .Values.controlplane.ingresses 0) }} {{- if .enabled }}{{/* if ingress enabled */}} {{- if .tls.enabled }}{{/* if TLS enabled */}} diff --git a/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml b/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml index e35926a18..e5426d115 100644 --- a/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml @@ -53,8 +53,40 @@ spec: serviceAccountName: {{ include "txdc.serviceAccountName" . }} securityContext: {{- toYaml .Values.controlplane.podSecurityContext | nindent 8 }} + {{- if or .Values.controlplane.initContainers .Values.customCaCerts }} initContainers: + {{- if .Values.controlplane.initContainers }} {{- toYaml .Values.controlplane.initContainers | nindent 8 }} + {{- end }} + {{- if .Values.customCaCerts }} + - name: custom-cacerts + # either use the specified image, or use the default one + {{- if .Values.controlplane.image.repository }} + image: "{{ .Values.controlplane.image.repository }}:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + {{- else }} + image: "tractusx/edc-controlplane-postgresql-azure-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + {{- end }} + imagePullPolicy: {{ .Values.controlplane.image.pullPolicy }} + command: + - /bin/sh + - -c + - | + cp /opt/java/openjdk/lib/security/cacerts /workdir/ + find /cacerts -type f \( -iname \*.crt -o -iname \*.pem \) -exec echo "{}" \; | while read PEM_FILE_PATH; do + PEM_FILE=${PEM_FILE_PATH##*/} + ALIAS=${PEM_FILE%.*} + echo "adding ${PEM_FILE} with alias ${ALIAS} to cacerts ..." + keytool -import -noprompt -trustcacerts -alias ${ALIAS} -file ${PEM_FILE_PATH} -keystore /workdir/cacerts -storepass changeit + done + securityContext: + {{- toYaml .Values.controlplane.securityContext | nindent 12 }} + volumeMounts: + - name: custom-cacertificates + mountPath: /cacerts + - name: custom-cacerts + mountPath: /workdir + {{- end }} + {{- end }} containers: - name: {{ .Chart.Name }} securityContext: @@ -135,7 +167,7 @@ spec: # API # ####### - name: "EDC_API_AUTH_KEY" - value: {{ .Values.controlplane.endpoints.management.authKey | required ".Values.controlplane.endpoints.mangement.authKey is required" | quote }} + value: {{ .Values.controlplane.endpoints.management.authKey | required ".Values.controlplane.endpoints.management.authKey is required" | quote }} - name: "WEB_HTTP_DEFAULT_PORT" value: {{ .Values.controlplane.endpoints.default.port | quote }} - name: "WEB_HTTP_DEFAULT_PATH" @@ -228,6 +260,16 @@ spec: - name: "EDC_DATASOURCE_EDR_URL" value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/bpn-validation/business-partner-store-sql + - name: "EDC_DATASOURCE_BPN_NAME" + value: "bpn" + - name: "EDC_DATASOURCE_BPN_USER" + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} + - name: "EDC_DATASOURCE_BPN_PASSWORD" + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} + - name: "EDC_DATASOURCE_BPN_URL" + value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + ################ ## DATA PLANE ## ################ @@ -337,6 +379,11 @@ spec: - name: "configuration" mountPath: "/app/logging.properties" subPath: "logging.properties" + {{- if .Values.customCaCerts }} + - name: custom-cacerts + mountPath: /opt/java/openjdk/lib/security/cacerts + subPath: cacerts + {{- end }} - name: "tmp" mountPath: "/tmp" volumes: @@ -348,6 +395,15 @@ spec: path: "opentelemetry.properties" - key: "logging.properties" path: "logging.properties" + {{- if .Values.customCaCerts }} + - name: custom-cacertificates + configMap: + name: {{ include "txdc.fullname" . }}-custom-cacerts + defaultMode: 0400 + - name: custom-cacerts + emptyDir: + sizeLimit: 1Mi + {{- end }} - name: "tmp" emptyDir: { } {{- with .Values.controlplane.nodeSelector }} diff --git a/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml b/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml index 66f90bbde..e5490a90e 100644 --- a/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml @@ -53,8 +53,40 @@ spec: serviceAccountName: {{ include "txdc.serviceAccountName" . }} securityContext: {{- toYaml .Values.dataplane.podSecurityContext | nindent 8 }} + {{- if or .Values.dataplane.initContainers .Values.customCaCerts }} initContainers: + {{- if .Values.dataplane.initContainers }} {{- toYaml .Values.dataplane.initContainers | nindent 8 }} + {{- end }} + {{- if .Values.customCaCerts }} + - name: custom-cacerts + # either use the specified image, or use the default one + {{- if .Values.dataplane.image.repository }} + image: "{{ .Values.dataplane.image.repository }}:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + {{- else }} + image: "tractusx/edc-dataplane-azure-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + {{- end }} + imagePullPolicy: {{ .Values.dataplane.image.pullPolicy }} + command: + - /bin/sh + - -c + - | + cp /opt/java/openjdk/lib/security/cacerts /workdir/ + find /cacerts -type f \( -iname \*.crt -o -iname \*.pem \) -exec echo "{}" \; | while read PEM_FILE_PATH; do + PEM_FILE=${PEM_FILE_PATH##*/} + ALIAS=${PEM_FILE%.*} + echo "adding ${PEM_FILE} with alias ${ALIAS} to cacerts ..." + keytool -import -noprompt -trustcacerts -alias ${ALIAS} -file ${PEM_FILE_PATH} -keystore /workdir/cacerts -storepass changeit + done + securityContext: + {{- toYaml .Values.dataplane.securityContext | nindent 12 }} + volumeMounts: + - name: custom-cacertificates + mountPath: /cacerts + - name: custom-cacerts + mountPath: /workdir + {{- end }} + {{- end }} containers: - name: {{ .Chart.Name }} securityContext: @@ -107,6 +139,12 @@ spec: {{- end }} {{- end }} + ######################## + ## ID CONFIGURATION ## + ######################## + - name: EDC_PARTICIPANT_ID + value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }} + ####### # API # ####### @@ -208,6 +246,11 @@ spec: - name: "configuration" mountPath: "/app/logging.properties" subPath: "logging.properties" + {{- if .Values.customCaCerts }} + - name: custom-cacerts + mountPath: /opt/java/openjdk/lib/security/cacerts + subPath: cacerts + {{- end }} - name: "tmp" mountPath: "/tmp" volumes: @@ -219,6 +262,15 @@ spec: path: "opentelemetry.properties" - key: "logging.properties" path: "logging.properties" + {{- if .Values.customCaCerts }} + - name: custom-cacertificates + configMap: + name: {{ include "txdc.fullname" . }}-custom-cacerts + defaultMode: 0400 + - name: custom-cacerts + emptyDir: + sizeLimit: 1Mi + {{- end }} - name: "tmp" emptyDir: { } {{- with .Values.dataplane.nodeSelector }} diff --git a/charts/tractusx-connector-azure-vault/templates/tests/test-controlplane.yaml b/charts/tractusx-connector-azure-vault/templates/tests/test-controlplane.yaml new file mode 100644 index 000000000..33b188764 --- /dev/null +++ b/charts/tractusx-connector-azure-vault/templates/tests/test-controlplane.yaml @@ -0,0 +1,56 @@ +# + # 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 + # + +--- +apiVersion: v1 +kind: Pod +metadata: + name: "{{include "txdc.fullname" .}}-test-controlplane" + labels: + {{- include "txdc.controlplane.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": {{ .Values.tests.hookDeletePolicy }} +spec: + containers: + {{/* Poke the pod's management API */}} + - name: readiness + image: curlimages/curl + command: [ 'curl', '--fail' ] + args: [ '{{- printf "http://%s-controlplane:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.controlplane.endpoints.default.port $.Values.controlplane.endpoints.default.path -}}' ] + + {{/* Try adding a BPN Group to the store via the management API */}} + - name: mgmt-api-bpn-store + image: curlimages/curl + command: [ 'curl', '-X', 'POST', '--fail','-H','Content-Type: application/json', '-H', '{{- printf "x-api-key: %s" $.Values.controlplane.endpoints.management.authKey }}', '-d', '{ + "@context": { + "tx": "https://w3id.org/tractusx/v0.0.1/ns/" + }, + "@id": "tx:BPN000001234", + "tx:groups": ["group1", "group2", "group3"] + }' ] + args: [ '{{- printf "http://%s-controlplane:%v%s/business-partner-groups" (include "txdc.fullname" $ ) $.Values.controlplane.endpoints.management.port $.Values.controlplane.endpoints.management.path -}}' ] + restartPolicy: Never + securityContext: + fsGroup: 101 # curl_group + runAsGroup: 101 # curl_group + runAsNonRoot: true + runAsUser: 100 # curl_user + seccompProfile: + type: RuntimeDefault diff --git a/charts/tractusx-connector-azure-vault/templates/tests/test-dataplane-readiness.yaml b/charts/tractusx-connector-azure-vault/templates/tests/test-dataplane-readiness.yaml index bf8c226ca..fa58c7da7 100644 --- a/charts/tractusx-connector-azure-vault/templates/tests/test-dataplane-readiness.yaml +++ b/charts/tractusx-connector-azure-vault/templates/tests/test-dataplane-readiness.yaml @@ -31,6 +31,13 @@ spec: containers: - name: wget image: curlimages/curl - command: [ 'curl' ] + command: [ 'curl', '--fail' ] args: [ '{{- printf "http://%s-dataplane:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.dataplane.endpoints.default.port $.Values.dataplane.endpoints.default.path -}}' ] restartPolicy: Never + securityContext: + fsGroup: 101 # curl_group + runAsGroup: 101 # curl_group + runAsNonRoot: true + runAsUser: 100 # curl_user + seccompProfile: + type: RuntimeDefault diff --git a/charts/tractusx-connector-azure-vault/values.yaml b/charts/tractusx-connector-azure-vault/values.yaml index ea64ae4b8..0c157c2b1 100644 --- a/charts/tractusx-connector-azure-vault/values.yaml +++ b/charts/tractusx-connector-azure-vault/values.yaml @@ -33,9 +33,11 @@ nameOverride: "" # -- 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) imagePullSecrets: [] +# -- To add some custom labels customLabels: {} participant: + # -- BPN Number id: "" controlplane: @@ -51,13 +53,6 @@ controlplane: enabled: false port: 1044 suspendOnStart: false - internationalDataSpaces: - id: TXDC - description: Tractus-X Eclipse IDS Data Space Connector - title: "" - maintainer: "" - curator: "" - catalogId: TXDC-Catalog livenessProbe: # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) enabled: true @@ -106,7 +101,7 @@ controlplane: port: 8083 # -- path for incoming api calls path: /control - # -- ids api, used for inter connector communication and must be internet facing + # -- dsp api, used for inter connector communication and must be internet facing protocol: # -- port for incoming api calls port: 8084 @@ -122,16 +117,23 @@ controlplane: businessPartnerValidation: log: agreementValidation: true + # 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" + 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. type: ClusterIP @@ -290,8 +292,11 @@ controlplane: affinity: {} url: - # -- Explicitly declared url for reaching the ids api (e.g. if ingresses not used) - ids: "" + # -- Explicitly declared url for reaching the dsp api (e.g. if ingresses not used) + protocol: "" + +# -- Add custom ca certificates to the truststore +customCaCerts: {} dataplane: image: diff --git a/charts/tractusx-connector-legacy/Chart.yaml b/charts/tractusx-connector-legacy/Chart.yaml deleted file mode 100644 index 9c9ef13d8..000000000 --- a/charts/tractusx-connector-legacy/Chart.yaml +++ /dev/null @@ -1,74 +0,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 -# - ---- -apiVersion: v2 -name: tractusx-connector-legacy -deprecated: true -description: | - 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. - - This chart is intended for use with an _existing_ PostgreSQL database and an _existing_ HashiCorp Vault. - - Deprecation notice: this chart uses DAPS, which was replaced with an SSI solution in v0.5.0 of Tractus-X EDC and is thus deprecated. - It will not be maintained, supported or tested anymore and it will be removed in future versions. -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -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.5.0 -# 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.5.0" -home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-legacy -sources: - - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-legacy -dependencies: - # IDS Dynamic Attribute Provisioning Service (IAM) - - name: daps - version: 0.0.1 - repository: "file://./subcharts/omejdn" - alias: daps - condition: install.daps - # HashiCorp Vault - - name: vault - alias: vault - version: 0.20.0 - repository: https://helm.releases.hashicorp.com - condition: install.vault - # PostgreSQL - - name: postgresql - alias: postgresql - version: 12.1.6 - repository: https://charts.bitnami.com/bitnami - condition: install.postgresql diff --git a/charts/tractusx-connector-legacy/README.md b/charts/tractusx-connector-legacy/README.md deleted file mode 100644 index d2ab622b4..000000000 --- a/charts/tractusx-connector-legacy/README.md +++ /dev/null @@ -1,291 +0,0 @@ -# tractusx-connector-legacy - -> **:exclamation: This Helm Chart is deprecated!** - -![Version: 0.5.0](https://img.shields.io/badge/Version-0.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.0](https://img.shields.io/badge/AppVersion-0.5.0-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. - -This chart is intended for use with an _existing_ PostgreSQL database and an _existing_ HashiCorp Vault. - -Deprecation notice: this chart uses DAPS, which was replaced with an SSI solution in v0.5.0 of Tractus-X EDC and is thus deprecated. -It will not be maintained, supported or tested anymore and it will be removed in future versions. - -**Homepage:** - -This chart uses Hashicorp Vault, which is expected to contain the following secrets on application start: - -- `daps-cert`: contains the x509 certificate of the connector. -- `daps-key`: the private key of the x509 certificate -- `aes-keys`: a 128bit, 256bit or 512bit string used to encrypt data. Must be stored in base64 format. - -These must be obtained from a DAPS instance, the process of which is out of the scope of this document. Alternatively, -self-signed certificates can be used for testing: - -```shell -openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout daps.key -out daps.cert -subj "/CN=test" -export DAPS_KEY="$(cat daps.key)" -export DAPS_CERT="$(cat daps.cert)" -``` - -## Launching the application - -The following requirements must be met before launching the application: - -- Write access to a HashiCorp Vault instance is required to run this chart -- Secrets are seeded in advance - -Please also consider using [this example configuration](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml) -to launch the application. -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.5.0 \ - -f /tractusx-connector-test.yaml -``` - -## Source Code - -* - -## Requirements - -| Repository | Name | Version | -|------------|------|---------| -| file://./subcharts/omejdn | daps(daps) | 0.0.1 | -| https://charts.bitnami.com/bitnami | postgresql(postgresql) | 12.1.6 | -| https://helm.releases.hashicorp.com | vault(vault) | 0.20.0 | - -## Values - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| backendService.httpProxyTokenReceiverUrl | string | `""` | | -| 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.businessPartnerValidation.log.agreementValidation | bool | `true` | | -| controlplane.debug.enabled | bool | `false` | | -| controlplane.debug.port | int | `1044` | | -| controlplane.debug.suspendOnStart | bool | `false` | | -| controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"","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 | -| controlplane.endpoints.control.port | int | `8083` | port for incoming api calls | -| controlplane.endpoints.default | object | `{"path":"/api","port":8080}` | default api for health checks, should not be added to any ingress | -| controlplane.endpoints.default.path | string | `"/api"` | path for incoming api calls | -| controlplane.endpoints.default.port | int | `8080` | port for incoming api calls | -| controlplane.endpoints.management | object | `{"authKey":"","path":"/management","port":8081}` | data management api, used by internal users, can be added to an ingress and must not be internet facing | -| controlplane.endpoints.management.authKey | string | `""` | authentication key, must be attached to each 'X-Api-Key' request header | -| controlplane.endpoints.management.path | string | `"/management"` | path for incoming api calls | -| controlplane.endpoints.management.port | int | `8081` | port for incoming api calls | -| controlplane.endpoints.metrics | object | `{"path":"/metrics","port":9090}` | metrics api, used for application metrics, must not be internet facing | -| controlplane.endpoints.metrics.path | string | `"/metrics"` | path for incoming api calls | -| controlplane.endpoints.metrics.port | int | `9090` | port for incoming api calls | -| controlplane.endpoints.protocol | object | `{"path":"/api/v1/dsp","port":8084}` | ids api, used for inter connector communication and must be internet facing | -| controlplane.endpoints.protocol.path | string | `"/api/v1/dsp"` | path for incoming api calls | -| controlplane.endpoints.protocol.port | int | `8084` | port for incoming api calls | -| controlplane.env | object | `{}` | | -| controlplane.envConfigMapNames | list | `[]` | | -| controlplane.envSecretNames | list | `[]` | | -| controlplane.envValueFrom | object | `{}` | | -| controlplane.image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| controlplane.image.repository | string | `""` | Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically | -| controlplane.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | -| controlplane.ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | -| controlplane.ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| controlplane.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| controlplane.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| controlplane.ingresses[0].enabled | bool | `false` | | -| controlplane.ingresses[0].endpoints | list | `["protocol"]` | EDC endpoints exposed by this ingress resource | -| controlplane.ingresses[0].hostname | string | `"edc-control.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| controlplane.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| controlplane.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| controlplane.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | -| controlplane.ingresses[1].annotations | object | `{}` | Additional ingress annotations to add | -| controlplane.ingresses[1].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| controlplane.ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| controlplane.ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| controlplane.ingresses[1].enabled | bool | `false` | | -| controlplane.ingresses[1].endpoints | list | `["management","control"]` | EDC endpoints exposed by this ingress resource | -| controlplane.ingresses[1].hostname | string | `"edc-control.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| controlplane.ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| controlplane.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| controlplane.ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | -| controlplane.initContainers | list | `[]` | | -| controlplane.internationalDataSpaces.catalogId | string | `"TXDC-Catalog"` | | -| controlplane.internationalDataSpaces.curator | string | `""` | | -| controlplane.internationalDataSpaces.description | string | `"Tractus-X Eclipse IDS Data Space Connector"` | | -| controlplane.internationalDataSpaces.id | string | `"TXDC"` | | -| controlplane.internationalDataSpaces.maintainer | string | `""` | | -| controlplane.internationalDataSpaces.title | string | `""` | | -| controlplane.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| controlplane.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | -| controlplane.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | -| controlplane.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | -| controlplane.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | -| controlplane.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | -| controlplane.logging | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | -| controlplane.nodeSelector | object | `{}` | | -| controlplane.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | -| controlplane.podAnnotations | object | `{}` | additional annotations for the pod | -| controlplane.podLabels | object | `{}` | additional labels for the pod | -| controlplane.podSecurityContext | object | `{"fsGroup":10001,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | -| controlplane.podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | -| controlplane.podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | -| controlplane.podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | -| controlplane.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | -| controlplane.readinessProbe.enabled | bool | `true` | Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| controlplane.readinessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | -| controlplane.readinessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first readiness check | -| controlplane.readinessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a readiness check every 10 seconds | -| controlplane.readinessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | -| controlplane.readinessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | -| controlplane.replicaCount | int | `1` | | -| controlplane.resources | object | `{}` | [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container | -| controlplane.securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | -| controlplane.securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | -| controlplane.securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | -| controlplane.securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | -| controlplane.securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | -| 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.tolerations | list | `[]` | | -| controlplane.url.ids | string | `""` | Explicitly declared url for reaching the ids api (e.g. if ingresses not used) | -| controlplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | -| controlplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | -| customLabels | object | `{}` | | -| daps.clientId | string | `""` | | -| daps.connectors[0].attributes.referringConnector | string | `"http://sokrates-controlplane/BPNSOKRATES"` | | -| daps.connectors[0].certificate | string | `""` | | -| daps.connectors[0].id | string | `"E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65"` | | -| daps.connectors[0].name | string | `"sokrates"` | | -| daps.paths.jwks | string | `"/jwks.json"` | | -| daps.paths.token | string | `"/token"` | | -| daps.url | string | `"http://{{ .Release.Name }}-daps:4567"` | | -| dataplane.affinity | object | `{}` | | -| dataplane.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/) | -| dataplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | -| dataplane.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | -| dataplane.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | -| dataplane.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| dataplane.aws.accessKeyId | string | `""` | | -| dataplane.aws.endpointOverride | string | `""` | | -| dataplane.aws.secretAccessKey | string | `""` | | -| 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"` | | -| dataplane.endpoints.metrics.port | int | `9090` | | -| dataplane.endpoints.proxy.path | string | `"/proxy"` | | -| dataplane.endpoints.proxy.port | int | `8186` | | -| dataplane.endpoints.public.path | string | `"/api/public"` | | -| dataplane.endpoints.public.port | int | `8081` | | -| dataplane.env | object | `{}` | | -| dataplane.envConfigMapNames | list | `[]` | | -| dataplane.envSecretNames | list | `[]` | | -| dataplane.envValueFrom | object | `{}` | | -| dataplane.image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| dataplane.image.repository | string | `""` | Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically | -| dataplane.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | -| dataplane.ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | -| dataplane.ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| dataplane.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| dataplane.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| dataplane.ingresses[0].enabled | bool | `false` | | -| dataplane.ingresses[0].endpoints | list | `["public"]` | EDC endpoints exposed by this ingress resource | -| dataplane.ingresses[0].hostname | string | `"edc-data.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| dataplane.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| dataplane.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| dataplane.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | -| dataplane.initContainers | list | `[]` | | -| dataplane.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| dataplane.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | -| dataplane.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | -| dataplane.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | -| dataplane.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | -| dataplane.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | -| dataplane.logging | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | -| dataplane.nodeSelector | object | `{}` | | -| dataplane.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | -| dataplane.podAnnotations | object | `{}` | additional annotations for the pod | -| dataplane.podLabels | object | `{}` | additional labels for the pod | -| dataplane.podSecurityContext | object | `{"fsGroup":10001,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | -| dataplane.podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | -| dataplane.podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | -| dataplane.podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | -| dataplane.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | -| dataplane.readinessProbe.enabled | bool | `true` | Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| dataplane.readinessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | -| dataplane.readinessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first readiness check | -| dataplane.readinessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | -| dataplane.readinessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | -| dataplane.readinessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | -| dataplane.replicaCount | int | `1` | | -| dataplane.resources | object | `{}` | [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container | -| dataplane.securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | -| dataplane.securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | -| dataplane.securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | -| dataplane.securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | -| dataplane.securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | -| 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.tolerations | list | `[]` | | -| dataplane.url.public | string | `""` | Explicitly declared url for reaching the public api (e.g. if ingresses not used) | -| dataplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | -| dataplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | -| fullnameOverride | string | `""` | | -| idsdaps.connectors[0].certificate | string | `""` | | -| 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.daps | bool | `true` | | -| install.postgresql | bool | `true` | | -| install.vault | 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 | `""` | | -| postgresql.auth.database | string | `"edc"` | | -| postgresql.auth.password | string | `"password"` | | -| postgresql.auth.username | string | `"user"` | | -| postgresql.jdbcUrl | string | `"jdbc:postgresql://{{ .Release.Name }}-postgresql:5432/edc"` | | -| postgresql.primary.persistence.enabled | bool | `false` | | -| postgresql.readReplicas.persistence.enabled | bool | `false` | | -| serviceAccount.annotations | object | `{}` | | -| serviceAccount.create | bool | `true` | | -| serviceAccount.imagePullSecrets | list | `[]` | 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) | -| serviceAccount.name | string | `""` | | -| tests | object | `{"hookDeletePolicy":"before-hook-creation,hook-succeeded"}` | Configurations for Helm tests | -| tests.hookDeletePolicy | string | `"before-hook-creation,hook-succeeded"` | Configure the hook-delete-policy for Helm tests | -| vault.hashicorp.healthCheck.enabled | bool | `true` | | -| vault.hashicorp.healthCheck.standbyOk | bool | `true` | | -| vault.hashicorp.paths.health | string | `"/v1/sys/health"` | | -| vault.hashicorp.paths.secret | string | `"/v1/secret"` | | -| vault.hashicorp.timeout | int | `30` | | -| vault.hashicorp.token | string | `""` | | -| vault.hashicorp.url | string | `"http://{{ .Release.Name }}-vault:8200"` | | -| vault.injector.enabled | bool | `false` | | -| vault.secretNames.dapsPrivateKey | string | `"daps-private-key"` | | -| vault.secretNames.dapsPublicKey | string | `"daps-public-key"` | | -| vault.secretNames.transferProxyTokenEncryptionAesKey | string | `"transfer-proxy-token-encryption-aes-key"` | | -| vault.secretNames.transferProxyTokenSignerPrivateKey | string | `"transfer-proxy-token-signer-private-key"` | | -| vault.secretNames.transferProxyTokenSignerPublicKey | string | `"transfer-proxy-token-signer-public-key"` | | -| vault.server.dev.devRootToken | string | `"root"` | | -| vault.server.dev.enabled | bool | `true` | | -| vault.server.postStart | string | `nil` | | - ----------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/tractusx-connector-legacy/README.md.gotmpl b/charts/tractusx-connector-legacy/README.md.gotmpl deleted file mode 100644 index 210216e6c..000000000 --- a/charts/tractusx-connector-legacy/README.md.gotmpl +++ /dev/null @@ -1,51 +0,0 @@ -{{ template "chart.header" . }} - -{{ template "chart.deprecationWarning" . }} - -{{ template "chart.badgesSection" . }} - -{{ template "chart.description" . }} - -{{ template "chart.homepageLine" . }} - -This chart uses Hashicorp Vault, which is expected to contain the following secrets on application start: - -- `daps-cert`: contains the x509 certificate of the connector. -- `daps-key`: the private key of the x509 certificate -- `aes-keys`: a 128bit, 256bit or 512bit string used to encrypt data. Must be stored in base64 format. - -These must be obtained from a DAPS instance, the process of which is out of the scope of this document. Alternatively, -self-signed certificates can be used for testing: - -```shell -openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout daps.key -out daps.cert -subj "/CN=test" -export DAPS_KEY="$(cat daps.key)" -export DAPS_CERT="$(cat daps.cert)" -``` - -## Launching the application - -The following requirements must be met before launching the application: - -- Write access to a HashiCorp Vault instance is required to run this chart -- Secrets are seeded in advance - -Please also consider using [this example configuration](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml) -to launch the application. -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 {{ .Version }} \ - -f /tractusx-connector-test.yaml -``` - -{{ template "chart.maintainersSection" . }} - -{{ template "chart.sourcesSection" . }} - -{{ template "chart.requirementsSection" . }} - -{{ template "chart.valuesSection" . }} - -{{ template "helm-docs.versionFooter" . }} diff --git a/charts/tractusx-connector-legacy/example.values.yaml b/charts/tractusx-connector-legacy/example.values.yaml deleted file mode 100644 index d2b082afa..000000000 --- a/charts/tractusx-connector-legacy/example.values.yaml +++ /dev/null @@ -1,97 +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 -# - -fullnameOverride: tx-prod -################################ -# EDC ControlPlane + DataPlane # -################################ -participant: - id: "test-participant" -controlplane: - service: - type: NodePort - endpoints: - management: - authKey: password - image: - pullPolicy: Never - tag: "latest" - repository: "edc-controlplane-postgresql-hashicorp-vault-legacy" - securityContext: - # avoids some errors in the log: cannot write temp files of large multipart requests when R/O - readOnlyRootFilesystem: false -dataplane: - image: - pullPolicy: Never - tag: "latest" - repository: "edc-dataplane-hashicorp-vault" - securityContext: - # avoids some errors in the log: cannot write temp files of large multipart requests when R/O - readOnlyRootFilesystem: false - aws: - endpointOverride: http://minio:9000 - secretAccessKey: qwerty123 - accessKeyId: qwerty123 -postgresql: - jdbcUrl: jdbc:postgresql://{{ .Release.Name }}-postgresql:5432/edc - auth: - username: user - password: password -vault: - hashicorp: - url: http://{{ .Release.Name }}-vault:8200 - token: root - secretNames: - transferProxyTokenSignerPublicKey: daps-crt - transferProxyTokenSignerPrivateKey: daps-key - transferProxyTokenEncryptionAesKey: aes-keys - dapsPrivateKey: daps-key - dapsPublicKey: daps-crt - server: - postStart: - - sh - - -c - - |- - { - sleep 5 - - cat << EOF | /bin/vault kv put secret/daps-crt content=- - <<< ENTER CERTIFICATE CONTENT HERE!!! >>> - EOF - - - cat << EOF | /bin/vault kv put secret/daps-key content=- - <<< ENTER PRIVATE KEY CONTENT HERE !!! >>> - EOF - - - /bin/vault kv put secret/aes-keys content=YWVzX2VuY2tleV90ZXN0Cg== - - } -daps: - url: "http://{{ .Release.Name }}-daps:4567" - clientId: "E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65" -backendService: - httpProxyTokenReceiverUrl: "http://backend:8080" -tests: - hookDeletePolicy: before-hook-creation -idsdaps: - connectors: - - certificate: |- - <<< ENTER CERTIFICATE CONTENT HERE!!! >>> diff --git a/charts/tractusx-connector-legacy/subcharts/omejdn/.helmignore b/charts/tractusx-connector-legacy/subcharts/omejdn/.helmignore deleted file mode 100644 index 0e8a0eb36..000000000 --- a/charts/tractusx-connector-legacy/subcharts/omejdn/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/charts/tractusx-connector-legacy/subcharts/omejdn/Chart.yaml b/charts/tractusx-connector-legacy/subcharts/omejdn/Chart.yaml deleted file mode 100644 index a41ff8bd4..000000000 --- a/charts/tractusx-connector-legacy/subcharts/omejdn/Chart.yaml +++ /dev/null @@ -1,40 +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 -# - ---- -apiVersion: v2 -name: daps -description: A Helm chart for Kubernetes -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -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.0.1 -# 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.0.1" diff --git a/charts/tractusx-connector-legacy/subcharts/omejdn/README.md b/charts/tractusx-connector-legacy/subcharts/omejdn/README.md deleted file mode 100644 index 1f0c20e36..000000000 --- a/charts/tractusx-connector-legacy/subcharts/omejdn/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# daps - -![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) - -A Helm chart for Kubernetes - -## Values - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. | -| automountServiceAccountToken | bool | `false` | Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod | -| 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/) | -| autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | -| autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | -| autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | -| autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| connectors | list | `[]` | | -| fullnameOverride | string | `""` | Overrides the releases full name | -| image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/fraunhofer-aisec/omejdn-server"` | Which omjedn container image to use | -| image.tag | string | `"1.7.1"` | Overrides the image tag whose default is the chart appVersion | -| imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | -| nameOverride | string | `""` | Overrides the charts name | -| nodeSelector | object | `{}` | [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. | -| podAnnotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) | -| podSecurityContext | object | `{}` | | -| replicaCount | int | `1` | Specifies how many replicas of a deployed pod shall be created during the deployment Note: If horizontal pod autoscaling is enabled this setting has no effect | -| resources | object | `{}` | [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod | -| securityContext | object | `{}` | | -| service.port | int | `4567` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service) to expose the running application on a set of Pods as a network service. | -| 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. | -| serviceAccount.annotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account | -| serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | -| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template | -| tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. | - ----------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/_helpers.tpl b/charts/tractusx-connector-legacy/subcharts/omejdn/templates/_helpers.tpl deleted file mode 100644 index 95b115eee..000000000 --- a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "omejdn.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -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). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "omejdn.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "omejdn.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "omejdn.labels" -}} -helm.sh/chart: {{ include "omejdn.chart" . }} -{{ include "omejdn.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "omejdn.selectorLabels" -}} -app.kubernetes.io/name: {{ include "omejdn.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "omejdn.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "omejdn.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/configmap.yaml b/charts/tractusx-connector-legacy/subcharts/omejdn/templates/configmap.yaml deleted file mode 100644 index 0f007ed8d..000000000 --- a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/configmap.yaml +++ /dev/null @@ -1,92 +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 -# - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "omejdn.fullname" . }} - labels: - {{- include "omejdn.labels" . | nindent 4 }} -data: - scope_mapping.yml: |- - --- - idsc:IDS_CONNECTOR_ATTRIBUTES_ALL: - - referringConnector - - omejdn.yml: |- - --- - host: http://{{ .Release.Name }}-daps:4567/ - path_prefix: '' - bind_to: 0.0.0.0 - allow_origin: "*" - app_env: debug - openid: false - user_backend: - - yaml - user_backend_default: yaml - accept_audience: idsc:IDS_CONNECTORS_ALL - issuer: http://{{ .Release.Name }}-daps:4567/ - environment: development - default_audience: - - idsc:IDS_CONNECTORS_ALL - access_token: - expiration: 3600 - algorithm: RS256 - id_token: - expiration: 3600 - algorithm: RS256 - - plugins.yml: |- - --- - plugins: - token_user_attributes: - - clients.yml: |- - --- - - client_id: data-plane-oauth2 - client_secret: supersecret - name: provision oauth2 - grant_types: - - client_credentials - token_endpoint_auth_method: client_secret_post - scope: openid -{{- range $i, $val := .Values.connectors }} - - client_id: {{ quote $val.id }} - name: {{ quote $val.name }} - token_endpoint_auth_method: private_key_jwt - grant_types: - - client_credentials - scope: - - idsc:IDS_CONNECTOR_ATTRIBUTES_ALL - attributes: - - key: idsc - value: IDS_CONNECTOR_ATTRIBUTES_ALL - - key: securityProfile - value: idsc:BASE_SECURITY_PROFILE - {{- range $key, $value := $val.attributes }} - - key: {{ $key }} - value: {{ $value }} - {{- end }} - redirect_uri: http://localhost:4200 -{{ end -}} - - -{{- range $i, $val := .Values.connectors }} - {{ $val.name }}: {{ quote $val.certificate | toString }} -{{ end -}} diff --git a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/deployment.yaml b/charts/tractusx-connector-legacy/subcharts/omejdn/templates/deployment.yaml deleted file mode 100644 index 58bfff105..000000000 --- a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/deployment.yaml +++ /dev/null @@ -1,168 +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 -# - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "omejdn.fullname" . }} - labels: - {{- include "omejdn.labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "omejdn.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "omejdn.selectorLabels" . | nindent 8 }} - spec: - {{- if .Values.imagePullSecret.dockerconfigjson }} - imagePullSecrets: - - name: {{ include "omejdn.fullname" . }}-imagepullsecret - {{- else }} - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- end }} - serviceAccountName: {{ include "omejdn.serviceAccountName" . }} - automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} - initContainers: - - name: init-daps-pvc - image: alpine - command: - - "sh" - - "-c" - args: - - | - cp /opt/config/omejdn.yml /etc/daps/omejdn.yml - cp /opt/config/clients.yml /etc/daps/clients.yml - cp /opt/config/plugins.yml /etc/daps/plugins.yml - cp /opt/config/scope_mapping.yml /etc/daps/scope_mapping.yml - apk add --update openssl - openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout /etc/keys/omejdn/omejdn.key \ - -subj "/C=DE/ST=Berlin/L=Berlin/O=Tractus-X-EDC-Test, Inc./OU=DE" - volumeMounts: - - mountPath: /etc/daps - name: config-dir - - mountPath: /etc/keys/omejdn - name: omejdn-key-dir - - mountPath: /opt/config/omejdn.yml - name: omejdn-config - subPath: omejdn.yml - - mountPath: /opt/config/scope_mapping.yml - name: scope-mapping - subPath: scope_mapping.yml - - mountPath: /opt/config/clients.yml - name: clients-config - subPath: clients.yml - - mountPath: /opt/config/plugins.yml - name: plugins-config - subPath: plugins.yml - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - volumeMounts: - - mountPath: /opt/config/ - name: config-dir - - mountPath: /opt/keys/omejdn/omejdn.key - name: omejdn-key-dir - subPath: omejdn.key - - mountPath: /opt/keys/clients/ - name: client-certificates - ports: - - name: http - containerPort: 4567 - protocol: TCP - livenessProbe: - httpGet: - path: /jwks.json - port: http - readinessProbe: - httpGet: - path: /jwks.json - port: http - resources: - {{- toYaml .Values.resources | nindent 12 }} - env: - - name: OMEJDN_JWT_AUD_OVERRIDE - value: "idsc:IDS_CONNECTORS_ALL" - - name: OMEJDN_PLUGINS - value: "config/plugins.yml" - volumes: - - name: config-dir - emptyDir: { } - - name: omejdn-key-dir - emptyDir: { } - - name: omejdn-config - configMap: - name: {{ include "omejdn.fullname" . }} - items: - - key: omejdn.yml - path: omejdn.yml - - name: scope-mapping - configMap: - name: {{ include "omejdn.fullname" . }} - items: - - key: scope_mapping.yml - path: scope_mapping.yml - - name: clients-config - configMap: - name: {{ include "omejdn.fullname" . }} - items: - - key: clients.yml - path: clients.yml - - name: plugins-config - configMap: - name: {{ include "omejdn.fullname" . }} - items: - - key: plugins.yml - path: plugins.yml - - name: client-certificates - configMap: - name: {{ include "omejdn.fullname" . }} - items: - {{- range $i, $val := .Values.connectors }} - - key: {{ $val.name }} - path: {{ $val.id }}.cert - {{- end }} diff --git a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/hpa.yaml b/charts/tractusx-connector-legacy/subcharts/omejdn/templates/hpa.yaml deleted file mode 100644 index f1f072f6c..000000000 --- a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/hpa.yaml +++ /dev/null @@ -1,47 +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 - # - -{{- if .Values.autoscaling.enabled }} ---- -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "omejdn.fullname" . }} - labels: - {{- include "omejdn.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "omejdn.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/imagepullsecret.yaml b/charts/tractusx-connector-legacy/subcharts/omejdn/templates/imagepullsecret.yaml deleted file mode 100644 index 44f573e0f..000000000 --- a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/imagepullsecret.yaml +++ /dev/null @@ -1,31 +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 - # - -{{- if .Values.imagePullSecret.dockerconfigjson }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "edc-dataplane.fullname" . }}-imagepullsecret - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -data: - .dockerconfigjson: {{ .Values.imagePullSecret.dockerconfigjson }} -type: kubernetes.io/dockerconfigjson -{{- end }} diff --git a/charts/tractusx-connector-legacy/subcharts/omejdn/values.yaml b/charts/tractusx-connector-legacy/subcharts/omejdn/values.yaml deleted file mode 100644 index f411b8774..000000000 --- a/charts/tractusx-connector-legacy/subcharts/omejdn/values.yaml +++ /dev/null @@ -1,109 +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 -# - ---- -# Default values for omejdn. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -# -- Specifies how many replicas of a deployed pod shall be created during the deployment -# Note: If horizontal pod autoscaling is enabled this setting has no effect -replicaCount: 1 - -image: - # -- Which omjedn container image to use - repository: ghcr.io/fraunhofer-aisec/omejdn-server - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use - pullPolicy: IfNotPresent - # -- Overrides the image tag whose default is the chart appVersion - tag: "1.7.1" - -imagePullSecret: - # -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) - # Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). - # Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. - dockerconfigjson: "" - -# -- Overrides the charts name -nameOverride: "" - -# -- Overrides the releases full name -fullnameOverride: "" - -serviceAccount: - # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release - create: true - # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account - annotations: {} - # -- The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template - name: "" - -# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod -automountServiceAccountToken: false - -# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) -podAnnotations: {} - -# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment -podSecurityContext: {} - -# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod -securityContext: {} - -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. - type: ClusterIP - # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service) to expose the running application on a set of Pods as a network service. - port: 4567 - -# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod -resources: {} - -autoscaling: - # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) - enabled: false - # -- Minimal replicas if resource consumption falls below resource threshholds - minReplicas: 1 - # -- Maximum replicas if resource consumption exceeds resource threshholds - maxReplicas: 100 - # -- targetAverageUtilization of cpu provided to a pod - targetCPUUtilizationPercentage: 80 - # -- targetAverageUtilization of memory provided to a pod - targetMemoryUtilizationPercentage: 80 - -# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. -nodeSelector: {} - -# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. -tolerations: [] - -# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. -affinity: {} - -# List of connector clients. Certificate and Client-ID must be configured in parallel. -#
-# Example Connector: -# - id: grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 -# name: my-connector -# attributes: -# issuerConnector: http://localhost:8080/ -# certificate: |- -# -----BEGIN CERTIFICATE----- -# foo -# -----END CERTIFICATE----- -connectors: [] diff --git a/charts/tractusx-connector-legacy/templates/NOTES.txt b/charts/tractusx-connector-legacy/templates/NOTES.txt deleted file mode 100644 index 66a2337b5..000000000 --- a/charts/tractusx-connector-legacy/templates/NOTES.txt +++ /dev/null @@ -1,45 +0,0 @@ -1. Get the control plane URL by running these commands: -{{ with index .Values.controlplane.ingresses 0}} -{{- if .enabled }} -{{- range .paths }} - http{{ if .tls }}s{{ end }}://{{ .hostname }}{{ .path }} -{{- end }} -{{- else if contains "NodePort" $.Values.controlplane.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ $.Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "txdc.fullname" $ }}-controlplane) - export NODE_IP=$(kubectl get nodes --namespace {{ $.Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" $.Values.controlplane.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "txdc.fullname" . }}-controlplane' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "txdc.fullname" . }}-controlplane --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ $.Values.controlplane.service.port }} -{{- else if contains "ClusterIP" $.Values.controlplane.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ $.Release.Namespace }} -l "app.kubernetes.io/name={{ include "txdc.name" $ }}-controlplane,app.kubernetes.io/instance={{ $.Release.Name }}-controlplane" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ $.Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - kubectl --namespace {{ $.Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT - echo "Visit http://127.0.0.1:8080 to use your application" -{{- end }} -{{- end }} - -2. Get the data plane URL by running these commands: -{{ with index .Values.controlplane.ingresses 0}} -{{- if .enabled }} -{{- range .paths }} - http{{ if .tls }}s{{ end }}://{{ .hostname }}{{ .path }} -{{- end }} -{{- else if contains "NodePort" $.Values.dataplane.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ $.Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "txdc.fullname" $ }}-dataplane) - export NODE_IP=$(kubectl get nodes --namespace {{ $.Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" $.Values.dataplane.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ $.Release.Namespace }} svc -w {{ include "txdc.fullname" $ }}-dataplane' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "txdc.fullname" $ }}-dataplane --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" $.Values.dataplane.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ $.Release.Namespace }} -l "app.kubernetes.io/name={{ include "txdc.name" $ }}-dataplane,app.kubernetes.io/instance={{ $.Release.Name }}-dataplane" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ $.Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - kubectl --namespace {{ $.Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT - echo "Visit http://127.0.0.1:8080 to use your application" -{{- end }} -{{- end }} diff --git a/charts/tractusx-connector-legacy/templates/_helpers.tpl b/charts/tractusx-connector-legacy/templates/_helpers.tpl deleted file mode 100644 index 701e6fc75..000000000 --- a/charts/tractusx-connector-legacy/templates/_helpers.tpl +++ /dev/null @@ -1,175 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "txdc.name" -}} -{{- default .Chart.Name .Values.nameOverride | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end }} - -{{/* -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). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "txdc.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "txdc.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Control Common labels -*/}} -{{- define "txdc.labels" -}} -helm.sh/chart: {{ include "txdc.chart" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Control Common labels -*/}} -{{- define "txdc.controlplane.labels" -}} -helm.sh/chart: {{ include "txdc.chart" . }} -{{ include "txdc.controlplane.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -app.kubernetes.io/component: edc-controlplane -app.kubernetes.io/part-of: edc -{{- end }} - -{{/* -Data Common labels -*/}} -{{- define "txdc.dataplane.labels" -}} -helm.sh/chart: {{ include "txdc.chart" . }} -{{ include "txdc.dataplane.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -app.kubernetes.io/component: edc-dataplane -app.kubernetes.io/part-of: edc -{{- end }} - -{{/* -Control Selector labels -*/}} -{{- define "txdc.controlplane.selectorLabels" -}} -app.kubernetes.io/name: {{ include "txdc.name" . }}-controlplane -app.kubernetes.io/instance: {{ .Release.Name }}-controlplane -{{- end }} - -{{/* -Data Selector labels -*/}} -{{- define "txdc.dataplane.selectorLabels" -}} -app.kubernetes.io/name: {{ include "txdc.name" . }}-dataplane -app.kubernetes.io/instance: {{ .Release.Name }}-dataplane -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "txdc.controlplane.serviceaccount.name" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "txdc.fullname" . ) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "txdc.dataplane.serviceaccount.name" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "txdc.fullname" . ) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} - -{{/* -Control IDS URL -*/}} -{{- define "txdc.controlplane.url.protocol" -}} -{{- if .Values.controlplane.url.protocol }}{{/* if ids api url has been specified explicitly */}} -{{- .Values.controlplane.url.protocol }} -{{- else }}{{/* else when ids api url has not been specified explicitly */}} -{{- with (index .Values.controlplane.ingresses 0) }} -{{- if .enabled }}{{/* if ingress enabled */}} -{{- if .tls.enabled }}{{/* if TLS enabled */}} -{{- printf "https://%s" .hostname -}} -{{- else }}{{/* else when TLS not enabled */}} -{{- printf "http://%s" .hostname -}} -{{- end }}{{/* end if tls */}} -{{- else }}{{/* else when ingress not enabled */}} -{{- printf "http://%s-controlplane:%v" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.protocol.port -}} -{{- end }}{{/* end if ingress */}} -{{- end }}{{/* end with ingress */}} -{{- end }}{{/* end if .Values.controlplane.url.protocol */}} -{{- end }} - -{{/* -Validation URL -*/}} -{{- define "txdc.controlplane.url.validation" -}} -{{- printf "http://%s-controlplane:%v%s/token" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.control.port $.Values.controlplane.endpoints.control.path -}} -{{- end }} - -{{/* -Data Control 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 -}} -{{- end }} - -{{/* -Data Public URL -*/}} -{{- define "txdc.dataplane.url.public" -}} -{{- if .Values.dataplane.url.public }}{{/* if public api url has been specified explicitly */}} -{{- .Values.dataplane.url.public }} -{{- else }}{{/* else when public api url has not been specified explicitly */}} -{{- with (index .Values.dataplane.ingresses 0) }} -{{- if .enabled }}{{/* if ingress enabled */}} -{{- if .tls.enabled }}{{/* if TLS enabled */}} -{{- printf "https://%s%s" .hostname $.Values.dataplane.endpoints.public.path -}} -{{- else }}{{/* else when TLS not enabled */}} -{{- printf "http://%s%s" .hostname $.Values.dataplane.endpoints.public.path -}} -{{- end }}{{/* end if tls */}} -{{- else }}{{/* else when ingress not enabled */}} -{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" $ ) $.Values.dataplane.endpoints.public.port $.Values.dataplane.endpoints.public.path -}} -{{- end }}{{/* end if ingress */}} -{{- end }}{{/* end with ingress */}} -{{- end }}{{/* end if .Values.dataplane.url.public */}} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "txdc.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "txdc.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/charts/tractusx-connector-legacy/templates/configmap-controlplane.yaml b/charts/tractusx-connector-legacy/templates/configmap-controlplane.yaml deleted file mode 100644 index 42f2a493f..000000000 --- a/charts/tractusx-connector-legacy/templates/configmap-controlplane.yaml +++ /dev/null @@ -1,36 +0,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 -# - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "txdc.fullname" . }}-controlplane - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "txdc.controlplane.labels" . | nindent 4 }} -data: - opentelemetry.properties: |- - {{- .Values.controlplane.opentelemetry | nindent 4 }} - - logging.properties: |- - {{- .Values.controlplane.logging | nindent 4 }} diff --git a/charts/tractusx-connector-legacy/templates/configmap-dataplane.yaml b/charts/tractusx-connector-legacy/templates/configmap-dataplane.yaml deleted file mode 100644 index 87fd401c3..000000000 --- a/charts/tractusx-connector-legacy/templates/configmap-dataplane.yaml +++ /dev/null @@ -1,36 +0,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 -# - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "txdc.fullname" . }}-dataplane - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "txdc.dataplane.labels" . | nindent 4 }} -data: - opentelemetry.properties: |- - {{- .Values.dataplane.opentelemetry | nindent 4 }} - - logging.properties: |- - {{- .Values.dataplane.logging | nindent 4 }} diff --git a/charts/tractusx-connector-legacy/templates/deployment-controlplane.yaml b/charts/tractusx-connector-legacy/templates/deployment-controlplane.yaml deleted file mode 100644 index 73f7e0e77..000000000 --- a/charts/tractusx-connector-legacy/templates/deployment-controlplane.yaml +++ /dev/null @@ -1,355 +0,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 - # - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "txdc.fullname" . }}-controlplane - labels: - {{- include "txdc.controlplane.labels" . | nindent 4 }} -spec: - {{- if not .Values.controlplane.autoscaling.enabled }} - replicas: {{ .Values.controlplane.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "txdc.controlplane.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.controlplane.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "txdc.controlplane.selectorLabels" . | nindent 8 }} - {{- with .Values.controlplane.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "txdc.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.controlplane.podSecurityContext | nindent 8 }} - initContainers: - {{- toYaml .Values.controlplane.initContainers | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.controlplane.securityContext | nindent 12 }} - - # either use the specified image, or use the default one - {{- if .Values.controlplane.image.repository }} - image: "{{ .Values.controlplane.image.repository }}:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" - {{- else }} - image: "tractusx/edc-controlplane-postgresql-hashicorp-vault-legacy:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" - {{- end }} - imagePullPolicy: {{ .Values.controlplane.image.pullPolicy }} - ports: - {{- range $key,$value := .Values.controlplane.endpoints }} - - name: {{ $key }} - containerPort: {{ $value.port }} - protocol: TCP - {{- end }} - {{- if .Values.controlplane.livenessProbe.enabled }} - livenessProbe: - httpGet: - path: {{ .Values.controlplane.endpoints.default.path }}/check/liveness - port: {{ .Values.controlplane.endpoints.default.port }} - initialDelaySeconds: {{ .Values.controlplane.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.controlplane.livenessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.controlplane.livenessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.controlplane.livenessProbe.failureThreshold }} - successThreshold: {{ .Values.controlplane.livenessProbe.successThreshold }} - {{- end }} - {{- if .Values.controlplane.readinessProbe.enabled }} - readinessProbe: - httpGet: - path: {{ .Values.controlplane.endpoints.default.path }}/check/readiness - port: {{ .Values.controlplane.endpoints.default.port }} - initialDelaySeconds: {{ .Values.controlplane.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.controlplane.readinessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.controlplane.readinessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.controlplane.readinessProbe.failureThreshold }} - successThreshold: {{ .Values.controlplane.readinessProbe.successThreshold }} - {{- end }} - resources: - {{- toYaml .Values.controlplane.resources | nindent 12 }} - env: - {{- if .Values.controlplane.debug.enabled }} - - name: "JAVA_TOOL_OPTIONS" - {{- if .Values.controlplane.debug.suspendOnStart }} - value: >- - {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=%v" .Values.controlplane.debug.port }} - {{- else }} - value: >- - {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=%v" .Values.controlplane.debug.port }} - {{- end }} - {{- end }} - - ######################## - ## ID CONFIGURATION ## - ######################## - - name: EDC_PARTICIPANT_ID - value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }} - - ######################## - ## DAPS CONFIGURATION ## - ######################## - - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/iam/oauth2/oauth2-core - - name: EDC_OAUTH_CLIENT_ID - value: {{ .Values.daps.clientId | required ".Values.daps.clientId is required" | quote }} - - name: EDC_OAUTH_PROVIDER_JWKS_URL - value: {{ printf "%s%s" (tpl .Values.daps.url .) .Values.daps.paths.jwks }} - - name: EDC_OAUTH_TOKEN_URL - value: {{ printf "%s%s" (tpl .Values.daps.url .) .Values.daps.paths.token }} - - name: EDC_OAUTH_PRIVATE_KEY_ALIAS - value: {{ .Values.vault.secretNames.dapsPrivateKey | required ".Values.vault.secretNames.dapsPrivateKey is required" | quote }} - - name: EDC_OAUTH_CERTIFICATE_ALIAS - value: {{ .Values.vault.secretNames.dapsPublicKey | required ".Values.vault.secretNames.dapsPublicKey is required" | quote }} - - ####### - # API # - ####### - - name: "EDC_API_AUTH_KEY" - value: {{ .Values.controlplane.endpoints.management.authKey | required ".Values.controlplane.endpoints.management.authKey is required" | quote }} - - name: "WEB_HTTP_DEFAULT_PORT" - value: {{ .Values.controlplane.endpoints.default.port | quote }} - - name: "WEB_HTTP_DEFAULT_PATH" - value: {{ .Values.controlplane.endpoints.default.path | quote }} - - name: "WEB_HTTP_MANAGEMENT_PORT" - value: {{ .Values.controlplane.endpoints.management.port | quote }} - - name: "WEB_HTTP_MANAGEMENT_PATH" - value: {{ .Values.controlplane.endpoints.management.path | quote }} - - name: "WEB_HTTP_CONTROL_PORT" - value: {{ .Values.controlplane.endpoints.control.port | quote }} - - name: "WEB_HTTP_CONTROL_PATH" - value: {{ .Values.controlplane.endpoints.control.path | quote }} - - name: "WEB_HTTP_PROTOCOL_PORT" - value: {{ .Values.controlplane.endpoints.protocol.port | quote }} - - name: "WEB_HTTP_PROTOCOL_PATH" - value: {{ .Values.controlplane.endpoints.protocol.path | quote }} - - ######### - ## DSP ## - ######### - - - name: "EDC_DSP_CALLBACK_ADDRESS" - value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" .) .Values.controlplane.endpoints.protocol.path | quote }} - - name: "EDC_OAUTH_PROVIDER_AUDIENCE" - value: "idsc:IDS_CONNECTORS_ALL" - - name: "EDC_OAUTH_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path | quote }} - - ################ - ## POSTGRESQL ## - ################ - - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/store/sql/asset-index-sql - - name: "EDC_DATASOURCE_ASSET_NAME" - value: "asset" - - name: "EDC_DATASOURCE_ASSET_USER" - value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - - name: "EDC_DATASOURCE_ASSET_PASSWORD" - value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - - name: "EDC_DATASOURCE_ASSET_URL" - value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} - - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/store/sql/contract-definition-store-sql - - name: "EDC_DATASOURCE_CONTRACTDEFINITION_NAME" - value: "contractdefinition" - - name: "EDC_DATASOURCE_CONTRACTDEFINITION_USER" - value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - - name: "EDC_DATASOURCE_CONTRACTDEFINITION_PASSWORD" - value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - - name: "EDC_DATASOURCE_CONTRACTDEFINITION_URL" - value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} - - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/store/sql/contract-negotiation-store-sql - - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_NAME" - value: "contractnegotiation" - - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_USER" - value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_PASSWORD" - value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_URL" - value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} - - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/store/sql/policy-store-sql - - name: "EDC_DATASOURCE_POLICY_NAME" - value: "policy" - - name: "EDC_DATASOURCE_POLICY_USER" - value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - - name: "EDC_DATASOURCE_POLICY_PASSWORD" - value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - - name: "EDC_DATASOURCE_POLICY_URL" - value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} - - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/store/sql/transfer-process-store-sql - - name: "EDC_DATASOURCE_TRANSFERPROCESS_NAME" - value: "transferprocess" - - name: "EDC_DATASOURCE_TRANSFERPROCESS_USER" - value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - - name: "EDC_DATASOURCE_TRANSFERPROCESS_PASSWORD" - value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - - 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 }} - - ################ - ## 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 - - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_SOURCETYPES" - value: "HttpData,AmazonS3" - - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_DESTINATIONTYPES" - value: "HttpProxy,AmazonS3" - - 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" . }} - - name: "EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS" - value: {{ .Values.vault.secretNames.transferProxyTokenSignerPrivateKey | quote }} - - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" - value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | 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 ## - ########### - - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/hashicorp-vault - - name: "EDC_VAULT_HASHICORP_URL" - value: {{ tpl .Values.vault.hashicorp.url . | quote }} - - name: "EDC_VAULT_HASHICORP_TOKEN" - value: {{ .Values.vault.hashicorp.token | required ".Values.vault.hashicorp.token is required" | quote }} - - name: "EDC_VAULT_HASHICORP_TIMEOUT_SECONDS" - value: {{ .Values.vault.hashicorp.timeout | quote }} - - name: "EDC_VAULT_HASHICORP_HEALTH_CHECK_ENABLED" - value: {{ .Values.vault.hashicorp.healthCheck.enabled | quote }} - - name: "EDC_VAULT_HASHICORP_HEALTH_CHECK_STANDBY_OK" - value: {{ .Values.vault.hashicorp.healthCheck.standbyOk | quote }} - - name: "EDC_VAULT_HASHICORP_API_SECRET_PATH" - value: {{ .Values.vault.hashicorp.paths.secret | quote }} - - 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 ## - ########################### - - name: "TRACTUSX_BUSINESSPARTNERVALIDATION_LOG_AGREEMENT_VALIDATION" - value: {{ .Values.controlplane.businessPartnerValidation.log.agreementValidation | quote }} - - ###################################### - ## Additional environment variables ## - ###################################### - - name: "EDC_CONNECTOR_NAME" - value: {{ include "txdc.fullname" .}}-controlplane - {{- range $key, $value := .Values.controlplane.envValueFrom }} - - name: {{ $key | quote }} - valueFrom: - {{- tpl (toYaml $value) $ | nindent 16 }} - {{- end }} - {{- range $key, $value := .Values.controlplane.env }} - - name: {{ $key | quote }} - value: {{ $value | quote }} - {{- end }} - {{- if and (or .Values.controlplane.envSecretNames .Values.controlplane.envConfigMapNames) (or (gt (len .Values.controlplane.envSecretNames) 0) (gt (len .Values.controlplane.envConfigMapNames) 0)) }} - envFrom: - {{- range $value := .Values.controlplane.envSecretNames }} - - secretRef: - name: {{ $value | quote }} - {{- end }} - {{- range $value := .Values.controlplane.envConfigMapNames }} - - configMapRef: - name: {{ $value | quote }} - {{- end }} - {{- end }} - volumeMounts: - - name: "configuration" - mountPath: "/app/opentelemetry.properties" - subPath: "opentelemetry.properties" - - name: "configuration" - mountPath: "/app/logging.properties" - subPath: "logging.properties" - volumes: - - name: "configuration" - configMap: - name: {{ include "txdc.fullname" . }}-controlplane - items: - - key: "opentelemetry.properties" - path: "opentelemetry.properties" - - key: "logging.properties" - path: "logging.properties" - {{- with .Values.controlplane.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.controlplane.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.controlplane.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/charts/tractusx-connector-legacy/templates/deployment-dataplane.yaml b/charts/tractusx-connector-legacy/templates/deployment-dataplane.yaml deleted file mode 100644 index 0364a105c..000000000 --- a/charts/tractusx-connector-legacy/templates/deployment-dataplane.yaml +++ /dev/null @@ -1,230 +0,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 -# - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "txdc.fullname" . }}-dataplane - labels: - {{- include "txdc.dataplane.labels" . | nindent 4 }} -spec: - {{- if not .Values.dataplane.autoscaling.enabled }} - replicas: {{ .Values.dataplane.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "txdc.dataplane.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.dataplane.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "txdc.dataplane.selectorLabels" . | nindent 8 }} - {{- with .Values.dataplane.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "txdc.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.dataplane.podSecurityContext | nindent 8 }} - initContainers: - {{- toYaml .Values.dataplane.initContainers | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.dataplane.securityContext | nindent 12 }} - {{- if .Values.dataplane.image.repository }} - image: "{{ .Values.dataplane.image.repository }}:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" - {{- else }} - image: "tractusx/edc-dataplane-hashicorp-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" - {{- end }} - imagePullPolicy: {{ .Values.dataplane.image.pullPolicy }} - ports: - {{- range $key,$value := .Values.dataplane.endpoints }} - - name: {{ $key }} - containerPort: {{ $value.port }} - protocol: TCP - {{- end }} - {{- if .Values.dataplane.livenessProbe.enabled }} - livenessProbe: - httpGet: - path: {{ .Values.dataplane.endpoints.default.path }}/check/liveness - port: {{ .Values.dataplane.endpoints.default.port }} - initialDelaySeconds: {{ .Values.dataplane.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.dataplane.livenessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.dataplane.livenessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.dataplane.livenessProbe.failureThreshold }} - successThreshold: {{ .Values.dataplane.livenessProbe.successThreshold }} - {{- end }} - {{- if .Values.dataplane.readinessProbe.enabled }} - readinessProbe: - httpGet: - path: {{ .Values.dataplane.endpoints.default.path }}/check/readiness - port: {{ .Values.dataplane.endpoints.default.port }} - initialDelaySeconds: {{ .Values.dataplane.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.dataplane.readinessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.dataplane.readinessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.dataplane.readinessProbe.failureThreshold }} - successThreshold: {{ .Values.dataplane.readinessProbe.successThreshold }} - {{- end }} - resources: - {{- toYaml .Values.dataplane.resources | nindent 12 }} - env: - {{- if .Values.dataplane.debug.enabled }} - - name: "JAVA_TOOL_OPTIONS" - {{- if .Values.dataplane.debug.suspendOnStart }} - value: >- - {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=%v" .Values.dataplane.debug.port }} - {{- else }} - value: >- - {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=%v" .Values.dataplane.debug.port }} - {{- end }} - {{- end }} - - ####### - # API # - ####### - - name: "WEB_HTTP_DEFAULT_PORT" - 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_PUBLIC_PORT" - value: {{ .Values.dataplane.endpoints.public.port | quote }} - - name: "WEB_HTTP_PUBLIC_PATH" - value: {{ .Values.dataplane.endpoints.public.path | quote }} - - name: "EDC_DATAPLANE_TOKEN_VALIDATION_ENDPOINT" - value: {{ include "txdc.controlplane.url.validation" .}} - - ####### - # AWS # - ####### - {{- if .Values.dataplane.aws.endpointOverride }} - - name: "EDC_AWS_ENDPOINT_OVERRIDE" - value: {{ .Values.dataplane.aws.endpointOverride | quote }} - {{- end }} - {{- if .Values.dataplane.aws.secretAccessKey }} - - name: "AWS_SECRET_ACCESS_KEY" - value: {{ .Values.dataplane.aws.secretAccessKey | quote }} - {{- end }} - {{- if .Values.dataplane.aws.accessKeyId }} - - name: "AWS_ACCESS_KEY_ID" - 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 ## - ########### - - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/hashicorp-vault - - name: "EDC_VAULT_HASHICORP_URL" - value: {{ tpl .Values.vault.hashicorp.url . | quote }} - - name: "EDC_VAULT_HASHICORP_TOKEN" - value: {{ .Values.vault.hashicorp.token | required ".Values.vault.hashicorp.token is required" | quote }} - - name: "EDC_VAULT_HASHICORP_TIMEOUT_SECONDS" - value: {{ .Values.vault.hashicorp.timeout | quote }} - - name: "EDC_VAULT_HASHICORP_HEALTH_CHECK_ENABLED" - value: {{ .Values.vault.hashicorp.healthCheck.enabled | quote }} - - name: "EDC_VAULT_HASHICORP_HEALTH_CHECK_STANDBY_OK" - value: {{ .Values.vault.hashicorp.healthCheck.standbyOk | quote }} - - name: "EDC_VAULT_HASHICORP_API_SECRET_PATH" - value: {{ .Values.vault.hashicorp.paths.secret | quote }} - - name: "EDC_VAULT_HASHICORP_API_HEALTH_CHECK_PATH" - value: {{ .Values.vault.hashicorp.paths.health | quote }} - - ###################################### - ## Additional environment variables ## - ###################################### - - name: "EDC_CONNECTOR_NAME" - value: {{ include "txdc.fullname" .}}-dataplane - {{- range $key, $value := .Values.dataplane.envValueFrom }} - - name: {{ $key | quote }} - valueFrom: - {{- tpl (toYaml $value) $ | nindent 16 }} - {{- end }} - {{- range $key, $value := .Values.dataplane.env }} - - name: {{ $key | quote }} - value: {{ $value | quote }} - {{- end }} - {{- if and (or .Values.dataplane.envSecretNames .Values.dataplane.envConfigMapNames) (or (gt (len .Values.dataplane.envSecretNames) 0) (gt (len .Values.dataplane.envConfigMapNames) 0)) }} - envFrom: - {{- range $value := .Values.dataplane.envSecretNames }} - - secretRef: - name: {{ $value | quote }} - {{- end }} - {{- range $value := .Values.dataplane.envConfigMapNames }} - - configMapRef: - name: {{ $value | quote }} - {{- end }} - {{- end }} - volumeMounts: - - name: "configuration" - mountPath: "/app/opentelemetry.properties" - subPath: "opentelemetry.properties" - - name: "configuration" - mountPath: "/app/logging.properties" - subPath: "logging.properties" - volumes: - - name: "configuration" - configMap: - name: {{ include "txdc.fullname" . }}-dataplane - items: - - key: "opentelemetry.properties" - path: "opentelemetry.properties" - - key: "logging.properties" - path: "logging.properties" - {{- with .Values.dataplane.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.dataplane.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.dataplane.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/charts/tractusx-connector-legacy/templates/hpa-controlplane.yaml b/charts/tractusx-connector-legacy/templates/hpa-controlplane.yaml deleted file mode 100644 index c52ed9152..000000000 --- a/charts/tractusx-connector-legacy/templates/hpa-controlplane.yaml +++ /dev/null @@ -1,51 +0,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 -# - -{{- if .Values.controlplane.autoscaling.enabled }} ---- -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "txdc.fullname" . }}-controlplane - labels: - {{- include "txdc.controlplane.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "txdc.fullname" . }}-controlplane - minReplicas: {{ .Values.controlplane.autoscaling.minReplicas }} - maxReplicas: {{ .Values.controlplane.autoscaling.maxReplicas }} - metrics: - {{- if .Values.controlplane.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.controlplane.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.controlplane.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.controlplane.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/charts/tractusx-connector-legacy/templates/hpa-dataplane.yaml b/charts/tractusx-connector-legacy/templates/hpa-dataplane.yaml deleted file mode 100644 index 519c0e526..000000000 --- a/charts/tractusx-connector-legacy/templates/hpa-dataplane.yaml +++ /dev/null @@ -1,51 +0,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 -# - -{{- if .Values.controlplane.autoscaling.enabled }} ---- -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "txdc.fullname" . }}-dataplane - labels: - {{- include "txdc.dataplane.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "txdc.fullname" . }}-dataplane - minReplicas: {{ .Values.dataplane.autoscaling.minReplicas }} - maxReplicas: {{ .Values.dataplane.autoscaling.maxReplicas }} - metrics: - {{- if .Values.dataplane.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.dataplane.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.dataplane.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.dataplane.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/charts/tractusx-connector-legacy/templates/ingress-controlplane.yaml b/charts/tractusx-connector-legacy/templates/ingress-controlplane.yaml deleted file mode 100644 index abc90106e..000000000 --- a/charts/tractusx-connector-legacy/templates/ingress-controlplane.yaml +++ /dev/null @@ -1,100 +0,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 -# - -{{- $fullName := include "txdc.fullname" . }} -{{- $controlLabels := include "txdc.controlplane.labels" . }} -{{- $controlEdcEndpoints := .Values.controlplane.endpoints }} -{{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} -{{- $namespace := .Release.Namespace }} - -{{- range .Values.controlplane.ingresses }} -{{- if and .enabled .endpoints }} -{{- $controlIngressName := printf "%s-controlplane-%s" $fullName .hostname }} -{{- $annotations := .annotations | default dict }} ---- -{{- if semverCompare ">=1.19-0" $gitVersion }} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" $gitVersion }} -apiVersion: networking.k8s.io/v1beta1 -{{- else }} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $controlIngressName }} - namespace: {{ $namespace | default "default" | quote }} - labels: - {{- $controlLabels | nindent 4 }} - annotations: - {{- if and .className (not (semverCompare ">=1.18-0" $gitVersion)) }} - {{- if not (hasKey $annotations "kubernetes.io/ingress.class") }} - {{- $_ := set $annotations "kubernetes.io/ingress.class" .className}} - {{- end }} - {{- end }} - {{- if .certManager }} - {{- if .certManager.issuer }} - {{- $_ := set $annotations "cert-manager.io/issuer" .certManager.issuer}} - {{- end }} - {{- if .certManager.clusterIssuer }} - {{- $_ := set $annotations "cert-manager.io/cluster-issuer" .certManager.clusterIssuer}} - {{- end }} - {{- end }} - {{- with $annotations }} - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .className (semverCompare ">=1.18-0" $gitVersion) }} - ingressClassName: {{ .className }} - {{- end }} - {{- if .hostname }} - {{- if .tls.enabled }} - tls: - - hosts: - - {{ .hostname }} - {{- if .tls.secretName }} - secretName: {{ .tls.secretName }} - {{- else }} - secretName: {{ $controlIngressName }}-tls - {{- end }} - {{- end }} - rules: - - host: {{ .hostname }} - http: - paths: - {{- $ingressEdcEndpoints := .endpoints }} - {{- range $name, $mapping := $controlEdcEndpoints }} - {{- if (has $name $ingressEdcEndpoints) }} - - path: {{ $mapping.path }} - pathType: Prefix - backend: - {{- if semverCompare ">=1.19-0" $gitVersion }} - service: - name: {{ $fullName }}-controlplane - port: - number: {{ $mapping.port }} - {{- else }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} -{{- end }}{{- /* end: if .enabled */}} -{{- end }}{{- /* end: range .Values.ingresses */}} diff --git a/charts/tractusx-connector-legacy/templates/ingress-dataplane.yaml b/charts/tractusx-connector-legacy/templates/ingress-dataplane.yaml deleted file mode 100644 index 4777a55d4..000000000 --- a/charts/tractusx-connector-legacy/templates/ingress-dataplane.yaml +++ /dev/null @@ -1,100 +0,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 -# - -{{- $fullName := include "txdc.fullname" . }} -{{- $dataLabels := include "txdc.dataplane.labels" . }} -{{- $dataEdcEndpoints := .Values.dataplane.endpoints }} -{{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} -{{- $namespace := .Release.Namespace }} - -{{- range .Values.dataplane.ingresses }} -{{- if and .enabled .endpoints }} -{{- $dataIngressName := printf "%s-dataplane-%s" $fullName .hostname }} -{{- $annotations := .annotations | default dict }} ---- -{{- if semverCompare ">=1.19-0" $gitVersion }} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" $gitVersion }} -apiVersion: networking.k8s.io/v1beta1 -{{- else }} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $dataIngressName }} - namespace: {{ $namespace | default "default" | quote }} - labels: - {{- $dataLabels | nindent 4 }} - annotations: - {{- if and .className (not (semverCompare ">=1.18-0" $gitVersion)) }} - {{- if not (hasKey $annotations "kubernetes.io/ingress.class") }} - {{- $_ := set $annotations "kubernetes.io/ingress.class" .className}} - {{- end }} - {{- end }} - {{- if .certManager }} - {{- if .certManager.issuer }} - {{- $_ := set $annotations "cert-manager.io/issuer" .certManager.issuer}} - {{- end }} - {{- if .certManager.clusterIssuer }} - {{- $_ := set $annotations "cert-manager.io/cluster-issuer" .certManager.clusterIssuer}} - {{- end }} - {{- end }} - {{- with $annotations }} - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .className (semverCompare ">=1.18-0" $gitVersion) }} - ingressClassName: {{ .className }} - {{- end }} - {{- if .hostname }} - {{- if .tls.enabled }} - tls: - - hosts: - - {{ .hostname }} - {{- if .tls.secretName }} - secretName: {{ .tls.secretName }} - {{- else }} - secretName: {{ $dataIngressName }}-tls - {{- end }} - {{- end }} - rules: - - host: {{ .hostname }} - http: - paths: - {{- $ingressEdcEndpoints := .endpoints }} - {{- range $name, $mapping := $dataEdcEndpoints }} - {{- if (has $name $ingressEdcEndpoints) }} - - path: {{ $mapping.path }} - pathType: Prefix - backend: - {{- if semverCompare ">=1.19-0" $gitVersion }} - service: - name: {{ $fullName }}-dataplane - port: - number: {{ $mapping.port }} - {{- else }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} -{{- end }}{{- /* end: if .enabled */}} -{{- end }}{{- /* end: range .Values.ingresses */}} diff --git a/charts/tractusx-connector-legacy/templates/networkpolicy.yaml b/charts/tractusx-connector-legacy/templates/networkpolicy.yaml deleted file mode 100644 index 7a40cb6a3..000000000 --- a/charts/tractusx-connector-legacy/templates/networkpolicy.yaml +++ /dev/null @@ -1,45 +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 -# - -{{- if eq (.Values.networkPolicy.enabled | toString) "true" }} -{{- range tuple "controlplane" "dataplane" }} -{{- $name := . }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ include "txdc.fullname" $ }}-{{ $name }} - labels: - {{- include (printf "txdc.%s.labels" $name) $ | nindent 4 }} -spec: - podSelector: - matchLabels: - {{- include (printf "txdc.%s.selectorLabels" $name) $ | nindent 6 }} - ingress: - - from: - {{- toYaml (index $.Values.networkPolicy $name "from") | nindent 6 }} - ports: - {{- range $key,$value := (index $.Values $name "endpoints") }} - - port: {{ $value.port }} - protocol: TCP - {{- end }} - policyTypes: - - Ingress ---- -{{- end }} -{{- end }} diff --git a/charts/tractusx-connector-legacy/templates/service-controlplane.yaml b/charts/tractusx-connector-legacy/templates/service-controlplane.yaml deleted file mode 100644 index 277e9d707..000000000 --- a/charts/tractusx-connector-legacy/templates/service-controlplane.yaml +++ /dev/null @@ -1,55 +0,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 -# - ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "txdc.fullname" . }}-controlplane - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "txdc.controlplane.labels" . | nindent 4 }} -spec: - type: {{ .Values.controlplane.service.type }} - ports: - - port: {{ .Values.controlplane.endpoints.default.port }} - targetPort: default - protocol: TCP - name: default - - port: {{ .Values.controlplane.endpoints.control.port }} - targetPort: control - protocol: TCP - name: control - - port: {{ .Values.controlplane.endpoints.management.port }} - targetPort: management - protocol: TCP - name: management - - port: {{ .Values.controlplane.endpoints.protocol.port }} - targetPort: protocol - protocol: TCP - name: protocol - - port: {{ .Values.controlplane.endpoints.metrics.port }} - targetPort: metrics - protocol: TCP - name: metrics - selector: - {{- include "txdc.controlplane.selectorLabels" . | nindent 4 }} diff --git a/charts/tractusx-connector-legacy/templates/service-dataplane.yaml b/charts/tractusx-connector-legacy/templates/service-dataplane.yaml deleted file mode 100644 index def4e9811..000000000 --- a/charts/tractusx-connector-legacy/templates/service-dataplane.yaml +++ /dev/null @@ -1,56 +0,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 -# - ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "txdc.fullname" . }}-dataplane - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "txdc.dataplane.labels" . | nindent 4 }} -spec: - type: {{ .Values.dataplane.service.type }} - ports: - - port: {{ .Values.dataplane.endpoints.default.port }} - targetPort: default - protocol: TCP - name: default - - port: {{ .Values.dataplane.endpoints.control.port }} - targetPort: control - protocol: TCP - name: control - - port: {{ .Values.dataplane.endpoints.public.port }} - targetPort: public - protocol: TCP - name: public - - port: {{ .Values.dataplane.endpoints.metrics.port }} - targetPort: metrics - protocol: TCP - name: metrics - - port: {{ .Values.dataplane.endpoints.proxy.port }} - targetPort: proxy - protocol: TCP - name: proxy - - selector: - {{- include "txdc.dataplane.selectorLabels" . | nindent 4 }} diff --git a/charts/tractusx-connector-legacy/templates/serviceaccount.yaml b/charts/tractusx-connector-legacy/templates/serviceaccount.yaml deleted file mode 100644 index 4a6e1ac07..000000000 --- a/charts/tractusx-connector-legacy/templates/serviceaccount.yaml +++ /dev/null @@ -1,39 +0,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 -# - -{{- if .Values.serviceAccount.create -}} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "txdc.serviceAccountName" . }} - labels: - {{- include "txdc.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- with .Values.serviceAccount.imagePullSecrets }} -imagePullSecrets: - {{- toYaml . | nindent 2 }} -{{- end }} -{{- end }} diff --git a/charts/tractusx-connector-legacy/templates/tests/test-controlplane-readiness.yaml b/charts/tractusx-connector-legacy/templates/tests/test-controlplane-readiness.yaml deleted file mode 100644 index efd6f3b1e..000000000 --- a/charts/tractusx-connector-legacy/templates/tests/test-controlplane-readiness.yaml +++ /dev/null @@ -1,36 +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 -# - ---- -apiVersion: v1 -kind: Pod -metadata: - name: "{{include "txdc.fullname" .}}test-controlplane-readiness" - labels: - {{- include "txdc.controlplane.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test - "helm.sh/hook-delete-policy": {{ .Values.tests.hookDeletePolicy }} -spec: - containers: - - name: wget - image: curlimages/curl - command: [ 'curl' ] - args: [ '{{- printf "http://%s-controlplane:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.controlplane.endpoints.default.port $.Values.controlplane.endpoints.default.path -}}' ] - restartPolicy: Never diff --git a/charts/tractusx-connector-legacy/templates/tests/test-dataplane-readiness.yaml b/charts/tractusx-connector-legacy/templates/tests/test-dataplane-readiness.yaml deleted file mode 100644 index 48beeff34..000000000 --- a/charts/tractusx-connector-legacy/templates/tests/test-dataplane-readiness.yaml +++ /dev/null @@ -1,36 +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 -# - ---- -apiVersion: v1 -kind: Pod -metadata: - name: "{{include "txdc.fullname" .}}test-dataplane-readiness" - labels: - {{- include "txdc.dataplane.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test - "helm.sh/hook-delete-policy": {{ .Values.tests.hookDeletePolicy }} -spec: - containers: - - name: wget - image: curlimages/curl - command: [ 'curl' ] - args: [ '{{- printf "http://%s-dataplane:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.dataplane.endpoints.default.port $.Values.dataplane.endpoints.default.path -}}' ] - restartPolicy: Never diff --git a/charts/tractusx-connector-legacy/values.yaml b/charts/tractusx-connector-legacy/values.yaml deleted file mode 100644 index 4e6902ce5..000000000 --- a/charts/tractusx-connector-legacy/values.yaml +++ /dev/null @@ -1,561 +0,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 -# - - ---- -# Default values for eclipse-dataspace-connector. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -install: - daps: true - postgresql: true - vault: true -fullnameOverride: "" -nameOverride: "" -# -- 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) -imagePullSecrets: [] -customLabels: {} - -participant: - id: "" - -controlplane: - image: - # -- Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically - repository: "" - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use - pullPolicy: IfNotPresent - # -- Overrides the image tag whose default is the chart appVersion - tag: "" - initContainers: [] - debug: - enabled: false - port: 1044 - suspendOnStart: false - internationalDataSpaces: - id: TXDC - description: Tractus-X Eclipse IDS Data Space Connector - title: "" - maintainer: "" - curator: "" - catalogId: TXDC-Catalog - livenessProbe: - # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - # -- seconds to wait before performing the first liveness check - initialDelaySeconds: 30 - # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds - periodSeconds: 10 - # -- number of seconds after which the probe times out - timeoutSeconds: 5 - # -- when a probe fails kubernetes will try 6 times before giving up - failureThreshold: 6 - # -- number of consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - readinessProbe: - # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - # -- seconds to wait before performing the first readiness check - initialDelaySeconds: 30 - # -- this fields specifies that kubernetes should perform a readiness check every 10 seconds - periodSeconds: 10 - # -- number of seconds after which the probe times out - timeoutSeconds: 5 - # -- when a probe fails kubernetes will try 6 times before giving up - failureThreshold: 6 - # -- number of consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - # -- endpoints of the control plane - endpoints: - # -- default api for health checks, should not be added to any ingress - default: - # -- port for incoming api calls - port: 8080 - # -- path for incoming api calls - path: /api - # -- data management api, used by internal users, can be added to an ingress and must not be internet facing - management: - # -- port for incoming api calls - port: 8081 - # -- path for incoming api calls - path: /management - # -- authentication key, must be attached to each 'X-Api-Key' request header - authKey: "" - # -- control api, used for internal control calls. can be added to the internal ingress, but should probably not - control: - # -- port for incoming api calls - port: 8083 - # -- path for incoming api calls - path: /control - # -- ids api, used for inter connector communication and must be internet facing - protocol: - # -- port for incoming api calls - port: 8084 - # -- path for incoming api calls - path: /api/v1/dsp - # -- metrics api, used for application metrics, must not be internet facing - metrics: - # -- port for incoming api calls - port: 9090 - # -- path for incoming api calls - path: /metrics - - businessPartnerValidation: - log: - agreementValidation: true - 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. - type: ClusterIP - annotations: {} - # -- additional labels for the pod - podLabels: {} - # -- additional annotations for the pod - podAnnotations: {} - # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment - podSecurityContext: - seccompProfile: - # -- Restrict a Container's Syscalls with seccomp - type: RuntimeDefault - # -- Runs all processes within a pod with a special uid - runAsUser: 10001 - # -- Processes within a pod will belong to this guid - runAsGroup: 10001 - # -- The owner for volumes and any files created within volumes will belong to this guid - fsGroup: 10001 - # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod - securityContext: - capabilities: - # -- Specifies which capabilities to drop to reduce syscall attack surface - drop: - - ALL - # -- Specifies which capabilities to add to issue specialized syscalls - add: [] - # -- Whether the root filesystem is mounted in read-only mode - readOnlyRootFilesystem: true - # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID - allowPrivilegeEscalation: false - # -- Requires the container to run without root privileges - runAsNonRoot: true - # -- The container's process will run with the specified uid - runAsUser: 10001 - # Extra environment variables that will be pass onto deployment pods - env: {} - # ENV_NAME: value - - # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. - # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core - envValueFrom: {} - # ENV_NAME: - # configMapKeyRef: - # name: configmap-name - # key: value_key - # secretKeyRef: - # name: secret-name - # key: value_key - - # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from - envSecretNames: [] - # - first-secret - # - second-secret - - # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from - envConfigMapNames: [] - # - first-config-map - # - second-config-map - - ## Ingress declaration to expose the network service. - ingresses: - ## Public / Internet facing Ingress - - enabled: false - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-control.local" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - protocol - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - ## Private / Intranet facing Ingress - - enabled: false - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-control.intranet" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - management - - control - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container - volumeMounts: [] - # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories - volumes: [] - # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container - resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - replicaCount: 1 - autoscaling: - # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) - enabled: false - # -- Minimal replicas if resource consumption falls below resource threshholds - minReplicas: 1 - # -- Maximum replicas if resource consumption exceeds resource threshholds - maxReplicas: 100 - # -- targetAverageUtilization of cpu provided to a pod - targetCPUUtilizationPercentage: 80 - # -- targetAverageUtilization of memory provided to a pod - targetMemoryUtilizationPercentage: 80 - # -- configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics - opentelemetry: |- - otel.javaagent.enabled=false - otel.javaagent.debug=false - # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) - logging: |- - .level=INFO - org.eclipse.edc.level=ALL - handlers=java.util.logging.ConsoleHandler - java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter - java.util.logging.ConsoleHandler.level=ALL - java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n - # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes - nodeSelector: {} - # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes - tolerations: [] - # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on - affinity: {} - url: - # -- Explicitly declared url for reaching the ids api (e.g. if ingresses not used) - ids: "" -dataplane: - image: - # -- Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically - repository: "" - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use - pullPolicy: IfNotPresent - # -- Overrides the image tag whose default is the chart appVersion - tag: "" - initContainers: [] - debug: - enabled: false - port: 1044 - suspendOnStart: false - livenessProbe: - # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - # -- seconds to wait before performing the first liveness check - initialDelaySeconds: 30 - # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds - periodSeconds: 10 - # -- number of seconds after which the probe times out - timeoutSeconds: 5 - # -- when a probe fails kubernetes will try 6 times before giving up - failureThreshold: 6 - # -- number of consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - readinessProbe: - # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - # -- seconds to wait before performing the first readiness check - initialDelaySeconds: 30 - # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds - periodSeconds: 10 - # -- number of seconds after which the probe times out - timeoutSeconds: 5 - # -- when a probe fails kubernetes will try 6 times before giving up - failureThreshold: 6 - # -- number of consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - 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. - type: ClusterIP - port: 80 - endpoints: - default: - port: 8080 - path: /api - public: - port: 8081 - path: /api/public - control: - port: 8083 - path: /api/dataplane/control - proxy: - port: 8186 - path: /proxy - metrics: - port: 9090 - path: /metrics - aws: - endpointOverride: "" - accessKeyId: "" - secretAccessKey: "" - # -- additional labels for the pod - podLabels: {} - # -- additional annotations for the pod - podAnnotations: {} - # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment - podSecurityContext: - seccompProfile: - # -- Restrict a Container's Syscalls with seccomp - type: RuntimeDefault - # -- Runs all processes within a pod with a special uid - runAsUser: 10001 - # -- Processes within a pod will belong to this guid - runAsGroup: 10001 - # -- The owner for volumes and any files created within volumes will belong to this guid - fsGroup: 10001 - # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod - securityContext: - capabilities: - # -- Specifies which capabilities to drop to reduce syscall attack surface - drop: - - ALL - # -- Specifies which capabilities to add to issue specialized syscalls - add: [] - # -- Whether the root filesystem is mounted in read-only mode - readOnlyRootFilesystem: true - # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID - allowPrivilegeEscalation: false - # -- Requires the container to run without root privileges - runAsNonRoot: true - # -- The container's process will run with the specified uid - runAsUser: 10001 - # Extra environment variables that will be pass onto deployment pods - env: {} - # ENV_NAME: value - - # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. - # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core - envValueFrom: {} - # ENV_NAME: - # configMapKeyRef: - # name: configmap-name - # key: value_key - # secretKeyRef: - # name: secret-name - # key: value_key - - # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from - envSecretNames: [] - # - first-secret - # - second-secret - - # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from - envConfigMapNames: [] - # - first-config-map - # - second-config-map - - ## Ingress declaration to expose the network service. - ingresses: - ## Public / Internet facing Ingress - - enabled: false - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-data.local" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - public - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container - volumeMounts: [] - # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories - volumes: [] - # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container - resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - replicaCount: 1 - autoscaling: - # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) - enabled: false - # -- Minimal replicas if resource consumption falls below resource threshholds - minReplicas: 1 - # -- Maximum replicas if resource consumption exceeds resource threshholds - maxReplicas: 100 - # -- targetAverageUtilization of cpu provided to a pod - targetCPUUtilizationPercentage: 80 - # -- targetAverageUtilization of memory provided to a pod - targetMemoryUtilizationPercentage: 80 - # -- configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics - opentelemetry: |- - otel.javaagent.enabled=false - otel.javaagent.debug=false - # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) - logging: |- - .level=INFO - org.eclipse.edc.level=ALL - handlers=java.util.logging.ConsoleHandler - java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter - java.util.logging.ConsoleHandler.level=ALL - java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n - # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes - nodeSelector: {} - # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes - tolerations: [] - # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on - affinity: {} - url: - # -- Explicitly declared url for reaching the public api (e.g. if ingresses not used) - public: "" -postgresql: - jdbcUrl: "jdbc:postgresql://{{ .Release.Name }}-postgresql:5432/edc" - primary: - persistence: - enabled: false - readReplicas: - persistence: - enabled: false - auth: - database: "edc" - username: "user" - password: "password" -vault: - injector: - enabled: false - server: - dev: - enabled: true - devRootToken: "root" - # Must be the same certificate that is configured in section 'daps' - postStart: # must be set externally! - hashicorp: - url: "http://{{ .Release.Name }}-vault:8200" - token: "" - timeout: 30 - healthCheck: - enabled: true - standbyOk: true - paths: - secret: /v1/secret - health: /v1/sys/health - secretNames: - transferProxyTokenSignerPrivateKey: transfer-proxy-token-signer-private-key - transferProxyTokenSignerPublicKey: transfer-proxy-token-signer-public-key - transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key - dapsPrivateKey: daps-private-key - dapsPublicKey: daps-public-key -daps: - url: "http://{{ .Release.Name }}-daps:4567" - clientId: "" - paths: - jwks: /jwks.json - token: /token - connectors: - - id: E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65 - name: sokrates - attributes: - referringConnector: http://sokrates-controlplane/BPNSOKRATES - # Must be the same certificate that is stores in section 'sokrates-vault' - certificate: "" # must be set externally! -backendService: - httpProxyTokenReceiverUrl: "" - -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 - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - 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: [] -idsdaps: - connectors: - - certificate: |- - -# -- Configurations for Helm tests -tests: - # -- Configure the hook-delete-policy for Helm tests - hookDeletePolicy: before-hook-creation,hook-succeeded diff --git a/charts/tractusx-connector-memory/Chart.yaml b/charts/tractusx-connector-memory/Chart.yaml index 4ae4cf1c9..c77ee4ca3 100644 --- a/charts/tractusx-connector-memory/Chart.yaml +++ b/charts/tractusx-connector-memory/Chart.yaml @@ -34,12 +34,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.5.0 +version: 0.5.1 # 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.5.0" +appVersion: "0.5.1" 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/LICENSE b/charts/tractusx-connector-memory/LICENSE new file mode 100644 index 000000000..c815b0d05 --- /dev/null +++ b/charts/tractusx-connector-memory/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Catena-X + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/tractusx-connector-memory/README.md b/charts/tractusx-connector-memory/README.md index dfd40312e..b36dec5d8 100644 --- a/charts/tractusx-connector-memory/README.md +++ b/charts/tractusx-connector-memory/README.md @@ -1,6 +1,6 @@ # tractusx-connector-memory -![Version: 0.5.0](https://img.shields.io/badge/Version-0.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.0](https://img.shields.io/badge/AppVersion-0.5.0-informational?style=flat-square) +![Version: 0.5.1](https://img.shields.io/badge/Version-0.5.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.1](https://img.shields.io/badge/AppVersion-0.5.1-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.5.0 \ +helm install my-release tractusx-edc/tractusx-connector-memory --version 0.5.1 \ -f /tractusx-connector-memory-test.yaml \ --set vault.secrets="client-secret:$YOUR_CLIENT_SECRET" ``` @@ -53,7 +53,8 @@ helm install my-release tractusx-edc/tractusx-connector-memory --version 0.5.0 \ | Key | Type | Default | Description | |-----|------|---------|-------------| | backendService.httpProxyTokenReceiverUrl | string | `""` | | -| customLabels | object | `{}` | | +| customCaCerts | object | `{}` | Add custom ca certificates to the truststore | +| customLabels | object | `{}` | To add some custom labels | | daps.clientId | string | `""` | | | daps.connectors[0].attributes.referringConnector | string | `"http://sokrates-controlplane/BPNSOKRATES"` | | | daps.connectors[0].certificate | string | `""` | | @@ -65,7 +66,7 @@ helm install my-release tractusx-edc/tractusx-connector-memory --version 0.5.0 \ | fullnameOverride | string | `""` | | | 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 | `""` | | +| participant.id | string | `""` | BPN Number | | runtime.affinity | object | `{}` | | | runtime.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/) | | runtime.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | @@ -87,7 +88,7 @@ helm install my-release tractusx-edc/tractusx-connector-memory --version 0.5.0 \ | runtime.endpoints.management.authKey | string | `""` | authentication key, must be attached to each 'X-Api-Key' request header | | runtime.endpoints.management.path | string | `"/management"` | path for incoming api calls | | runtime.endpoints.management.port | int | `8081` | port for incoming api calls | -| runtime.endpoints.protocol | object | `{"path":"/api/v1/dsp","port":8084}` | ids api, used for inter connector communication and must be internet facing | +| 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.validation | object | `{"path":"/validation","port":8082}` | validation api, only used by the data plane and should not be added to any ingress | @@ -121,12 +122,6 @@ helm install my-release tractusx-edc/tractusx-connector-memory --version 0.5.0 \ | runtime.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | | runtime.ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | | runtime.initContainers | list | `[]` | | -| runtime.internationalDataSpaces.catalogId | string | `"TXDC-Catalog"` | | -| runtime.internationalDataSpaces.curator | string | `""` | | -| runtime.internationalDataSpaces.description | string | `"Tractus-X Eclipse IDS Data Space Connector"` | | -| runtime.internationalDataSpaces.id | string | `"TXDC"` | | -| runtime.internationalDataSpaces.maintainer | string | `""` | | -| runtime.internationalDataSpaces.title | string | `""` | | | runtime.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | | runtime.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | | runtime.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | @@ -158,13 +153,13 @@ helm install my-release tractusx-edc/tractusx-connector-memory --version 0.5.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 | `""` | | -| runtime.ssi.miw.url | string | `""` | | -| runtime.ssi.oauth.client.id | string | `""` | | -| runtime.ssi.oauth.client.secretAlias | string | `"client-secret"` | | -| runtime.ssi.oauth.tokenurl | string | `""` | | +| 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.tolerations | list | `[]` | | -| runtime.url.ids | string | `""` | Explicitly declared url for reaching the ids api (e.g. if ingresses not used) | +| runtime.url.protocol | string | `""` | Explicitly declared url for reaching the dsp api (e.g. if ingresses not used) | | runtime.url.public | string | `""` | | | runtime.url.readiness | string | `""` | | | runtime.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | diff --git a/charts/tractusx-connector-memory/templates/_helpers.tpl b/charts/tractusx-connector-memory/templates/_helpers.tpl index bbe52eb41..9788b6641 100644 --- a/charts/tractusx-connector-memory/templates/_helpers.tpl +++ b/charts/tractusx-connector-memory/templates/_helpers.tpl @@ -83,12 +83,12 @@ Create the name of the service account to use {{- end }} {{/* -Control IDS URL +Control DSP URL */}} {{- define "txdc.runtime.url.protocol" -}} -{{- if .Values.runtime.url.protocol }}{{/* if ids api url has been specified explicitly */}} +{{- if .Values.runtime.url.protocol }}{{/* if dsp api url has been specified explicitly */}} {{- .Values.runtime.url.protocol }} -{{- else }}{{/* else when ids api url has not been specified explicitly */}} +{{- else }}{{/* else when dsp api url has not been specified explicitly */}} {{- with (index .Values.runtime.ingresses 0) }} {{- if .enabled }}{{/* if ingress enabled */}} {{- if .tls.enabled }}{{/* if TLS enabled */}} diff --git a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml index 5ba60f8bf..4df159470 100644 --- a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml @@ -53,8 +53,40 @@ spec: serviceAccountName: {{ include "txdc.serviceAccountName" . }} securityContext: {{- toYaml .Values.runtime.podSecurityContext | nindent 8 }} + {{- if or .Values.runtime.initContainers .Values.customCaCerts }} initContainers: + {{- if .Values.runtime.initContainers }} {{- toYaml .Values.runtime.initContainers | nindent 8 }} + {{- end }} + {{- if .Values.customCaCerts }} + - name: custom-cacerts + # either use the specified image, or use the default one + {{- if .Values.runtime.image.repository }} + image: "{{ .Values.runtime.image.repository }}:{{ .Values.runtime.image.tag | default .Chart.AppVersion }}" + {{- else }} + image: "tractusx/edc-runtime-memory:{{ .Values.runtime.image.tag | default .Chart.AppVersion }}" + {{- end }} + imagePullPolicy: {{ .Values.runtime.image.pullPolicy }} + command: + - /bin/sh + - -c + - | + cp /opt/java/openjdk/lib/security/cacerts /workdir/ + find /cacerts -type f \( -iname \*.crt -o -iname \*.pem \) -exec echo "{}" \; | while read PEM_FILE_PATH; do + PEM_FILE=${PEM_FILE_PATH##*/} + ALIAS=${PEM_FILE%.*} + echo "adding ${PEM_FILE} with alias ${ALIAS} to cacerts ..." + keytool -import -noprompt -trustcacerts -alias ${ALIAS} -file ${PEM_FILE_PATH} -keystore /workdir/cacerts -storepass changeit + done + securityContext: + {{- toYaml .Values.runtime.securityContext | nindent 12 }} + volumeMounts: + - name: custom-cacertificates + mountPath: /cacerts + - name: custom-cacerts + mountPath: /workdir + {{- end }} + {{- end }} containers: - name: {{ .Chart.Name }} securityContext: @@ -276,6 +308,11 @@ spec: - name: "configuration" mountPath: "/app/logging.properties" subPath: "logging.properties" + {{- if .Values.customCaCerts }} + - name: custom-cacerts + mountPath: /opt/java/openjdk/lib/security/cacerts + subPath: cacerts + {{- end }} - name: "tmp" mountPath: "/tmp" volumes: @@ -285,6 +322,15 @@ spec: items: - key: "logging.properties" path: "logging.properties" + {{- if .Values.customCaCerts }} + - name: custom-cacertificates + configMap: + name: {{ include "txdc.fullname" . }}-custom-cacerts + defaultMode: 0400 + - name: custom-cacerts + emptyDir: + sizeLimit: 1Mi + {{- end }} - name: "tmp" emptyDir: { } {{- with .Values.runtime.nodeSelector }} diff --git a/charts/tractusx-connector-memory/templates/service-runtime.yaml b/charts/tractusx-connector-memory/templates/service-runtime.yaml index 572e51068..016b7effa 100644 --- a/charts/tractusx-connector-memory/templates/service-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/service-runtime.yaml @@ -40,16 +40,16 @@ spec: protocol: TCP name: control - port: {{ .Values.runtime.endpoints.management.port }} - targetPort: data + targetPort: management protocol: TCP - name: data + name: management - port: {{ .Values.runtime.endpoints.validation.port }} targetPort: validation protocol: TCP name: validation - port: {{ .Values.runtime.endpoints.protocol.port }} - targetPort: ids + targetPort: protocol protocol: TCP - name: ids + name: protocol selector: {{- include "txdc.runtime.selectorLabels" . | nindent 4 }} diff --git a/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml b/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml deleted file mode 100644 index 88ab588e1..000000000 --- a/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml +++ /dev/null @@ -1,39 +0,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 -# - ---- -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "txdc.fullname" . }}-test-readiness" - labels: - {{- include "txdc.runtime.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test - "helm.sh/hook-delete-policy": {{ .Values.tests.hookDeletePolicy }} -spec: - containers: - - name: wget - image: curlimages/curl - command: [ 'curl' ] - args: [ '{{- printf "http://%s-runtime:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.runtime.endpoints.default.port $.Values.runtime.endpoints.default.path -}}' ] - restartPolicy: Never diff --git a/charts/tractusx-connector-memory/templates/tests/test-runtime.yaml b/charts/tractusx-connector-memory/templates/tests/test-runtime.yaml new file mode 100644 index 000000000..87f132235 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/tests/test-runtime.yaml @@ -0,0 +1,57 @@ +# + # 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 + # + +--- +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "txdc.fullname" . }}-test" + labels: + {{- include "txdc.runtime.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": {{ .Values.tests.hookDeletePolicy }} +spec: + containers: + - name: readiness + image: curlimages/curl + command: [ 'curl', '--fail' ] + args: [ '{{- printf "http://%s-runtime:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.runtime.endpoints.default.port $.Values.runtime.endpoints.default.path -}}' ] + {{/* Try adding a BPN Group to the store via the management API */}} + - name: mgmt-api-bpn-store + image: curlimages/curl + command: [ 'curl', '-X', 'POST', '--fail','-H','Content-Type: application/json', '-H', '{{- printf "x-api-key: %s" $.Values.runtime.endpoints.management.authKey }}', '-d', '{ + "@context": { + "tx": "https://w3id.org/tractusx/v0.0.1/ns/" + }, + "@id": "tx:BPN000001234", + "tx:groups": ["group1", "group2", "group3"] + }' ] + args: [ '{{- printf "http://%s-runtime:%v%s/business-partner-groups" (include "txdc.fullname" $ ) $.Values.runtime.endpoints.management.port $.Values.runtime.endpoints.management.path -}}' ] + restartPolicy: Never + securityContext: + fsGroup: 101 # curl_group + runAsGroup: 101 # curl_group + runAsNonRoot: true + runAsUser: 100 # curl_user + seccompProfile: + type: RuntimeDefault diff --git a/charts/tractusx-connector-memory/values.yaml b/charts/tractusx-connector-memory/values.yaml index 69b548d3a..d22884373 100644 --- a/charts/tractusx-connector-memory/values.yaml +++ b/charts/tractusx-connector-memory/values.yaml @@ -27,11 +27,16 @@ fullnameOverride: "" nameOverride: "" # -- 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) imagePullSecrets: [] +# -- To add some custom labels customLabels: {} participant: + # -- BPN Number id: "" +# -- Add custom ca certificates to the truststore +customCaCerts: {} + runtime: image: repository: "" @@ -44,13 +49,6 @@ runtime: enabled: false port: 1044 suspendOnStart: false - internationalDataSpaces: - id: TXDC - description: Tractus-X Eclipse IDS Data Space Connector - title: "" - maintainer: "" - curator: "" - catalogId: TXDC-Catalog livenessProbe: # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) enabled: true @@ -105,7 +103,7 @@ runtime: port: 8083 # -- path for incoming api calls path: /control - # -- ids api, used for inter connector communication and must be internet facing + # -- dsp api, used for inter connector communication and must be internet facing protocol: # -- port for incoming api calls port: 8084 @@ -120,15 +118,21 @@ runtime: businessPartnerValidation: log: agreementValidation: true + # 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" service: @@ -283,8 +287,8 @@ runtime: # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on affinity: {} url: - # -- Explicitly declared url for reaching the ids api (e.g. if ingresses not used) - ids: "" + # -- Explicitly declared url for reaching the dsp api (e.g. if ingresses not used) + protocol: "" public: "" readiness: "" vault: diff --git a/charts/tractusx-connector/Chart.yaml b/charts/tractusx-connector/Chart.yaml index d06287e82..81ac62198 100644 --- a/charts/tractusx-connector/Chart.yaml +++ b/charts/tractusx-connector/Chart.yaml @@ -40,12 +40,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.5.0 +version: 0.5.1 # 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.5.0" +appVersion: "0.5.1" 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 diff --git a/charts/tractusx-connector/LICENSE b/charts/tractusx-connector/LICENSE new file mode 100644 index 000000000..c815b0d05 --- /dev/null +++ b/charts/tractusx-connector/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Catena-X + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/tractusx-connector/README.md b/charts/tractusx-connector/README.md index 545e28fc3..66f0ce0a0 100644 --- a/charts/tractusx-connector/README.md +++ b/charts/tractusx-connector/README.md @@ -1,6 +1,6 @@ # tractusx-connector -![Version: 0.5.0](https://img.shields.io/badge/Version-0.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.0](https://img.shields.io/badge/AppVersion-0.5.0-informational?style=flat-square) +![Version: 0.5.1](https://img.shields.io/badge/Version-0.5.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.1](https://img.shields.io/badge/AppVersion-0.5.1-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.5.0 \ +helm install my-release tractusx-edc/tractusx-connector --version 0.5.1 \ -f /tractusx-connector-test.yaml ``` @@ -86,7 +86,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.5.0 \ | controlplane.endpoints.metrics | object | `{"path":"/metrics","port":9090}` | metrics api, used for application metrics, must not be internet facing | | controlplane.endpoints.metrics.path | string | `"/metrics"` | path for incoming api calls | | controlplane.endpoints.metrics.port | int | `9090` | port for incoming api calls | -| controlplane.endpoints.protocol | object | `{"path":"/api/v1/dsp","port":8084}` | ids api, used for inter connector communication and must be internet facing | +| controlplane.endpoints.protocol | object | `{"path":"/api/v1/dsp","port":8084}` | dsp api, used for inter connector communication and must be internet facing | | controlplane.endpoints.protocol.path | string | `"/api/v1/dsp"` | path for incoming api calls | | controlplane.endpoints.protocol.port | int | `8084` | port for incoming api calls | | controlplane.env | object | `{}` | | @@ -117,12 +117,6 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.5.0 \ | controlplane.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | | controlplane.ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | | controlplane.initContainers | list | `[]` | | -| controlplane.internationalDataSpaces.catalogId | string | `"TXDC-Catalog"` | | -| controlplane.internationalDataSpaces.curator | string | `""` | | -| controlplane.internationalDataSpaces.description | string | `"Tractus-X Eclipse IDS Data Space Connector"` | | -| controlplane.internationalDataSpaces.id | string | `"TXDC"` | | -| controlplane.internationalDataSpaces.maintainer | string | `""` | | -| controlplane.internationalDataSpaces.title | string | `""` | | | controlplane.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | | controlplane.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | | controlplane.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | @@ -155,16 +149,17 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.5.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 | `""` | | -| controlplane.ssi.miw.url | string | `""` | | -| controlplane.ssi.oauth.client.id | string | `""` | | -| controlplane.ssi.oauth.client.secretAlias | string | `"client-secret"` | | -| controlplane.ssi.oauth.tokenurl | string | `""` | | +| 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.ids | string | `""` | Explicitly declared url for reaching the ids api (e.g. if ingresses not used) | +| controlplane.url.protocol | string | `""` | Explicitly declared url for reaching the dsp api (e.g. if ingresses not used) | | controlplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | | controlplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | -| customLabels | object | `{}` | | +| customCaCerts | object | `{}` | Add custom ca certificates to the truststore | +| customLabels | object | `{}` | To add some custom labels | | dataplane.affinity | object | `{}` | | | dataplane.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/) | | dataplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | @@ -251,7 +246,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.5.0 \ | 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 | `""` | | +| participant.id | string | `""` | BPN Number | | postgresql.auth.database | string | `"edc"` | | | postgresql.auth.password | string | `"password"` | | | postgresql.auth.username | string | `"user"` | | diff --git a/charts/tractusx-connector/templates/_helpers.tpl b/charts/tractusx-connector/templates/_helpers.tpl index 701e6fc75..c579ca6d6 100644 --- a/charts/tractusx-connector/templates/_helpers.tpl +++ b/charts/tractusx-connector/templates/_helpers.tpl @@ -108,12 +108,12 @@ Create the name of the service account to use {{- end }} {{/* -Control IDS URL +Control DSP URL */}} {{- define "txdc.controlplane.url.protocol" -}} -{{- if .Values.controlplane.url.protocol }}{{/* if ids api url has been specified explicitly */}} +{{- if .Values.controlplane.url.protocol }}{{/* if dsp api url has been specified explicitly */}} {{- .Values.controlplane.url.protocol }} -{{- else }}{{/* else when ids api url has not been specified explicitly */}} +{{- else }}{{/* else when dsp api url has not been specified explicitly */}} {{- with (index .Values.controlplane.ingresses 0) }} {{- if .enabled }}{{/* if ingress enabled */}} {{- if .tls.enabled }}{{/* if TLS enabled */}} diff --git a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/serviceaccount.yaml b/charts/tractusx-connector/templates/configmap-customcacerts.yaml similarity index 73% rename from charts/tractusx-connector-legacy/subcharts/omejdn/templates/serviceaccount.yaml rename to charts/tractusx-connector/templates/configmap-customcacerts.yaml index 536f31871..2a6912957 100644 --- a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/serviceaccount.yaml +++ b/charts/tractusx-connector/templates/configmap-customcacerts.yaml @@ -1,3 +1,4 @@ +# # Copyright (c) 2023 Contributors to the Eclipse Foundation # # See the NOTICE file(s) distributed with this work for additional @@ -15,17 +16,15 @@ # # SPDX-License-Identifier: Apache-2.0 # - -{{- if .Values.serviceAccount.create -}} +{{- if .Values.customCaCerts }} --- apiVersion: v1 -kind: ServiceAccount +kind: ConfigMap metadata: - name: {{ include "omejdn.serviceAccountName" . }} + name: {{ include "txdc.fullname" . }}-custom-cacerts + namespace: {{ .Release.Namespace | default "default" | quote }} labels: - {{- include "omejdn.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} + {{- include "txdc.labels" . | nindent 4 }} +data: + {{- .Values.customCaCerts | toYaml | nindent 2 }} {{- end }} diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index bd4e892ae..830bf349f 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -53,8 +53,40 @@ spec: serviceAccountName: {{ include "txdc.serviceAccountName" . }} securityContext: {{- toYaml .Values.controlplane.podSecurityContext | nindent 8 }} + {{- if or .Values.controlplane.initContainers .Values.customCaCerts }} initContainers: + {{- if .Values.controlplane.initContainers }} {{- toYaml .Values.controlplane.initContainers | nindent 8 }} + {{- end }} + {{- if .Values.customCaCerts }} + - name: custom-cacerts + # either use the specified image, or use the default one + {{- if .Values.controlplane.image.repository }} + image: "{{ .Values.controlplane.image.repository }}:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + {{- else }} + image: "tractusx/edc-controlplane-postgresql-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + {{- end }} + imagePullPolicy: {{ .Values.controlplane.image.pullPolicy }} + command: + - /bin/sh + - -c + - | + cp /opt/java/openjdk/lib/security/cacerts /workdir/ + find /cacerts -type f \( -iname \*.crt -o -iname \*.pem \) -exec echo "{}" \; | while read PEM_FILE_PATH; do + PEM_FILE=${PEM_FILE_PATH##*/} + ALIAS=${PEM_FILE%.*} + echo "adding ${PEM_FILE} with alias ${ALIAS} to cacerts ..." + keytool -import -noprompt -trustcacerts -alias ${ALIAS} -file ${PEM_FILE_PATH} -keystore /workdir/cacerts -storepass changeit + done + securityContext: + {{- toYaml .Values.controlplane.securityContext | nindent 12 }} + volumeMounts: + - name: custom-cacertificates + mountPath: /cacerts + - name: custom-cacerts + mountPath: /workdir + {{- end }} + {{- end }} containers: - name: {{ .Chart.Name }} securityContext: @@ -228,6 +260,16 @@ spec: - name: "EDC_DATASOURCE_EDR_URL" value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/bpn-validation/business-partner-store-sql + - name: "EDC_DATASOURCE_BPN_NAME" + value: "bpn" + - name: "EDC_DATASOURCE_BPN_USER" + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} + - name: "EDC_DATASOURCE_BPN_PASSWORD" + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} + - name: "EDC_DATASOURCE_BPN_URL" + value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + ################ ## DATA PLANE ## ################ @@ -335,6 +377,11 @@ spec: - name: "configuration" mountPath: "/app/logging.properties" subPath: "logging.properties" + {{- if .Values.customCaCerts }} + - name: custom-cacerts + mountPath: /opt/java/openjdk/lib/security/cacerts + subPath: cacerts + {{- end }} - name: "tmp" mountPath: "/tmp" volumes: @@ -346,6 +393,15 @@ spec: path: "opentelemetry.properties" - key: "logging.properties" path: "logging.properties" + {{- if .Values.customCaCerts }} + - name: custom-cacertificates + configMap: + name: {{ include "txdc.fullname" . }}-custom-cacerts + defaultMode: 0400 + - name: custom-cacerts + emptyDir: + sizeLimit: 1Mi + {{- end }} - name: "tmp" emptyDir: { } {{- with .Values.controlplane.nodeSelector }} diff --git a/charts/tractusx-connector/templates/deployment-dataplane.yaml b/charts/tractusx-connector/templates/deployment-dataplane.yaml index 0b3016af1..e3aa80d09 100644 --- a/charts/tractusx-connector/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector/templates/deployment-dataplane.yaml @@ -53,8 +53,40 @@ spec: serviceAccountName: {{ include "txdc.serviceAccountName" . }} securityContext: {{- toYaml .Values.dataplane.podSecurityContext | nindent 8 }} + {{- if or .Values.dataplane.initContainers .Values.customCaCerts }} initContainers: + {{- if .Values.dataplane.initContainers }} {{- toYaml .Values.dataplane.initContainers | nindent 8 }} + {{- end }} + {{- if .Values.customCaCerts }} + - name: custom-cacerts + # either use the specified image, or use the default one + {{- if .Values.dataplane.image.repository }} + image: "{{ .Values.dataplane.image.repository }}:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + {{- else }} + image: "tractusx/edc-dataplane-hashicorp-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + {{- end }} + imagePullPolicy: {{ .Values.dataplane.image.pullPolicy }} + command: + - /bin/sh + - -c + - | + cp /opt/java/openjdk/lib/security/cacerts /workdir/ + find /cacerts -type f \( -iname \*.crt -o -iname \*.pem \) -exec echo "{}" \; | while read PEM_FILE_PATH; do + PEM_FILE=${PEM_FILE_PATH##*/} + ALIAS=${PEM_FILE%.*} + echo "adding ${PEM_FILE} with alias ${ALIAS} to cacerts ..." + keytool -import -noprompt -trustcacerts -alias ${ALIAS} -file ${PEM_FILE_PATH} -keystore /workdir/cacerts -storepass changeit + done + securityContext: + {{- toYaml .Values.dataplane.securityContext | nindent 12 }} + volumeMounts: + - name: custom-cacertificates + mountPath: /cacerts + - name: custom-cacerts + mountPath: /workdir + {{- end }} + {{- end }} containers: - name: {{ .Chart.Name }} securityContext: @@ -107,6 +139,12 @@ spec: {{- end }} {{- end }} + ######################## + ## ID CONFIGURATION ## + ######################## + - name: EDC_PARTICIPANT_ID + value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }} + ####### # API # ####### @@ -207,6 +245,11 @@ spec: - name: "configuration" mountPath: "/app/logging.properties" subPath: "logging.properties" + {{- if .Values.customCaCerts }} + - name: custom-cacerts + mountPath: /opt/java/openjdk/lib/security/cacerts + subPath: cacerts + {{- end }} - name: "tmp" mountPath: "/tmp" volumes: @@ -218,6 +261,15 @@ spec: path: "opentelemetry.properties" - key: "logging.properties" path: "logging.properties" + {{- if .Values.customCaCerts }} + - name: custom-cacertificates + configMap: + name: {{ include "txdc.fullname" . }}-custom-cacerts + defaultMode: 0400 + - name: custom-cacerts + emptyDir: + sizeLimit: 1Mi + {{- end }} - name: "tmp" emptyDir: { } {{- with .Values.dataplane.nodeSelector }} diff --git a/charts/tractusx-connector/templates/tests/test-controlplane-readiness.yaml b/charts/tractusx-connector/templates/tests/test-controlplane-readiness.yaml deleted file mode 100644 index efd6f3b1e..000000000 --- a/charts/tractusx-connector/templates/tests/test-controlplane-readiness.yaml +++ /dev/null @@ -1,36 +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 -# - ---- -apiVersion: v1 -kind: Pod -metadata: - name: "{{include "txdc.fullname" .}}test-controlplane-readiness" - labels: - {{- include "txdc.controlplane.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test - "helm.sh/hook-delete-policy": {{ .Values.tests.hookDeletePolicy }} -spec: - containers: - - name: wget - image: curlimages/curl - command: [ 'curl' ] - args: [ '{{- printf "http://%s-controlplane:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.controlplane.endpoints.default.port $.Values.controlplane.endpoints.default.path -}}' ] - restartPolicy: Never diff --git a/charts/tractusx-connector-azure-vault/templates/tests/test-controlplane-readiness.yaml b/charts/tractusx-connector/templates/tests/test-controlplane.yaml similarity index 52% rename from charts/tractusx-connector-azure-vault/templates/tests/test-controlplane-readiness.yaml rename to charts/tractusx-connector/templates/tests/test-controlplane.yaml index 01beac227..796b94fa2 100644 --- a/charts/tractusx-connector-azure-vault/templates/tests/test-controlplane-readiness.yaml +++ b/charts/tractusx-connector/templates/tests/test-controlplane.yaml @@ -21,7 +21,7 @@ apiVersion: v1 kind: Pod metadata: - name: "{{include "txdc.fullname" .}}test-controlplane-readiness" + name: "{{include "txdc.fullname" .}}-test-controlplane" labels: {{- include "txdc.controlplane.labels" . | nindent 4 }} annotations: @@ -29,8 +29,28 @@ metadata: "helm.sh/hook-delete-policy": {{ .Values.tests.hookDeletePolicy }} spec: containers: - - name: wget + {{/* Poke the pod's management API */}} + - name: readiness image: curlimages/curl - command: [ 'curl' ] + command: [ 'curl', '--fail' ] args: [ '{{- printf "http://%s-controlplane:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.controlplane.endpoints.default.port $.Values.controlplane.endpoints.default.path -}}' ] + + {{/* Try adding a BPN Group to the store via the management API */}} + - name: bpn-store + image: curlimages/curl + command: [ 'curl', '-X', 'POST', '--fail','-H','Content-Type: application/json', '-H', '{{- printf "x-api-key: %s" $.Values.controlplane.endpoints.management.authKey }}', '-d', '{ + "@context": { + "tx": "https://w3id.org/tractusx/v0.0.1/ns/" + }, + "@id": "tx:BPN000001234", + "tx:groups": ["group1", "group2", "group3"] + }' ] + args: [ '{{- printf "http://%s-controlplane:%v%s/business-partner-groups" (include "txdc.fullname" $ ) $.Values.controlplane.endpoints.management.port $.Values.controlplane.endpoints.management.path -}}' ] restartPolicy: Never + securityContext: + fsGroup: 101 # curl_group + runAsGroup: 101 # curl_group + runAsNonRoot: true + runAsUser: 100 # curl_user + seccompProfile: + type: RuntimeDefault diff --git a/charts/tractusx-connector/templates/tests/test-dataplane-readiness.yaml b/charts/tractusx-connector/templates/tests/test-dataplane-readiness.yaml index 48beeff34..fa58c7da7 100644 --- a/charts/tractusx-connector/templates/tests/test-dataplane-readiness.yaml +++ b/charts/tractusx-connector/templates/tests/test-dataplane-readiness.yaml @@ -1,21 +1,21 @@ # -# 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 -# + # 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 + # --- apiVersion: v1 @@ -31,6 +31,13 @@ spec: containers: - name: wget image: curlimages/curl - command: [ 'curl' ] + command: [ 'curl', '--fail' ] args: [ '{{- printf "http://%s-dataplane:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.dataplane.endpoints.default.port $.Values.dataplane.endpoints.default.path -}}' ] restartPolicy: Never + securityContext: + fsGroup: 101 # curl_group + runAsGroup: 101 # curl_group + runAsNonRoot: true + runAsUser: 100 # curl_user + seccompProfile: + type: RuntimeDefault diff --git a/charts/tractusx-connector/values.yaml b/charts/tractusx-connector/values.yaml index 2f4781184..b98fc645b 100644 --- a/charts/tractusx-connector/values.yaml +++ b/charts/tractusx-connector/values.yaml @@ -33,9 +33,11 @@ fullnameOverride: "" nameOverride: "" # -- 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) imagePullSecrets: [] +# -- To add some custom labels customLabels: {} participant: + # -- BPN Number id: "" controlplane: @@ -51,13 +53,6 @@ controlplane: enabled: false port: 1044 suspendOnStart: false - internationalDataSpaces: - id: TXDC - description: Tractus-X Eclipse IDS Data Space Connector - title: "" - maintainer: "" - curator: "" - catalogId: TXDC-Catalog livenessProbe: # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) enabled: true @@ -106,7 +101,7 @@ controlplane: port: 8083 # -- path for incoming api calls path: /control - # -- ids api, used for inter connector communication and must be internet facing + # -- dsp api, used for inter connector communication and must be internet facing protocol: # -- port for incoming api calls port: 8084 @@ -126,12 +121,17 @@ controlplane: # 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" service: @@ -290,8 +290,12 @@ controlplane: # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on affinity: {} url: - # -- Explicitly declared url for reaching the ids api (e.g. if ingresses not used) - ids: "" + # -- Explicitly declared url for reaching the dsp api (e.g. if ingresses not used) + protocol: "" + +# -- Add custom ca certificates to the truststore +customCaCerts: {} + dataplane: image: # -- Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically diff --git a/core/edr-cache-core/build.gradle.kts b/core/edr-cache-core/build.gradle.kts index 95b0c0be6..3521efcd0 100644 --- a/core/edr-cache-core/build.gradle.kts +++ b/core/edr-cache-core/build.gradle.kts @@ -21,9 +21,9 @@ dependencies { implementation(libs.edc.config.filesystem) implementation(libs.edc.util) - implementation(project(":spi:edr-cache-spi")) + implementation(project(":spi:edr-spi")) - testImplementation(testFixtures(project(":spi:edr-cache-spi"))) + testImplementation(testFixtures(project(":spi:edr-spi"))) } diff --git a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCacheCoreExtension.java b/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCacheCoreExtension.java index 5de7e78a5..14fd2bea0 100644 --- a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCacheCoreExtension.java +++ b/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCacheCoreExtension.java @@ -21,7 +21,7 @@ import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.tractusx.edc.edr.core.defaults.InMemoryEndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; /** * Registers default services for the EDR cache. diff --git a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/EdrCacheEntryPredicateConverter.java b/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/EdrCacheEntryPredicateConverter.java index 27346d512..23845d991 100644 --- a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/EdrCacheEntryPredicateConverter.java +++ b/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/EdrCacheEntryPredicateConverter.java @@ -14,23 +14,119 @@ package org.eclipse.tractusx.edc.edr.core.defaults; -import org.eclipse.edc.spi.query.BaseCriterionToPredicateConverter; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; +import org.eclipse.edc.spi.query.Criterion; +import org.eclipse.edc.spi.query.CriterionToPredicateConverter; +import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; +import org.jetbrains.annotations.NotNull; -public class EdrCacheEntryPredicateConverter extends BaseCriterionToPredicateConverter { +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.regex.Pattern; + +import static java.lang.String.format; + +/** + * 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 EdrCacheEntryPredicateConverter implements CriterionToPredicateConverter { @Override - protected Object property(String key, Object object) { - if (object instanceof EndpointDataReferenceEntry) { - var entry = (EndpointDataReferenceEntry) object; - switch (key) { - case "assetId": - return entry.getAssetId(); - case "agreementId": - return entry.getAgreementId(); - default: - return null; + public Predicate convert(Criterion criterion) { + var operator = criterion.getOperator().toLowerCase(); + + return switch (operator) { + case "=" -> equalPredicate(criterion); + case "in" -> inPredicate(criterion); + case "like" -> likePredicate(criterion); + default -> throw new IllegalArgumentException(format("Operator [%s] is not supported by this converter!", criterion.getOperator())); + }; + } + + @NotNull + private Predicate equalPredicate(Criterion criterion) { + return t -> { + var operandLeft = (String) criterion.getOperandLeft(); + var property = property(operandLeft, t); + if (property == null) { + return false; + } + + if (property.getClass().isEnum() && criterion.getOperandRight() instanceof String) { + var enumProperty = (Enum) property; + return Objects.equals(enumProperty.name(), criterion.getOperandRight()); + } + + if (property instanceof Number c1 && criterion.getOperandRight() instanceof Number c2) { + // interpret as double to not lose any precision + return Double.compare(c1.doubleValue(), c2.doubleValue()) == 0; + } + + if (property instanceof List list) { + return list.stream().anyMatch(it -> Objects.equals(it, criterion.getOperandRight())); + } + + return Objects.equals(property, criterion.getOperandRight()); + }; + } + + @NotNull + private Predicate inPredicate(Criterion criterion) { + return t -> { + var operandLeft = (String) criterion.getOperandLeft(); + var property = property(operandLeft, t); + if (property == null) { + return false; + } + + if (criterion.getOperandRight() instanceof Iterable iterable) { + for (var value : iterable) { + if (value.equals(property)) { + return true; + } + } + return false; + } else { + throw new IllegalArgumentException("Operator IN requires the right-hand operand to be an " + Iterable.class.getName() + " but was " + criterion.getOperandRight().getClass().getName()); + } + + + }; + } + + @NotNull + private Predicate likePredicate(Criterion criterion) { + return t -> { + var operandLeft = (String) criterion.getOperandLeft(); + var property = property(operandLeft, t); + if (property == null) { + return false; } + + if (criterion.getOperandRight() instanceof String operandRight) { + var regexPattern = Pattern.quote(operandRight) + .replace("%", "\\E.*\\Q") + .replace("_", "\\E.\\Q"); + + regexPattern = "^" + regexPattern + "$"; + + return Pattern.compile(regexPattern).matcher(property.toString()).matches(); + } + + return false; + }; + } + + protected Object property(String key, Object object) { + if (object instanceof EndpointDataReferenceEntry entry) { + return switch (key) { + case "assetId" -> entry.getAssetId(); + case "agreementId" -> entry.getAgreementId(); + case "providerId" -> entry.getProviderId(); + case "state" -> entry.getState(); + default -> null; + }; } throw new IllegalArgumentException("Can only handle objects of type " + EndpointDataReferenceEntry.class.getSimpleName() + " but received an " + object.getClass().getSimpleName()); } 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 index 0149902bf..c8c99d315 100644 --- 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 @@ -14,27 +14,35 @@ 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.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.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; +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; @@ -43,20 +51,34 @@ * An in-memory, threadsafe implementation of the cache. */ public class InMemoryEndpointDataReferenceCache implements EndpointDataReferenceCache { + private static final long DEFAULT_LEASE_TIME_MILLIS = 60_000; private final LockManager lockManager; - private final EdrCacheEntryPredicateConverter predicateConverter = new EdrCacheEntryPredicateConverter(); + 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() { + this(UUID.randomUUID().toString(), Clock.systemUTC(), new ConcurrentHashMap<>()); + } + + public InMemoryEndpointDataReferenceCache(String lockId, Clock clock, Map leases) { + this.lockId = lockId; lockManager = new LockManager(new ReentrantReadWriteLock()); entriesByAssetId = new HashMap<>(); entriesByEdrId = new ConcurrentHashMap<>(); edrsByTransferProcessId = new HashMap<>(); + this.leases = leases; + this.clock = clock; } @Override @@ -64,14 +86,48 @@ public InMemoryEndpointDataReferenceCache() { return lockManager.readLock(() -> edrsByTransferProcessId.get(transferProcessId)); } + @Override + public @Nullable 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 + public StoreResult findByCorrelationIdAndLease(String correlationId) { + return findByIdAndLease(correlationId); + } + + @Override + public void save(EndpointDataReferenceEntry entity) { + throw new UnsupportedOperationException("Please use save(EndpointDataReferenceEntry, EndpointDataReference) instead!"); + } + @Override @NotNull - public List referencesForAsset(String assetId) { - var entries = entriesByAssetId.get(assetId); - if (entries == null) { - return emptyList(); - } - return entries.stream().map(e -> resolveReference(e.getTransferProcessId())).filter(Objects::nonNull).collect(toList()); + 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 @@ -91,9 +147,26 @@ public void save(EndpointDataReferenceEntry entry, EndpointDataReference edr) { }); } + @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); @@ -109,12 +182,55 @@ public StoreResult deleteByTransferProcessId(String }); } + @Override + public @NotNull List nextNotLeased(int max, Criterion... criteria) { + return leaseAndGet(max, criteria); + } + + + private @NotNull List leaseAndGet(int max, Criterion... criteria) { + return lockManager.writeLock(() -> { + var filterPredicate = Arrays.stream(criteria).map(predicateConverter::convert).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) { - var predicate = criteria.stream() - .map(predicateConverter::convert) - .reduce(x -> true, Predicate::and); + return lockManager.readLock(() -> { + var predicate = criteria.stream() + .map(predicateConverter::convert) + .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()); + } - return entriesByEdrId.values().stream() - .filter(predicate); + 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 index 3a9ae9ebd..9c31b141b 100644 --- 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 @@ -16,7 +16,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; +import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; /** * A wrapper to persist {@link EndpointDataReferenceEntry}s and {@link EndpointDataReference}s. 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 index 7fb287716..314224adc 100644 --- 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 @@ -14,15 +14,42 @@ package org.eclipse.tractusx.edc.edr.core.defaults; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCacheBaseTest; +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; -class InMemoryEndpointDataReferenceCacheTest extends EndpointDataReferenceCacheBaseTest { - private final InMemoryEndpointDataReferenceCache cache = new InMemoryEndpointDataReferenceCache(); +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() { + cache = new InMemoryEndpointDataReferenceCache(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 index b17c1e524..22fc0af8f 100644 --- 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 @@ -17,7 +17,7 @@ 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.EndpointDataReferenceEntry; +import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; import org.junit.jupiter.api.Test; import static java.util.UUID.randomUUID; @@ -39,6 +39,7 @@ void verify_serializeDeserialize() throws JsonProcessingException { .assetId(randomUUID().toString()) .agreementId(randomUUID().toString()) .transferProcessId(randomUUID().toString()) + .providerId(randomUUID().toString()) .build(); var serialized = mapper.writeValueAsString(new PersistentCacheEntry(edrEntry, edr)); diff --git a/core/edr-core/README.md b/core/edr-core/README.md new file mode 100644 index 000000000..aeb2e2402 --- /dev/null +++ b/core/edr-core/README.md @@ -0,0 +1,26 @@ +# EDR core extension + +This extension provide a base implementation of `EdrManager` and `EdrService` both +required for interacting with the EDR APIs and state machine + +The EDR state machine handle the lifecycle of a negotiated EDR. The negotiation request can be submitted +via EDR APIs, and it will go through two phases: + +- Contract Negotiation +- Transfer Request + +Once the latter has completed the EDR entry will be saved with the associated EDR in the primordial state `NEGOTIATED` +The state machine will also manage the lifecycle and the renewal of the `EDR`. If a token is about to expire it will +transition to the `REFRESHING` state and fire off another transfer process with the same parameter of the expiring +one. Once completed the new `EDR` will be cached and the old ones, with same `assetId` and `agreementId` will transition +into the `EXPIRED` state. Then the state machine will also monitor the `EXPIRED` ones, and will delete them according to the +retention configuration. + +## 1. EDR state machine Configuration + +| Key | Description | Mandatory | Default | +|:--------------------------------------------|:----------------------------------------------------------------------------------------------------|-----------|---------| +| edc.edr.state-machine.iteration-wait-millis | The iteration wait time in milliseconds in the edr state machine | | 1000 | +| edc.edr.state-machine.batch-size | The batch size in the edr negotiation state machine | | 20 | +| edc.edr.state-machine.expiring-duration | The minimum duration on which the EDR token can be eligible for renewal (seconds) | | 60 | +| edc.edr.state-machine.expired-retention | The minimum duration on with the EDR token can be eligible for deletion when it's expired (seconds) | | 60 | diff --git a/core/edr-core/build.gradle.kts b/core/edr-core/build.gradle.kts new file mode 100644 index 000000000..c8fe53456 --- /dev/null +++ b/core/edr-core/build.gradle.kts @@ -0,0 +1,37 @@ +/* + * 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 + * + */ + +plugins { + `java-library` +} + +dependencies { + implementation(libs.edc.spi.core) + implementation(libs.edc.config.filesystem) + implementation(libs.edc.util) + implementation(libs.edc.spi.aggregateservices) + implementation(libs.edc.spi.contract) + implementation(libs.edc.spi.controlplane) + implementation(libs.edc.statemachine) + + implementation(project(":spi:edr-spi")) + implementation(project(":spi:core-spi")) + + + testImplementation(libs.edc.junit) + testImplementation(libs.awaitility) + testImplementation(testFixtures(project(":spi:edr-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 new file mode 100644 index 000000000..cf31d29c0 --- /dev/null +++ b/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCoreExtension.java @@ -0,0 +1,129 @@ +/* + * 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 + * + */ + +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 new file mode 100644 index 000000000..065eecfad --- /dev/null +++ b/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/EdrCoreServiceExtension.java @@ -0,0 +1,53 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.edr.core; + +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.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; + +/** + * Registers default services for the EDR cache. + */ +@Extension(value = EdrCoreServiceExtension.NAME) +public class EdrCoreServiceExtension implements ServiceExtension { + protected static final String NAME = "EDR Core Service extension"; + + @Inject + private Monitor monitor; + + @Inject + private EdrManager edrManager; + + @Inject + private EndpointDataReferenceCache endpointDataReferenceCache; + + @Override + public String name() { + return NAME; + } + + + @Provider + public EdrService edrService() { + return new EdrServiceImpl(edrManager, endpointDataReferenceCache); + } +} 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 new file mode 100644 index 000000000..b8df8cf26 --- /dev/null +++ b/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/manager/EdrManagerImpl.java @@ -0,0 +1,373 @@ +/* + * 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 + * + */ + +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.StateMachineManager; +import org.eclipse.edc.statemachine.StateProcessorImpl; +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.Objects; +import java.util.Set; +import java.util.function.Function; +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 StateProcessorImpl processEdrInState(EndpointDataReferenceEntryStates state, Function function) { + var filter = new Criterion[] {hasState(state.code())}; + return new StateProcessorImpl<>(() -> edrCache.nextNotLeased(batchSize, filter), telemetry.contextPropagationMiddleware(function)); + } + + + private StateProcessorImpl processDeletingEdr(Function function) { + var query = QuerySpec.Builder.newInstance() + .filter(hasState(DELETING.code())) + .limit(batchSize) + .build(); + + return new StateProcessorImpl<>(() -> 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.doSimpleProcess(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 boolean checkExpiration(EndpointDataReferenceEntry entry) { + if (shouldBeRemoved(entry)) { + transitionToDeleting(entry); + return true; + } else { + breakLease(entry); + return false; + } + } + + 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()) + .connectorId(dataRequest.getConnectorId()) + .contractId(dataRequest.getContractId()) + .protocol(dataRequest.getProtocol()) + .connectorAddress(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 new file mode 100644 index 000000000..5172395e6 --- /dev/null +++ b/core/edr-core/src/main/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImpl.java @@ -0,0 +1,74 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.edr.core.service; + +import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; +import org.eclipse.edc.service.spi.result.ServiceResult; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; +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.edr.spi.types.EndpointDataReferenceEntry; +import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static java.lang.String.format; + +public class EdrServiceImpl implements EdrService { + + private final EdrManager edrManager; + + private final EndpointDataReferenceCache endpointDataReferenceCache; + + public EdrServiceImpl(EdrManager edrManager, EndpointDataReferenceCache endpointDataReferenceCache) { + this.edrManager = edrManager; + this.endpointDataReferenceCache = endpointDataReferenceCache; + } + + @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()); + } + } + + @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))); + } + + @Override + public ServiceResult> findBy(QuerySpec querySpec) { + var results = endpointDataReferenceCache.queryForEntries(querySpec).collect(Collectors.toList()); + return ServiceResult.success(results); + } + + @Override + public ServiceResult deleteByTransferProcessId(String transferProcessId) { + var deleted = endpointDataReferenceCache.deleteByTransferProcessId(transferProcessId); + return ServiceResult.from(deleted); + } + +} diff --git a/edc-extensions/control-plane-adapter-callback/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 similarity index 74% rename from edc-extensions/control-plane-adapter-callback/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to core/edr-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 210521e83..e2c97c798 100644 --- a/edc-extensions/control-plane-adapter-callback/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 @@ -12,5 +12,5 @@ # # -org.eclipse.tractusx.edc.cp.adapter.callback.InProcessCallbackRegistryExtension -org.eclipse.tractusx.edc.cp.adapter.callback.LocalCallbackExtension +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/EdrCoreExtensionTest.java b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/EdrCoreExtensionTest.java new file mode 100644 index 000000000..95fd68665 --- /dev/null +++ b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/EdrCoreExtensionTest.java @@ -0,0 +1,47 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.edr.core; + +import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService; +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +@ExtendWith(DependencyInjectionExtension.class) +public class EdrCoreExtensionTest { + + @BeforeEach + void setUp(ServiceExtensionContext context) { + context.registerService(ContractNegotiationService.class, mock(ContractNegotiationService.class)); + context.registerService(EndpointDataReferenceCache.class, mock(EndpointDataReferenceCache.class)); + } + + @Test + void shouldInitializeTheExtension(ServiceExtensionContext context, EdrCoreExtension extension) { + extension.initialize(context); + + var service = context.getService(EdrManager.class); + assertThat(service).isInstanceOf(EdrManagerImpl.class); + + } +} 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 new file mode 100644 index 000000000..322a58d44 --- /dev/null +++ b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/EdrCoreServiceExtensionTest.java @@ -0,0 +1,46 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.edr.core; + +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); + + var service = extension.edrService(); + assertThat(service).isInstanceOf(EdrServiceImpl.class); + + } +} 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 new file mode 100644 index 000000000..9b81ca30b --- /dev/null +++ b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/fixtures/TestFunctions.java @@ -0,0 +1,50 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.edr.core.fixtures; + +import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; +import org.eclipse.edc.connector.contract.spi.types.offer.ContractOffer; +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; +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 new file mode 100644 index 000000000..cd6033559 --- /dev/null +++ b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/manager/EdrManagerImplTest.java @@ -0,0 +1,278 @@ +/* + * 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 + * + */ + +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.service.spi.result.ServiceResult; +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.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.findByCorrelationIdAndLease(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.findByCorrelationIdAndLease(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.findByCorrelationIdAndLease(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.findByCorrelationIdAndLease(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/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImplTest.java b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImplTest.java similarity index 61% rename from edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImplTest.java rename to core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImplTest.java index 69977fdfe..272e63e56 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImplTest.java +++ b/core/edr-core/src/test/java/org/eclipse/tractusx/edc/edr/core/service/EdrServiceImplTest.java @@ -12,45 +12,50 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +package org.eclipse.tractusx.edc.edr.core.service; 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.contract.spi.types.offer.ContractOffer; -import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.service.spi.result.ServiceFailure; import org.eclipse.edc.service.spi.result.ServiceResult; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.response.StatusResult; +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.tractusx.edc.edr.spi.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; +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.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 static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.cp.adapter.callback.AdapterTransferProcessServiceImpl.LOCAL_CALLBACK; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class AdapterTransferProcessServiceImplTest { +public class EdrServiceImplTest { - ContractNegotiationService contractNegotiationService = mock(ContractNegotiationService.class); + EdrManager edrManager = mock(EdrManager.class); EndpointDataReferenceCache endpointDataReferenceCache = mock(EndpointDataReferenceCache.class); + EdrServiceImpl transferService; + + @BeforeEach + void setup() { + transferService = new EdrServiceImpl(edrManager, endpointDataReferenceCache); + } + @Test void initEdrNegotiation_shouldFireContractNegotiation_WhenUsingCallbacks() { - var transferService = new AdapterTransferProcessServiceImpl(contractNegotiationService, endpointDataReferenceCache); - var captor = ArgumentCaptor.forClass(ContractRequest.class); - - when(contractNegotiationService.initiateNegotiation(any())).thenReturn(getContractNegotiation()); + when(edrManager.initiateEdrNegotiation(any())).thenReturn(StatusResult.success(getContractNegotiation())); var negotiateEdrRequest = getNegotiateEdrRequest(); @@ -59,16 +64,6 @@ void initEdrNegotiation_shouldFireContractNegotiation_WhenUsingCallbacks() { assertThat(result.succeeded()).isTrue(); assertThat(result.getContent()).isNotNull(); - verify(contractNegotiationService).initiateNegotiation(captor.capture()); - - var msg = captor.getValue(); - - assertThat(msg.getCallbackAddresses()).usingRecursiveFieldByFieldElementComparator().containsAll(negotiateEdrRequest.getCallbackAddresses()); - assertThat(msg.getCallbackAddresses()).usingRecursiveFieldByFieldElementComparator().contains(LOCAL_CALLBACK); - assertThat(msg.getRequestData().getContractOffer()).usingRecursiveComparison().isEqualTo(negotiateEdrRequest.getOffer()); - assertThat(msg.getRequestData().getProtocol()).isEqualTo(negotiateEdrRequest.getProtocol()); - assertThat(msg.getRequestData().getCounterPartyAddress()).isEqualTo(negotiateEdrRequest.getConnectorAddress()); - } @Test @@ -76,7 +71,6 @@ void findByTransferProcessId_shouldReturnTheEdr_whenFoundInCache() { var transferProcessId = "tpId"; - var transferService = new AdapterTransferProcessServiceImpl(contractNegotiationService, endpointDataReferenceCache); when(endpointDataReferenceCache.resolveReference(transferProcessId)).thenReturn(EndpointDataReference.Builder.newInstance().endpoint("test").build()); var result = transferService.findByTransferProcessId(transferProcessId); @@ -92,7 +86,6 @@ void findByTransferProcessId_shouldReturnTheEdr_whenFoundInCache() { void findByTransferProcessId_shouldNotFound_whenNotPresentInCache() { var transferProcessId = "tpId"; - var transferService = new AdapterTransferProcessServiceImpl(contractNegotiationService, endpointDataReferenceCache); when(endpointDataReferenceCache.resolveReference(transferProcessId)).thenReturn(null); var result = transferService.findByTransferProcessId(transferProcessId); @@ -104,6 +97,49 @@ void findByTransferProcessId_shouldNotFound_whenNotPresentInCache() { .isEqualTo(ServiceFailure.Reason.NOT_FOUND); } + @Test + void deleteByTransferProcessId() { + var transferProcessId = "tpId"; + + when(endpointDataReferenceCache.deleteByTransferProcessId(transferProcessId)).thenReturn(StoreResult.success(null)); + + var result = transferService.deleteByTransferProcessId(transferProcessId); + + assertThat(result) + .isNotNull() + .extracting(ServiceResult::succeeded) + .isEqualTo(true); + } + + @Test + void deleteByTransferProcessId_shouldNotFound_whenNotPresentInCache() { + var transferProcessId = "tpId"; + + when(endpointDataReferenceCache.deleteByTransferProcessId(transferProcessId)).thenReturn(StoreResult.notFound("")); + + var result = transferService.deleteByTransferProcessId(transferProcessId); + + assertThat(result) + .isNotNull() + .extracting(ServiceResult::getFailure) + .extracting(ServiceFailure::getReason) + .isEqualTo(ServiceFailure.Reason.NOT_FOUND); + } + + @Test + void queryEdrs() { + when(endpointDataReferenceCache.queryForEntries(any())).thenReturn(Stream.empty()); + + var result = transferService.findBy(QuerySpec.Builder.newInstance().build()); + + assertThat(result) + .isNotNull() + .extracting(ServiceResult::getContent) + .extracting(List::size) + .isEqualTo(0); + + } + private NegotiateEdrRequest getNegotiateEdrRequest() { return NegotiateEdrRequest.Builder.newInstance() .protocol("protocol") @@ -113,7 +149,6 @@ private NegotiateEdrRequest getNegotiateEdrRequest() { .id("id") .assetId("assetId") .policy(Policy.Builder.newInstance().build()) - .providerId("provider") .build()) .build(); } diff --git a/core/json-ld-core/build.gradle.kts b/core/json-ld-core/build.gradle.kts index 22bf99e1a..b43a2063e 100644 --- a/core/json-ld-core/build.gradle.kts +++ b/core/json-ld-core/build.gradle.kts @@ -17,6 +17,7 @@ plugins { } dependencies { + implementation(project(":spi:core-spi")) implementation(libs.edc.spi.core) implementation(libs.edc.spi.jsonld) testImplementation(testFixtures(libs.edc.junit)) 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 aab292b8e..66d33c122 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 @@ -29,10 +29,15 @@ 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_CONTEXT; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_PREFIX; public class JsonLdExtension implements ServiceExtension { public static final String CREDENTIALS_V_1 = "https://www.w3.org/2018/credentials/v1"; + public static final String CREDENTIALS_SUMMARY_V_1 = "https://w3id.org/2023/catenax/credentials/summary/v1"; public static final String CREDENTIALS_SUMMARY_V_1_FALLBACK = "https://catenax-ng.github.io/product-core-schemas/SummaryVC.json"; public static final String SECURITY_JWS_V1 = "https://w3id.org/security/suites/jws-2020/v1"; @@ -43,7 +48,9 @@ public class JsonLdExtension implements ServiceExtension { CREDENTIALS_SUMMARY_V_1, PREFIX + "summary-vc-context-v1.jsonld", CREDENTIALS_SUMMARY_V_1_FALLBACK, PREFIX + "summary-vc-context-v1.jsonld", SECURITY_JWS_V1, PREFIX + "security-jws-2020.jsonld", - SECURITY_ED25519_V1, PREFIX + "security-ed25519-2020.jsonld"); + SECURITY_ED25519_V1, PREFIX + "security-ed25519-2020.jsonld", + TX_CONTEXT, PREFIX + "tx-v1.jsonld", + EDC_CONTEXT, PREFIX + "edc-v1.jsonld"); @Inject private JsonLd jsonLdService; @@ -52,6 +59,7 @@ public class JsonLdExtension implements ServiceExtension { @Override public void initialize(ServiceExtensionContext context) { + jsonLdService.registerNamespace(TX_PREFIX, TX_NAMESPACE); FILES.entrySet().stream().map(this::mapToFile) .forEach(result -> result.onSuccess(entry -> jsonLdService.registerCachedDocument(entry.getKey(), entry.getValue())) .onFailure(failure -> monitor.warning("Failed to register cached json-ld document: " + failure.getFailureDetail()))); diff --git a/core/json-ld-core/src/main/resources/document/edc-v1.jsonld b/core/json-ld-core/src/main/resources/document/edc-v1.jsonld new file mode 100644 index 000000000..d539d3e0f --- /dev/null +++ b/core/json-ld-core/src/main/resources/document/edc-v1.jsonld @@ -0,0 +1,47 @@ +{ + "@context": { + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "@vocab": "https://w3id.org/edc/v0.0.1/ns/", + "schema": "http://schema.org/", + "PolicyDefinition": "edc:PolicyDefinition", + "AssetEntryNewDto": "edc:AssetEntryNewDto", + "DataAddress": "edc:DataAddress", + "NegotiationState": "edc:NegotiationState", + "TerminateNegotiation": "edc:TerminateNegotiation", + "ContractRequest": "edc:ContractRequest", + "policy": { + "@id": "edc:policy", + "@type": "@id", + "@context": [ + "http://www.w3.org/ns/odrl.jsonld" + ] + }, + "createdAt": "edc:createdAt", + "properties": "edc:properties", + "privateProperties": "edc:privateProperties", + "dataAddress": "edc:dataAddress", + "type": "edc:type", + "counterPartyAddress": "edc:counterPartyAddress", + "protocol": "edc:protocol", + "querySpec": "edc:querySpec", + "accessPolicyId": "edc:accessPolicyId", + "contractPolicyId": "edc:contractPolicyId", + "assetsSelector": "edc:assetsSelector", + "state": "edc:state", + "reason": "edc:reason", + "connectorAddress": "edc:connectorAddress", + "offer": "edc:offer", + "asset": "edc:asset", + "offerId": "edc:offerId", + "assetId": "edc:assetId", + "contractId": "edc:contractId", + "connectorId": "edc:connectorId", + "callbackAddresses": "edc:callbackAddresses", + "dataDestination": "edc:dataDestination", + "baseUrl": "edc:baseUrl", + "providerId": "edc:providerId", + "uri": "edc:uri", + "events": "edc:events", + "transactional": "edc:transactional" + } +} \ No newline at end of file diff --git a/core/json-ld-core/src/main/resources/document/tx-v1.jsonld b/core/json-ld-core/src/main/resources/document/tx-v1.jsonld new file mode 100644 index 000000000..58d3c974e --- /dev/null +++ b/core/json-ld-core/src/main/resources/document/tx-v1.jsonld @@ -0,0 +1,21 @@ +{ + "@context": { + "@version": 1.1, + "@protected": true, + "tx": "https://w3id.org/tractusx/v0.0.1/ns/", + "BusinessPartnerNumber": "tx:BusinessPartnerNumber", + "NegotiationInitiateRequestDto": "tx:NegotiationInitiateRequestDto", + "BusinessPartnerGroup": "tx:BusinessPartnerGroup", + "EndpointDataReferenceEntry": "tx:EndpointDataReferenceEntry", + "Membership": "tx:Membership", + "Dismantler": "tx:Dismantler", + "FrameworkAgreement.pcf": "tx:FrameworkAgreement.pcf", + "FrameworkAgreement.sustainability": "tx:FrameworkAgreement.sustainability", + "FrameworkAgreement.quality": "tx:FrameworkAgreement.quality", + "FrameworkAgreement.traceability": "tx:FrameworkAgreement.traceability", + "FrameworkAgreement.behavioraltwin": "tx:FrameworkAgreement.behavioraltwin", + "BPN": "tx:BPN", + "expirationDate": "tx:expirationDate", + "edrState": "tx:edrState" + } +} diff --git a/core/json-ld-core/src/test/java/org/eclipse/tractusx/edc/jsonld/JsonLdExtensionTest.java b/core/json-ld-core/src/test/java/org/eclipse/tractusx/edc/jsonld/JsonLdExtensionTest.java index 7009e5fa7..35ed4edc1 100644 --- a/core/json-ld-core/src/test/java/org/eclipse/tractusx/edc/jsonld/JsonLdExtensionTest.java +++ b/core/json-ld-core/src/test/java/org/eclipse/tractusx/edc/jsonld/JsonLdExtensionTest.java @@ -22,6 +22,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import java.net.URI; + import static org.eclipse.tractusx.edc.jsonld.JsonLdExtension.CREDENTIALS_SUMMARY_V_1; import static org.eclipse.tractusx.edc.jsonld.JsonLdExtension.CREDENTIALS_V_1; import static org.eclipse.tractusx.edc.jsonld.JsonLdExtension.SECURITY_ED25519_V1; @@ -46,10 +48,10 @@ void setup(ObjectFactory factory, ServiceExtensionContext context) { @Test void initialize(ServiceExtensionContext context) { extension.initialize(context); - jsonLdService.registerCachedDocument(eq(CREDENTIALS_V_1), any()); - jsonLdService.registerCachedDocument(eq(CREDENTIALS_SUMMARY_V_1), any()); - jsonLdService.registerCachedDocument(eq(SECURITY_JWS_V1), any()); - jsonLdService.registerCachedDocument(eq(SECURITY_ED25519_V1), any()); + jsonLdService.registerCachedDocument(eq(CREDENTIALS_V_1), any(URI.class)); + jsonLdService.registerCachedDocument(eq(CREDENTIALS_SUMMARY_V_1), any(URI.class)); + jsonLdService.registerCachedDocument(eq(SECURITY_JWS_V1), any(URI.class)); + jsonLdService.registerCachedDocument(eq(SECURITY_ED25519_V1), any(URI.class)); } } diff --git a/docs/development/decision-records/2023-08-03_improve_bpn_validation/README.md b/docs/development/decision-records/2023-08-03_improve_bpn_validation/README.md new file mode 100644 index 000000000..930789f27 --- /dev/null +++ b/docs/development/decision-records/2023-08-03_improve_bpn_validation/README.md @@ -0,0 +1,42 @@ +# Removal of manually curated CHANGELOG.md + +## Decision + +The BPN validation extension will be improved in the following aspects: + +1. Instead of hard-coding them on policies, BPNs are stored in a database (in-mem + Postgres) +2. BPNs are grouped to enable stable policies +3. More `Operator`s are supported +4. Database entries can be manipulated using a REST API + +## Rationale + +Hard-coding BPNs on policies is quite inflexible and does not scale, because when a new business partner joins or leaves +the network, all participants would have to update all their policies, which is a significant migration effort. Instead, +a structure has to be defined where that situation can be handled in a less intrusive and involved way. This effectively +will remove the need to update/migrate policies. + +## Approach + +Every BPN is associated with one or more groups, for example `BPN0000001` -> `["gold_member"]`. It is important to note, +that these groups are _internal_ tags that every participant maintains on their own, they are not claims in +VerifiableCredentials (the BPN would be a claim, however). A new policy constraint is introduced, that looks like this: + +```json +{ + "constraint": { + "leftOperand": "https://w3id.org/tractusx/v0.0.1/ns/BusinessPartnerGroup", + "operator": "isAnyOf", + "rightOperand": [ + "gold_customer", + "platin_partner" + ] + } +} +``` + +NB: the `leftOperand` must be an IRI as mandated by ODRL, thus it must either be prefixed with the namespace (as shown +in the example), or using a vocabulary entry in the JSON-LD context, i.e. `tx:BusinessPartnerGroup`. Supported operators +will be: `eq, neq, in, isAllOf, isAnyOf, isNoneOf`. + +Manipulating the BPN -> group associations can be done through a REST API. diff --git a/docs/kit/operation-view/page10_extensions.md b/docs/kit/operation-view/page10_extensions.md index 798c86195..ce0a72ead 100644 --- a/docs/kit/operation-view/page10_extensions.md +++ b/docs/kit/operation-view/page10_extensions.md @@ -7,7 +7,7 @@ They are currently only available in Tractus-X EDC. This extension allows for validation of business partners within the access policy. -## Control Plane Adapter +## 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, diff --git a/docs/migration/Version_0.4.x_0.5.x.md b/docs/migration/Version_0.4.x_0.5.x.md index 45fc9801e..c652f5ebc 100644 --- a/docs/migration/Version_0.4.x_0.5.x.md +++ b/docs/migration/Version_0.4.x_0.5.x.md @@ -91,12 +91,6 @@ the [SSI Documentation Repository](https://github.com/eclipse-tractusx/ssi-docu/ - the centralized MIW is an interim solution, and is bound to be replaced with a decentralized/distributed architecture in upcoming Catena-X releases. -### Fallback chart - -There is one Helm chart named `tractusx-connector-legacy` that is a carbon-copy of the old connector chart using DAPS. -It is not recommended for production use anymore and is solely intended as a fallback or as a way to gradually move to -SSI. We do not test it, nor do we provide support for it after the release of Tractus-X EDC `0.5.0`. - ## The Observability API changes The following settings are removed because the `observability-api-customization` extension will be no longer used. diff --git a/edc-controlplane/edc-controlplane-base/build.gradle.kts b/edc-controlplane/edc-controlplane-base/build.gradle.kts index 089811456..f8bc52809 100644 --- a/edc-controlplane/edc-controlplane-base/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-base/build.gradle.kts @@ -24,13 +24,16 @@ plugins { dependencies { runtimeOnly(project(":core:edr-cache-core")) - runtimeOnly(project(":edc-extensions:business-partner-validation")) + runtimeOnly(project(":core:edr-core")) runtimeOnly(project(":edc-extensions:dataplane-selector-configuration")) runtimeOnly(project(":edc-extensions:data-encryption")) runtimeOnly(project(":edc-extensions:provision-additional-headers")) - runtimeOnly(project(":edc-extensions:control-plane-adapter-api")) - runtimeOnly(project(":edc-extensions:control-plane-adapter-callback")) + runtimeOnly(project(":edc-extensions:edr:edr-api")) + runtimeOnly(project(":edc-extensions:edr:edr-callback")) + + // needed for BPN validation + runtimeOnly(project(":edc-extensions:bpn-validation")) // needed for SSI integration runtimeOnly(project(":core:json-ld-core")) @@ -44,6 +47,7 @@ dependencies { runtimeOnly(libs.edc.auth.tokenbased) runtimeOnly(libs.edc.api.management) + runtimeOnly(libs.edc.api.management.config) runtimeOnly(libs.edc.api.observability) runtimeOnly(libs.edc.dsp) runtimeOnly(libs.edc.spi.jwt) diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts index c6120480c..eee005572 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts @@ -26,7 +26,7 @@ plugins { dependencies { runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) - runtimeOnly(project(":edc-extensions:hashicorp-vault")) + runtimeOnly(libs.edc.vault.hashicorp) runtimeOnly(libs.edc.core.controlplane) runtimeOnly(libs.edc.dpf.transfer) diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile index 82e1dc8f7..7a459290e 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile @@ -19,17 +19,9 @@ # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.18.0 AS otel - -ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" - -HEALTHCHECK NONE - -RUN apk update && apk add curl=8.1.2-r0 --no-cache -RUN curl -L --proto "=https" -sSf ${OTEL_AGENT_LOCATION} --output /tmp/opentelemetry-javaagent.jar - FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR +ARG OTEL_JAR ARG APP_USER=docker ARG APP_UID=10100 @@ -48,8 +40,8 @@ RUN adduser \ USER "$APP_USER" WORKDIR /app -COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-controlplane.jar +COPY ${OTEL_JAR} opentelemetry-javaagent.jar HEALTHCHECK NONE 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 29e0ffc36..a1cdab224 100644 --- a/edc-controlplane/edc-controlplane-postgresql-azure-vault/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql-azure-vault/build.gradle.kts @@ -29,7 +29,8 @@ plugins { dependencies { runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) runtimeOnly(project(":edc-extensions:postgresql-migration")) - runtimeOnly(project(":edc-extensions:edr-cache-sql")) + runtimeOnly(project(":edc-extensions:edr:edr-cache-sql")) + 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) 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 9d7fb7801..013e84820 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 @@ -18,17 +18,9 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.18.2 AS otel - -ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" - -HEALTHCHECK NONE - -RUN apk update && apk add curl=8.1.2-r0 --no-cache -RUN curl -L --proto "=https" -sSf ${OTEL_AGENT_LOCATION} --output /tmp/opentelemetry-javaagent.jar - FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR +ARG OTEL_JAR ARG APP_USER=docker ARG APP_UID=10100 @@ -47,8 +39,8 @@ RUN adduser \ USER "$APP_USER" WORKDIR /app -COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-controlplane.jar +COPY ${OTEL_JAR} opentelemetry-javaagent.jar HEALTHCHECK NONE diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/README.md b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/README.md deleted file mode 100644 index 4d73773fb..000000000 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/README.md +++ /dev/null @@ -1,178 +0,0 @@ -# EDC Control-Plane backed by [Postgresql](https://www.postgresql.org/) and [HashiCorp vault](https://www.vaultproject.io/docs) - -## Building - -```shell -./gardlew :edc-controlplane:edc-controlplane-postgresql-hashicorp-vault:dockerize -``` - -## Configuration - -Listed below are configuration keys needed to get the `edc-controlplane-postgresql-hashicorp-vault` up and running. -Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-edc/Connector/tree/main/docs). - -| Key | Required | Example | Description | -|--------------------------------------------------|----------|------------------------------------------------------------------------------|----------------------------| -| edc.api.auth.key | | password | default value: random UUID | -| web.http.default.port | X | 8080 | | -| web.http.default.path | X | /api | | -| web.http.data.port | X | 8181 | | -| web.http.data.path | X | /data | | -| web.http.validation.port | X | 8182 | | -| web.http.validation.path | X | /validation | | -| web.http.control.port | X | 9999 | | -| web.http.control.path | X | /api/controlplane/control | | -| web.http.ids.port | X | 8282 | | -| web.http.ids.path | X | /api/v1/ids | | -| edc.receiver.http.endpoint | X | | | -| edc.ids.title | | Eclipse Dataspace Connector | | -| edc.ids.description | | Eclipse Dataspace Connector | | -| edc.ids.id | | urn:connector:edc | | -| edc.ids.security.profile | | base | | -| edc.ids.endpoint | | | | -| edc.ids.maintainer | | | | -| edc.ids.curator | | | | -| edc.ids.catalog.id | | urn:catalog:default | | -| ids.webhook.address | | | | -| edc.hostname | | localhost | | -| edc.oauth.token.url | X | | | -| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | -| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | -| edc.oauth.client.id | X | daps-oauth-client-id | | -| edc.vault.hashicorp.url | X | | | -| edc.vault.hashicorp.token | X | 55555555-6666-7777-8888-999999999999 | | -| edc.vault.hashicorp.timeout.seconds | | 30 | | -| edc.datasource.asset.name | X | asset | | -| edc.datasource.asset.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_asset_db | | -| edc.datasource.asset.user | X | username | | -| edc.datasource.asset.password | X | password | | -| edc.datasource.contractdefinition.name | X | contractdefinition | | -| edc.datasource.contractdefinition.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractdefinition_db | | -| edc.datasource.contractdefinition.user | X | username | | -| edc.datasource.contractdefinition.password | X | password | | -| edc.datasource.contractnegotiation.name | X | contractnegotiation | | -| edc.datasource.contractnegotiation.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractnegotiation_db | | -| edc.datasource.contractnegotiation.user | X | username | | -| edc.datasource.contractnegotiation.password | X | password | | -| edc.datasource.policy.name | X | policy | | -| edc.datasource.policy.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_policy_db | | -| edc.datasource.policy.user | X | username | | -| edc.datasource.policy.password | X | password | | -| edc.datasource.transferprocess.name | X | transferprocess | | -| edc.datasource.transferprocess.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_transferprocess_db | | -| edc.datasource.transferprocess.user | X | username | | -| edc.datasource.transferprocess.password | X | password | | -| edc.transfer.proxy.endpoint | X | | | -| edc.transfer.proxy.token.signer.privatekey.alias | X | | | - -### Example configuration.properties - -JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. - -```shell -# Create configuration.properties -export CONFIGURATION_PROPERTIES_FILE=$(mktemp /tmp/configuration.properties.XXXXXX) -cat << 'EOF' > ${CONFIGURATION_PROPERTIES_FILE} - -web.http.default.port=8080 -web.http.default.path=/api -web.http.data.port=8181 -web.http.data.path=/data -web.http.validation.port=8182 -web.http.validation.path=/validation -web.http.control.port=9999 -web.http.control.path=/api/controlplane/control -web.http.ids.port=8282 -web.http.ids.path=/api/v1/ids - -edc.receiver.http.endpoint=http://backend-service - -edc.ids.title=Eclipse Dataspace Connector -edc.ids.description=Eclipse Dataspace Connector -edc.ids.id=urn:connector:edc -edc.ids.security.profile=base -edc.ids.endpoint=http://localhost:8282/api/v1/ids -edc.ids.maintainer=http://localhost -edc.ids.curator=http://localhost -edc.ids.catalog.id=urn:catalog:default -ids.webhook.address=http://localhost:8282/api/v1/ids - -edc.hostname=localhost - -edc.api.auth.key=password - -# OAuth / DAPS related configuration -edc.oauth.token.url=https://daps.example.net -edc.oauth.public.key.alias=key-to-daps-certificate-in-keyvault -edc.oauth.private.key.alias=key-to-private-key-in-keyvault -edc.oauth.client.id=daps-oauth-client-id - -# HashiCorp vault related configuration -edc.vault.hashicorp.url=http://vault -edc.vault.hashicorp.token=55555555-6666-7777-8888-999999999999 -edc.vault.hashicorp.timeout.seconds=30 - -# Control- / Data- Plane configuration -edc.transfer.proxy.endpoint=http://dataplane-public-endpoint/public -edc.transfer.proxy.token.signer.privatekey.alias=token-signer-private-key - -# Postgresql related configuration -edc.datasource.asset.name=asset -edc.datasource.asset.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_asset -edc.datasource.asset.user=user -edc.datasource.asset.password=pass -edc.datasource.contractdefinition.name=contractdefinition -edc.datasource.contractdefinition.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractdefinition -edc.datasource.contractdefinition.user=user -edc.datasource.contractdefinition.password=pass -edc.datasource.contractnegotiation.name=contractnegotiation -edc.datasource.contractnegotiation.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractnegotiation -edc.datasource.contractnegotiation.user=user -edc.datasource.contractnegotiation.password=pass -edc.datasource.policy.name=policy -edc.datasource.policy.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_policy -edc.datasource.policy.user=user -edc.datasource.policy.password=pass -edc.datasource.transferprocess.name=transferprocess -edc.datasource.transferprocess.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_transferprocess -edc.datasource.transferprocess.user=user -edc.datasource.transferprocess.password=pass -EOF -``` - -### Example logging.properties - -```shell -# Create logging.properties -export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) -cat << 'EOF' > ${LOGGING_PROPERTIES_FILE} -.level=INFO -org.eclipse.edc.level=ALL -handlers=java.util.logging.ConsoleHandler -java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter -java.util.logging.ConsoleHandler.level=ALL -java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n -EOF -``` - -### Example opentelemetry.properties - -```shell -# Create opentelemetry.properties -export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) -cat << 'EOF' > ${OPENTELEMETRY_PROPERTIES_FILE} -otel.javaagent.enabled=false -otel.javaagent.debug=false -EOF -``` - -## Running - -```shell -docker run \ - -p 8080:8080 -p 8181:8181 -p 8182:8182 -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 \ - -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ - -i edc-controlplane-postgresql-hashicorp-vault:latest -``` diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/build.gradle.kts b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/build.gradle.kts deleted file mode 100644 index 11a6619c1..000000000 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/build.gradle.kts +++ /dev/null @@ -1,82 +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 - */ - -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar - -plugins { - `java-library` - id("application") - id("com.github.johnrengelman.shadow") version "8.1.1" -} - -dependencies { - runtimeOnly(project(":core:edr-cache-core")) - runtimeOnly(project(":edc-extensions:business-partner-validation")) - runtimeOnly(project(":edc-extensions:dataplane-selector-configuration")) - runtimeOnly(project(":edc-extensions:data-encryption")) - runtimeOnly(project(":edc-extensions:cx-oauth2")) - runtimeOnly(project(":edc-extensions:provision-additional-headers")) - runtimeOnly(project(":edc-extensions:control-plane-adapter-api")) - runtimeOnly(project(":edc-extensions:control-plane-adapter-callback")) - - runtimeOnly(libs.edc.core.controlplane) - runtimeOnly(libs.edc.config.filesystem) - runtimeOnly(libs.edc.auth.tokenbased) - runtimeOnly(libs.edc.auth.oauth2.core) - runtimeOnly(libs.edc.auth.oauth2.daps) - runtimeOnly(libs.edc.api.management) - runtimeOnly(libs.edc.api.observability) - runtimeOnly(libs.edc.dsp) - runtimeOnly(libs.edc.spi.jwt) - runtimeOnly(libs.bundles.edc.dpf) - - runtimeOnly(libs.edc.ext.http) - runtimeOnly(libs.bundles.edc.monitoring) - runtimeOnly(libs.edc.transfer.dynamicreceiver) - runtimeOnly(libs.edc.controlplane.callback.dispatcher.event) - runtimeOnly(libs.edc.controlplane.callback.dispatcher.http) - - runtimeOnly(project(":edc-extensions:postgresql-migration")) - runtimeOnly(project(":edc-extensions:hashicorp-vault")) - runtimeOnly(project(":edc-extensions:edr-cache-sql")) - 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) - - // needed for DAPS - not officially supported anymore - runtimeOnly(project(":edc-extensions:cx-oauth2")) - runtimeOnly(libs.edc.auth.oauth2.core) - runtimeOnly(libs.edc.auth.oauth2.daps) -} - - -tasks.withType { - exclude("**/pom.properties", "**/pom.xm") - mergeServiceFiles() - archiveFileName.set("${project.name}.jar") -} - - -application { - mainClass.set("org.eclipse.edc.boot.system.runtime.BaseRuntime") -} diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/notice.md b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/notice.md deleted file mode 100644 index 381253ec9..000000000 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/notice.md +++ /dev/null @@ -1,28 +0,0 @@ -# Notice for Docker image - -An EDC Control Plane using PostgreSQL as persistence backend, and HashiCorp Vault as secret store. - -DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-postgresql-hashicorp-vault - -Eclipse Tractus-X product(s) installed within the image: - -## Tractus-X EDC Control Plane - -- GitHub: https://github.com/eclipse-tractusx/tractusx-edc -- Project home: https://projects.eclipse.org/projects/automotive.tractusx -- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile -- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) - -## Used base image - -- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) -- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin -- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin -- Additional information about the Eclipse Temurin - images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin - -As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc -from the base distribution, along with any direct or indirect dependencies of the primary software being contained). - -As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies -with any relevant licenses for all software contained within. diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/src/main/docker/Dockerfile deleted file mode 100644 index 9d7fb7801..000000000 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault-legacy/src/main/docker/Dockerfile +++ /dev/null @@ -1,64 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2022,2023 Mercedes-Benz Tech Innovation GmbH -# 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 -# -FROM alpine:3.18.2 AS otel - -ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" - -HEALTHCHECK NONE - -RUN apk update && apk add curl=8.1.2-r0 --no-cache -RUN curl -L --proto "=https" -sSf ${OTEL_AGENT_LOCATION} --output /tmp/opentelemetry-javaagent.jar - -FROM eclipse-temurin:17.0.6_10-jre-alpine -ARG JAR - -ARG APP_USER=docker -ARG APP_UID=10100 - -RUN addgroup --system "$APP_USER" - -RUN adduser \ - --shell /sbin/nologin \ - --disabled-password \ - --gecos "" \ - --ingroup "$APP_USER" \ - --no-create-home \ - --uid "$APP_UID" \ - "$APP_USER" - -USER "$APP_USER" -WORKDIR /app - -COPY --from=otel /tmp/opentelemetry-javaagent.jar . -COPY ${JAR} edc-controlplane.jar - -HEALTHCHECK NONE - -CMD ["java", \ - "-javaagent:/app/opentelemetry-javaagent.jar", \ - "-Dedc.fs.config=/app/configuration.properties", \ - "-Djava.util.logging.config.file=/app/logging.properties", \ - "-Dotel.javaagent.configuration-file=/app/opentelemetry.properties", \ - "-Dotel.metrics.exporter=prometheus", \ - "-Dotel.exporter.prometheus.port=9090", \ - "-Djava.security.egd=file:/dev/urandom", \ - "-jar", \ - "edc-controlplane.jar"] 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 8b8398dcc..634c5a24f 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts @@ -29,8 +29,9 @@ plugins { dependencies { runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) runtimeOnly(project(":edc-extensions:postgresql-migration")) - runtimeOnly(project(":edc-extensions:hashicorp-vault")) - runtimeOnly(project(":edc-extensions:edr-cache-sql")) + runtimeOnly(project(":edc-extensions:edr:edr-cache-sql")) + 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) 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 9d7fb7801..7a459290e 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 @@ -18,17 +18,10 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.18.2 AS otel - -ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" - -HEALTHCHECK NONE - -RUN apk update && apk add curl=8.1.2-r0 --no-cache -RUN curl -L --proto "=https" -sSf ${OTEL_AGENT_LOCATION} --output /tmp/opentelemetry-javaagent.jar FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR +ARG OTEL_JAR ARG APP_USER=docker ARG APP_UID=10100 @@ -47,8 +40,8 @@ RUN adduser \ USER "$APP_USER" WORKDIR /app -COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-controlplane.jar +COPY ${OTEL_JAR} opentelemetry-javaagent.jar HEALTHCHECK NONE diff --git a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts index 4423254e6..04993fa96 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts @@ -32,8 +32,8 @@ dependencies { } } implementation(libs.edc.azure.identity) - implementation("com.azure:azure-security-keyvault-secrets:4.6.3") - runtimeOnly(project(":edc-extensions:edr-cache-sql")) + implementation("com.azure:azure-security-keyvault-secrets:4.6.4") + runtimeOnly(project(":edc-extensions:edr:edr-cache-sql")) runtimeOnly(libs.edc.transaction.local) runtimeOnly(libs.edc.sql.pool) 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 9475ed4f0..520ddfefa 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -18,17 +18,9 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.18.2 AS otel - -ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" - -HEALTHCHECK NONE - -RUN apk update && apk add curl=8.1.2-r0 --no-cache -RUN curl -L --proto "=https" -sSf ${OTEL_AGENT_LOCATION} --output /tmp/opentelemetry-javaagent.jar - FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR +ARG OTEL_JAR ARG APP_USER=docker ARG APP_UID=10100 @@ -47,8 +39,8 @@ RUN adduser \ USER "$APP_USER" WORKDIR /app -COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-dataplane.jar +COPY ${OTEL_JAR} opentelemetry-javaagent.jar HEALTHCHECK NONE diff --git a/edc-dataplane/edc-dataplane-base/build.gradle.kts b/edc-dataplane/edc-dataplane-base/build.gradle.kts index 7847c2f9f..03a5ee3db 100644 --- a/edc-dataplane/edc-dataplane-base/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-base/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { runtimeOnly(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-core")) runtimeOnly(libs.edc.config.filesystem) + runtimeOnly(libs.edc.auth.tokenbased) runtimeOnly(libs.edc.dpf.awss3) runtimeOnly(libs.edc.dpf.oauth2) runtimeOnly(libs.edc.dpf.http) diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts index 247744df2..877952cf2 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts @@ -25,8 +25,8 @@ plugins { dependencies { implementation(project(":edc-dataplane:edc-dataplane-base")) - implementation(project(":edc-extensions:hashicorp-vault")) - runtimeOnly(project(":edc-extensions:edr-cache-sql")) + 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.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 9475ed4f0..4fdf953c7 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -18,17 +18,10 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.18.2 AS otel - -ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" - -HEALTHCHECK NONE - -RUN apk update && apk add curl=8.1.2-r0 --no-cache -RUN curl -L --proto "=https" -sSf ${OTEL_AGENT_LOCATION} --output /tmp/opentelemetry-javaagent.jar FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR +ARG OTEL_JAR ARG APP_USER=docker ARG APP_UID=10100 @@ -47,8 +40,9 @@ RUN adduser \ USER "$APP_USER" WORKDIR /app -COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-dataplane.jar +COPY ${OTEL_JAR} opentelemetry-javaagent.jar + HEALTHCHECK NONE diff --git a/edc-extensions/bpn-validation/README.md b/edc-extensions/bpn-validation/README.md new file mode 100644 index 000000000..58ec270d1 --- /dev/null +++ b/edc-extensions/bpn-validation/README.md @@ -0,0 +1,157 @@ +# Business Partner Validation Extension + +This extension is used to introduce the capability to a connector to evaluate two types of policies: + +- A Business Partner Group policy: evaluates, whether a certain BPN belongs to a certain group. For example, a + participating company categorizes other dataspace participants in three + groups: `"customer"`, `"gold_customer"`, `"platin_customer"`. Then, that company may want to show certain assets only + to a specific group. The Business Partner Group Policy enables that semantic. +- [not recommended] a Business Partner Number Policy: evaluates, whether the BPN in question is contained in a list of " + white-listed" BPNs. That whitelist is hard-coded directly on the policy. This policy is **not recommended anymore** + due to + concerns of scalability and maintainability. Each time such a policy is evaluated, the runtime will log a warning. + +Technically, both these policies and their evaluation functions can be used in several circumstances, which in EDC are +called *scopes*. More information on how to bind policy functions to scopes can be found in +the [official documentation](https://github.com/eclipse-edc/Connector/blob/main/docs/developer/policy-engine.md). + +Both previously mentioned evaluation functions are bound to the following scopes: + +- `catalog`: determines, what policies (specifically: constraints) are to be evaluated when requesting the catalog ( + i.e. "access policy") +- `contract.negotiation`: determines, which policies/constraints are to be evaluated during the negotiation phase ( + i.e. "contract policy") +- `transfer.process`: determines, which policies/constraints are to be evaluated when performing a data transfer, e.g. + contract expiry + +## Business Partner Group Policy + +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. + +### Example + +```json +{ + "@type": "https://w3id.org/edc/v0.0.1/ns/PolicyDefinitionDto", + "https://w3id.org/edc/v0.0.1/ns/policy": { + "@context": "http://www.w3.org/ns/odrl.jsonld", + "permission": { + "action": "USE", + "constraint": { + "@type": "http://www.w3.org/ns/odrl/2/LogicalConstraint", + "or": [ + { + "@type": "http://www.w3.org/ns/odrl/2/Constraint", + "leftOperand": "https://w3id.org/tractusx/v0.0.1/ns/BusinessPartnerGroup", + "operator": "http://www.w3.org/ns/odrl/2/isAllOf", + "rightOperand": "greek,philosopher" + } + ] + } + } + }, + "@id": "some-policy-id" +} +``` + +The first important take-away is the `constraint` object, which contains a single expression that mandates, that in +order to fulfill the policy, a business partner must be `greek` and they must be a `philosopher`. Whether a +particular BPN has either of these groups assigned is determined by the `ParticipantAgent`, and by a subsequent lookup +in an internal database. See [the next section](#manipulating-groups) for details. + +The second important aspect is the `leftOperand`, which must +be `"https://w3id.org/tractusx/v0.0.1/ns/BusinessPartnerGroup"`. Together with the scope, the `leftOperand` determines, +which constraint functions is called to evaluate the policy. Here, it is the `BusinessPartnerGroupFunction`. + +### Manipulating groups + +The `bpn-evaluation` module provides a simple CRUD REST API to manipulate BPN <> group associations. Each BPN is stored +in an internal database together with the groups that it was assigned. The OpenAPI specification can be +found [here](../../resources/openapi/yaml/bpn-validation-api.yaml). + +## Business Partner Number Policy [not recommended] + +This policy mandates, that a particular Business Partner Number must be contained in a white-list that is hard-coded on +the policy. Here, only the ODRL `eq"` operator is supported, and the `rightOperand` must be the white-listed BPN. + +### Example + +```json +{ + "@type": "https://w3id.org/edc/v0.0.1/ns/PolicyDefinitionDto", + "https://w3id.org/edc/v0.0.1/ns/policy": { + "@context": "http://www.w3.org/ns/odrl.jsonld", + "permission": [ + { + "action": "USE", + "constraint": { + "@type": "http://www.w3.org/ns/odrl/2/LogicalConstraint", + "or": [ + { + "@type": "http://www.w3.org/ns/odrl/2/Constraint", + "leftOperand": "BusinessPartnerNumber", + "operator": "eq", + "rightOperand": "BPN00001234" + } + ] + } + } + ] + }, + "@id": "some-policy-id" +} +``` + +Again, the `leftOperand` must be `"BusinessPartnerNumber`, and it determines, which constraint function is evaluated +(here: `BusinessPartnerPermissionFunction`). The evaluation of the example policy only succeeds, when +the `ParticipantAgent`'s BPN is `"BPN00001234"`. + +In case multiple BPNs are to be white-listed, the policy would contain multiple `or` constraints: + +```json +{ + "@type": "https://w3id.org/edc/v0.0.1/ns/PolicyDefinitionDto", + "https://w3id.org/edc/v0.0.1/ns/policy": { + "@context": "http://www.w3.org/ns/odrl.jsonld", + "permission": [ + { + "action": "USE", + "constraint": { + "@type": "http://www.w3.org/ns/odrl/2/LogicalConstraint", + "or": [ + { + "@type": "http://www.w3.org/ns/odrl/2/Constraint", + "leftOperand": "BusinessPartnerNumber", + "operator": "eq", + "rightOperand": "BPN00001234" + }, + { + "@type": "http://www.w3.org/ns/odrl/2/Constraint", + "leftOperand": "BusinessPartnerNumber", + "operator": "eq", + "rightOperand": "BPN00005678" + } + ] + } + } + ] + }, + "@id": "some-policy-id" +} +``` + +The second policy expresses that the BPN of the participant in question must be either `"BPN00001234"` +*or* `"BPN00005678"`. + +### Deprecation warning + +The Business Partner Number Policy is not recommended for production use because it is severely limited in terms of +scalability and maintainability. Everytime a new participant onboards onto or off-boards from the dataspace, every +existing participant would have to either enter new contract definitions, effectively duplicating them, or update *all +existing policies*. That would be a significant maintenance and migration effort. + +For that reason, the Business Partner Number Policy is marked as deprecated, and is scheduled for removal in EDC 0.6.x. +Please consider upgrading your environment at your earliest convenience. diff --git a/edc-extensions/control-plane-adapter-api/build.gradle.kts b/edc-extensions/bpn-validation/bpn-validation-api/build.gradle.kts similarity index 88% rename from edc-extensions/control-plane-adapter-api/build.gradle.kts rename to edc-extensions/bpn-validation/bpn-validation-api/build.gradle.kts index 36e1437b9..d26e14408 100644 --- a/edc-extensions/control-plane-adapter-api/build.gradle.kts +++ b/edc-extensions/bpn-validation/bpn-validation-api/build.gradle.kts @@ -19,10 +19,8 @@ plugins { } dependencies { - implementation(project(":spi:control-plane-adapter-spi")) - implementation(project(":spi:edr-cache-spi")) + implementation(project(":edc-extensions:bpn-validation:bpn-validation-spi")) implementation(project(":spi:core-spi")) - implementation(libs.edc.api.management) implementation(libs.edc.spi.aggregateservices) implementation(libs.jakarta.rsApi) diff --git a/edc-extensions/bpn-validation/bpn-validation-api/src/main/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApi.java b/edc-extensions/bpn-validation/bpn-validation-api/src/main/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApi.java new file mode 100644 index 000000000..1c57262ca --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-api/src/main/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApi.java @@ -0,0 +1,102 @@ +/* + * + * Copyright (c) 2023 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.bpn; + +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.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.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.json.JsonObject; +import org.eclipse.edc.web.spi.ApiErrorDetail; + +import java.util.Set; + +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; + +@OpenAPIDefinition(info = @Info(description = "With this API clients can create, read, update and delete BusinessPartnerNumber groups. It allows the assigning of BPNs to groups.", title = "Business Partner Group API")) +@Tag(name = "Business Partner Group") +public interface BusinessPartnerGroupApi { + + + @Operation(description = "Resolves all groups for a particular BPN", + responses = { + @ApiResponse(responseCode = "200", description = "An object containing an array with the assigned groups"), + @ApiResponse(responseCode = "404", description = "No entry for the given BPN was found"), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) + }) + JsonObject resolve(@Parameter(name = "bpn", description = "The business partner number") String bpn); + + @Operation(description = "Deletes the entry for a particular BPN", + responses = { + @ApiResponse(responseCode = "204", description = "The object was successfully deleted"), + @ApiResponse(responseCode = "404", description = "No entry for the given BPN was found"), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) + }) + void deleteEntry(@Parameter(name = "bpn", description = "The business partner number") String bpn); + + @Operation(description = "Updates the entry for a particular BPN", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ListSchema.class))), + + responses = { + @ApiResponse(responseCode = "204", description = "The object was successfully updated"), + @ApiResponse(responseCode = "404", description = "No entry for the given BPN was found"), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) + }) + void updateEntry(JsonObject object); + + @Operation(description = "Creates an entry for a particular BPN", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ListSchema.class))), + + responses = { + @ApiResponse(responseCode = "204", description = "The object was successfully created"), + @ApiResponse(responseCode = "409", description = "An entry already exists for that BPN"), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) + }) + void createEntry(JsonObject entry); + + + @Schema(name = "List", example = ListSchema.EXAMPLE) + record ListSchema( + @Schema(name = ID) String id, + Set groups + ) { + public static final String EXAMPLE = """ + { + "@context": { + "tx": "https://w3id.org/tractusx/v0.0.1/ns/" + }, + "@id": "tx:BPN000001234", + "tx:groups": ["group1", "group2", "group3"] + } + """; + } +} diff --git a/edc-extensions/bpn-validation/bpn-validation-api/src/main/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiController.java b/edc-extensions/bpn-validation/bpn-validation-api/src/main/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiController.java new file mode 100644 index 000000000..5b7aeb6f3 --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-api/src/main/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiController.java @@ -0,0 +1,130 @@ +/* + * + * Copyright (c) 2023 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.bpn; + +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +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.web.spi.exception.InvalidRequestException; +import org.eclipse.edc.web.spi.exception.ObjectConflictException; +import org.eclipse.edc.web.spi.exception.ObjectNotFoundException; +import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; + + +@Consumes({MediaType.APPLICATION_JSON}) +@Produces({MediaType.APPLICATION_JSON}) +@Path("/business-partner-groups") +public class BusinessPartnerGroupApiController implements BusinessPartnerGroupApi { + + private final BusinessPartnerStore businessPartnerService; + + + public BusinessPartnerGroupApiController(BusinessPartnerStore businessPartnerService) { + this.businessPartnerService = businessPartnerService; + } + + @GET + @Path("/{bpn}") + @Override + public JsonObject resolve(@PathParam("bpn") String bpn) { + + // StoreResult does not support the .map() operator, because it does not override newInstance() + var result = businessPartnerService.resolveForBpn(bpn); + if (result.succeeded()) { + return createObject(bpn, result.getContent()); + } + + throw new ObjectNotFoundException(List.class, result.getFailureDetail()); + } + + @DELETE + @Path("/{bpn}") + @Override + public void deleteEntry(@PathParam("bpn") String bpn) { + businessPartnerService.delete(bpn) + .orElseThrow(f -> new ObjectNotFoundException(List.class, f.getFailureDetail())); + } + + @PUT + @Override + public void updateEntry(@RequestBody JsonObject object) { + var bpn = getBpn(object); + var groups = getGroups(object); + businessPartnerService.update(bpn, groups) + .orElseThrow(f -> new ObjectNotFoundException(List.class, f.getFailureDetail())); + } + + @POST + @Override + public void createEntry(@RequestBody JsonObject object) { + var bpn = getBpn(object); + var groups = getGroups(object); + businessPartnerService.save(bpn, groups) + .orElseThrow(f -> new ObjectConflictException(f.getFailureDetail())); + } + + private JsonObject createObject(String bpn, List list) { + return Json.createObjectBuilder() + .add(ID, bpn) + .add(TX_NAMESPACE + "groups", Json.createArrayBuilder(list)) + .build(); + } + + + private String getBpn(JsonObject object) { + try { + return object.getString(ID); + } catch (Exception ex) { + throw new InvalidRequestException(ex.getMessage()); + } + } + + @NotNull + private List getGroups(JsonObject object) { + try { + return object.getJsonArray(TX_NAMESPACE + "groups") + .stream() + .map(jv -> ((JsonString) jv.asJsonObject().get(VALUE)).getString()) + .toList(); + } catch (Exception ex) { + throw new InvalidRequestException(ex.getMessage()); + } + } + +} 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 new file mode 100644 index 000000000..899b991fd --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-api/src/main/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiExtension.java @@ -0,0 +1,58 @@ +/* + * + * Copyright (c) 2023 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.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; +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.validation.businesspartner.spi.BusinessPartnerStore; + +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_PREFIX; + +@Extension(value = "Registers the Business Partner Group API") +public class BusinessPartnerGroupApiExtension implements ServiceExtension { + + @Inject + private WebService webService; + @Inject + private ManagementApiConfiguration apiConfiguration; + @Inject + private ManagementApiTypeTransformerRegistry transformerRegistry; + @Inject + private JsonLd jsonLdService; + @Inject + private BusinessPartnerStore businessPartnerStore; + + @Override + public void initialize(ServiceExtensionContext context) { + jsonLdService.registerNamespace(TX_PREFIX, TX_NAMESPACE); + + webService.registerResource(apiConfiguration.getContextAlias(), new BusinessPartnerGroupApiController(businessPartnerStore)); + + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/bpn-validation/bpn-validation-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 77% rename from edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/bpn-validation/bpn-validation-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index b8d59a5b0..bb54cf2b2 100644 --- a/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/bpn-validation/bpn-validation-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -17,8 +17,4 @@ # # SPDX-License-Identifier: Apache-2.0 # -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file -# -org.eclipse.tractusx.edc.hashicorpvault.HashicorpVaultHealthExtension -org.eclipse.tractusx.edc.hashicorpvault.HashicorpVaultVaultExtension +org.eclipse.tractusx.edc.api.bpn.BusinessPartnerGroupApiExtension \ No newline at end of file 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 new file mode 100644 index 000000000..d83112a6f --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-api/src/test/java/org/eclipse/tractusx/edc/api/bpn/BusinessPartnerGroupApiControllerTest.java @@ -0,0 +1,196 @@ +/* + * + * Copyright (c) 2023 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.bpn; + +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; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; +import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; +import org.junit.jupiter.api.BeforeEach; +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 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.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_PREFIX; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ApiTest +class BusinessPartnerGroupApiControllerTest extends RestControllerTestBase { + + private final JsonLd jsonLdService = new TitaniumJsonLd(mock()); + private final BusinessPartnerStore businessPartnerStore = mock(); + private final ManagementApiTypeTransformerRegistry transformerRegistry = mock(); + + @BeforeEach + void setUp() { + jsonLdService.registerNamespace("edc", EDC_NAMESPACE); + jsonLdService.registerNamespace("tx", TX_NAMESPACE); + } + + @Test + void resolve() { + when(businessPartnerStore.resolveForBpn(any())).thenReturn(StoreResult.success(List.of("group1", "group2"))); + baseRequest() + .get("/test-bpn") + .then() + .statusCode(200) + .body(notNullValue()); + } + + @Test + void resolve_exists_noGroups() { + when(businessPartnerStore.resolveForBpn(any())).thenReturn(StoreResult.success(List.of())); + baseRequest() + .get("/test-bpn") + .then() + .statusCode(200); + } + + @Test + void resolve_notExists_returns404() { + when(businessPartnerStore.resolveForBpn(any())).thenReturn(StoreResult.notFound("test-message")); + baseRequest() + .get("/test-bpn") + .then() + .statusCode(404); + } + + @Test + void deleteEntry() { + when(businessPartnerStore.delete(anyString())).thenReturn(StoreResult.success()); + baseRequest() + .delete("/test-bpn") + .then() + .statusCode(204); + } + + @Test + void deleteEntry_notExists_returns404() { + when(businessPartnerStore.delete(anyString())).thenReturn(StoreResult.notFound("test-message")); + baseRequest() + .delete("/test-bpn") + .then() + .statusCode(404); + } + + @Test + void updateEntry() { + when(businessPartnerStore.update(anyString(), any())).thenReturn(StoreResult.success()); + baseRequest() + .contentType(JSON) + .body(createJsonObject()) + .put() + .then() + .statusCode(204); + } + + + @Test + void updateEntry_notExists_returns404() { + when(businessPartnerStore.update(anyString(), any())).thenReturn(StoreResult.notFound("test-message")); + baseRequest() + .contentType(JSON) + .body(createJsonObject()) + .put() + .then() + .statusCode(404); + } + + @Test + void updateEntry_invalidBody_returns400() { + baseRequest() + .contentType(JSON) + .body("{\"invalid-key\": \"invalid-value\"}") + .put() + .then() + .statusCode(400); + } + + @Test + void createEntry() { + when(businessPartnerStore.save(anyString(), any())).thenReturn(StoreResult.success()); + baseRequest() + .contentType(JSON) + .body(createJsonObject()) + .post() + .then() + .statusCode(204); + } + + @Test + void createEntry_alreadyExists_returns409() { + when(businessPartnerStore.save(anyString(), any())).thenReturn(StoreResult.alreadyExists("test-message")); + baseRequest() + .contentType(JSON) + .body(createJsonObject()) + .post() + .then() + .statusCode(409); + } + + @Test + void createEntry_invalidBody_returns400() { + baseRequest() + .contentType(JSON) + .body("{\"invalid-key\": \"invalid-value\"}") + .post() + .then() + .statusCode(400); + } + + @Override + protected Object controller() { + return new BusinessPartnerGroupApiController(businessPartnerStore); + } + + private RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port) + .basePath("/business-partner-groups") + .when(); + } + + private JsonObject createJsonObject() { + return jsonLdService.expand(Json.createObjectBuilder() + .add(ID, "test-bpn") + .add(CONTEXT, Json.createObjectBuilder().add(TX_PREFIX, TX_NAMESPACE).build()) + .add(TX_NAMESPACE + "groups", String.join(",", "group1", "group2", "group3")) + .build()).orElseThrow(f -> new RuntimeException(f.getFailureDetail())); + } + +} \ No newline at end of file diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactory.java b/edc-extensions/bpn-validation/bpn-validation-core/build.gradle.kts similarity index 57% rename from edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactory.java rename to edc-extensions/bpn-validation/bpn-validation-core/build.gradle.kts index 0144ad3a4..5d14017b9 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactory.java +++ b/edc-extensions/bpn-validation/bpn-validation-core/build.gradle.kts @@ -1,5 +1,4 @@ /* - * 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 @@ -17,19 +16,23 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.tractusx.edc.data.encryption.data; -public interface CryptoDataFactory { - - DecryptedData decryptedFromText(String text); - - DecryptedData decryptedFromBase64(String base64); - - DecryptedData decryptedFromBytes(byte[] bytes); - - EncryptedData encryptedFromText(String text); +plugins { + `java-library` + `maven-publish` + `java-test-fixtures` +} - EncryptedData encryptedFromBase64(String base64); +dependencies { + api(project(":edc-extensions:bpn-validation:bpn-validation-spi")) + implementation(project(":spi:core-spi")) + api(libs.edc.spi.core) + implementation(libs.edc.spi.policy) + implementation(libs.edc.spi.contract) + implementation(libs.edc.spi.policyengine) - EncryptedData encryptedFromBytes(byte[] bytes); + testImplementation(libs.edc.junit) + testFixturesImplementation(libs.edc.junit) + testFixturesImplementation(libs.junit.jupiter.api) + testFixturesImplementation(libs.assertj) } 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 new file mode 100644 index 000000000..0be759ea8 --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java @@ -0,0 +1,80 @@ +/* + * 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 + * + */ + +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.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.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}
  • + *
+ * 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: + *
+ * {
+ *     "constraint": {
+ *         "leftOperand": "https://w3id.org/tractusx/v0.0.1/ns/BusinessPartnerGroup",
+ *         "operator": "isAnyOf",
+ *         "rightOperand": ["gold_customer","platin_partner"]
+ *     }
+ * }
+ * 
+ *

+ * Note that the {@link BusinessPartnerGroupFunction} is an {@link org.eclipse.edc.policy.engine.spi.AtomicConstraintFunction}, thus it is registered with the {@link PolicyEngine} for the {@link Permission} class. + */ +@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; + @Inject + private PolicyEngine policyEngine; + @Inject + private BusinessPartnerStore store; + + @Override + public void initialize(ServiceExtensionContext context) { + var function = new BusinessPartnerGroupFunction(store); + + bindToScope(function, TRANSFER_SCOPE); + bindToScope(function, NEGOTIATION_SCOPE); + bindToScope(function, CATALOGING_SCOPE); + } + + private void bindToScope(BusinessPartnerGroupFunction function, String scope) { + ruleBindingRegistry.bind(USE, scope); + ruleBindingRegistry.bind(ODRL_SCHEMA + "use", scope); + ruleBindingRegistry.bind(BusinessPartnerGroupFunction.BUSINESS_PARTNER_CONSTRAINT_KEY, scope); + + policyEngine.registerFunction(scope, Permission.class, BusinessPartnerGroupFunction.BUSINESS_PARTNER_CONSTRAINT_KEY, function); + } +} 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 new file mode 100644 index 000000000..5e8758934 --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/LegacyBusinessPartnerValidationExtension.java @@ -0,0 +1,119 @@ +/* + * + * Copyright (c) 2023 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.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; + +/** + * 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 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); + + 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); + } + + 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/defaults/DefaultStoreProviderExtension.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/defaults/DefaultStoreProviderExtension.java new file mode 100644 index 000000000..1ec6fe3e3 --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/defaults/DefaultStoreProviderExtension.java @@ -0,0 +1,29 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.validation.businesspartner.defaults; + +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; + +@Extension("Provides a default BusinessPartnerGroupStore") +public class DefaultStoreProviderExtension implements ServiceExtension { + + @Provider(isDefault = true) + public BusinessPartnerStore createInMemStore() { + return new InMemoryBusinessPartnerStore(); + } +} diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/defaults/InMemoryBusinessPartnerStore.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/defaults/InMemoryBusinessPartnerStore.java new file mode 100644 index 000000000..13b54b6dd --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/defaults/InMemoryBusinessPartnerStore.java @@ -0,0 +1,61 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.validation.businesspartner.defaults; + +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class InMemoryBusinessPartnerStore implements BusinessPartnerStore { + private final Map> cache = new HashMap<>(); + + @Override + public StoreResult> resolveForBpn(String businessPartnerNumber) { + var entry = cache.get(businessPartnerNumber); + return entry == null ? + StoreResult.notFound(NOT_FOUND_TEMPLATE.formatted(businessPartnerNumber)) : + StoreResult.success(entry); + } + + @Override + public StoreResult save(String businessPartnerNumber, List groups) { + //to maintain behavioural consistency with the SQL store + if (cache.containsKey(businessPartnerNumber)) { + return StoreResult.alreadyExists(ALREADY_EXISTS_TEMPLATE.formatted(businessPartnerNumber)); + } + cache.put(businessPartnerNumber, groups); + return StoreResult.success(); + } + + @Override + public StoreResult delete(String businessPartnerNumber) { + + return cache.remove(businessPartnerNumber) == null ? + StoreResult.notFound(NOT_FOUND_TEMPLATE.formatted(businessPartnerNumber)) : + StoreResult.success(); + } + + @Override + public StoreResult update(String businessPartnerNumber, List groups) { + if (cache.containsKey(businessPartnerNumber)) { + cache.put(businessPartnerNumber, groups); + return StoreResult.success(); + } + return StoreResult.notFound(NOT_FOUND_TEMPLATE.formatted(businessPartnerNumber)); + } +} 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 new file mode 100644 index 000000000..06cd255a5 --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerGroupFunction.java @@ -0,0 +1,196 @@ +/* + * 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 + * + */ + +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 org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static java.lang.String.format; +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_ALL_OF; +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.TX_NAMESPACE; + +/** + * This function evaluates, that a particular {@link ParticipantAgent} is a member of a particular group. + * The {@link ParticipantAgent} is represented by its BPN, the {@link org.eclipse.edc.policy.model.Operator} and the {@code rightValue} determine the group(s) and + * whether the BPN must part of it, or not be part of it. + *

+ * For example, a {@link org.eclipse.edc.policy.model.Policy} that mandates the BPN be part of a group {@code "gold_customers"} or {@code "platin_partner} could look like this: + * + *

+ * {
+ *     "constraint": {
+ *         "leftOperand": "https://w3id.org/tractusx/v0.0.1/ns/BusinessPartnerGroup",
+ *         "operator": "isAnyOf",
+ *         "rightOperand": ["gold_customer","platin_partner"]
+ *     }
+ * }
+ * 
+ *

+ * Upon evaluation, the {@link BusinessPartnerGroupFunction} will take the {@link ParticipantAgent}s BPN, use it to resolve the groups that the BPN is part of, and check, whether `"gold_partner"` and + * `"platin_partner"` are amongst those groups. + *

+ * The following operators are supported: + *

    + *
  • {@link Operator#EQ}: must be exactly in - and only in - that particular group or set of groups
  • + *
  • {@link Operator#NEQ}: must not be in a particular group or set of groups
  • + *
  • {@link Operator#IN}: must be in any of the specified groups
  • + *
  • {@link Operator#IS_ALL_OF}: must be in all of the specified groups
  • + *
  • {@link Operator#IS_ANY_OF}: must be in any of the specified groups
  • + *
  • {@link Operator#IS_NONE_OF}: must not be in any of the specified groups
  • + *
+ * + * @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<>(); + private final BusinessPartnerStore store; + + public BusinessPartnerGroupFunction(BusinessPartnerStore store) { + this.store = store; + OPERATOR_EVALUATOR_MAP.put(EQ, this::evaluateEquals); + OPERATOR_EVALUATOR_MAP.put(NEQ, this::evaluateNotEquals); + OPERATOR_EVALUATOR_MAP.put(IN, this::evaluateIn); + OPERATOR_EVALUATOR_MAP.put(IS_ALL_OF, this::evaluateEquals); + OPERATOR_EVALUATOR_MAP.put(IS_ANY_OF, this::evaluateIn); + OPERATOR_EVALUATOR_MAP.put(IS_NONE_OF, this::evaluateNotEquals); + } + + + /** + * Policy evaluation function that checks whether a given BusinessPartnerNumber is covered by a given policy. + * The evaluation is prematurely aborted (returns {@code false}) if: + *
    + *
  • No {@link ParticipantAgent} was found on the {@link PolicyContext}
  • + *
  • The operator is invalid. Check {@link BusinessPartnerGroupFunction#ALLOWED_OPERATORS} for valid operators.
  • + *
  • No database entry was found for the BPN (taken from the {@link ParticipantAgent})
  • + *
  • A database entry was found, but no group-IDs were assigned
  • + *
  • The right value is anything other than {@link String} or {@link Collection}
  • + *
+ */ + @Override + public boolean evaluate(Operator operator, Object rightValue, Permission rule, PolicyContext policyContext) { + var participantAgent = policyContext.getContextData(ParticipantAgent.class); + + // No participant agent found in context + if (participantAgent == null) { + policyContext.reportProblem("ParticipantAgent not found on PolicyContext"); + return false; + } + + // invalid operator + if (!ALLOWED_OPERATORS.contains(operator)) { + var ops = ALLOWED_OPERATORS.stream().map(Enum::name).collect(Collectors.joining(", ")); + policyContext.reportProblem(format("Operator must be one of [%s] but was [%s]", ops, operator.name())); + return false; + } + + + var bpn = getBpnClaim(participantAgent); + var groups = store.resolveForBpn(bpn); + + // BPN not found in database + if (groups.failed()) { + policyContext.reportProblem(groups.getFailureDetail()); + return false; + } + var assignedGroups = groups.getContent(); + + // BPN was found, but it does not have groups assigned. + if (assignedGroups.isEmpty()) { + policyContext.reportProblem("No groups were assigned to BPN " + bpn); + return false; + } + + // right-operand is anything other than String or Collection + var rightOperand = parseRightOperand(rightValue, policyContext); + if (rightOperand == null) { + return false; + } + + //call evaluator function + return OPERATOR_EVALUATOR_MAP.get(operator).apply(new BpnGroupHolder(assignedGroups, rightOperand)); + } + + private List parseRightOperand(Object rightValue, PolicyContext context) { + if (rightValue instanceof String) { + var tokens = ((String) rightValue).split(","); + return Arrays.asList(tokens); + } + if (rightValue instanceof Collection) { + return ((Collection) rightValue).stream().map(Object::toString).toList(); + } + + 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 + return bpnGroupHolder.allowedGroups + .stream() + .distinct() + .anyMatch(assigned::contains); + } + + private Boolean evaluateNotEquals(BpnGroupHolder bpnGroupHolder) { + return !evaluateIn(bpnGroupHolder); + } + + private Boolean evaluateEquals(BpnGroupHolder bpnGroupHolder) { + return bpnGroupHolder.allowedGroups.equals(bpnGroupHolder.assignedGroups); + } + + /** + * Internal utility class to hold the list of assigned groups for a BPN, and the list of groups specified in the policy ("allowed groups"). + */ + private record BpnGroupHolder(List assignedGroups, List allowedGroups) { + } +} diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/AbstractBusinessPartnerValidation.java similarity index 76% rename from edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java rename to edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/AbstractBusinessPartnerValidation.java index 000630b19..c17b75293 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/AbstractBusinessPartnerValidation.java @@ -1,24 +1,25 @@ /* - * 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. + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 * - * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.tractusx.edc.validation.businesspartner.functions; +package org.eclipse.tractusx.edc.validation.businesspartner.functions.legacy; import org.eclipse.edc.connector.contract.spi.types.agreement.ContractAgreement; import org.eclipse.edc.policy.engine.spi.PolicyContext; @@ -34,7 +35,10 @@ /** * 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: @@ -72,8 +76,7 @@ protected AbstractBusinessPartnerValidation(Monitor monitor, boolean logAgreemen * @param businessPartnerNumber of the constraint * @return true if claim contains the business partner number */ - private static boolean isCorrectBusinessPartner( - String referringConnectorClaim, String businessPartnerNumber) { + private static boolean isCorrectBusinessPartner(String referringConnectorClaim, String businessPartnerNumber) { return referringConnectorClaim.contains(businessPartnerNumber); } @@ -90,12 +93,13 @@ public boolean isLogAgreementEvaluation() { * @param policyContext context of the policy with claims * @return true if claims are from the constrained business partner */ - protected boolean evaluate( - final Operator operator, final Object rightValue, final PolicyContext policyContext) { + 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()) { - String problems = String.join(", ", policyContext.getProblems()); - String message = + var problems = String.join(", ", policyContext.getProblems()); + var message = format( "BusinessPartnerNumberValidation: Rejecting PolicyContext with problems. Problems: %s", problems); @@ -103,7 +107,7 @@ protected boolean evaluate( return false; } - final ParticipantAgent participantAgent = policyContext.getParticipantAgent(); + var participantAgent = policyContext.getContextData(ParticipantAgent.class); if (participantAgent == null) { return false; @@ -117,7 +121,7 @@ protected boolean evaluate( if (operator == Operator.EQ) { return isBusinessPartnerNumber(referringConnectorClaim, rightValue, policyContext); } else { - final String message = format(FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR, operator); + var message = format(FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR, operator); monitor.warning(message); policyContext.reportProblem(message); return false; @@ -126,11 +130,10 @@ protected boolean evaluate( @Nullable private String getReferringConnectorClaim(ParticipantAgent participantAgent) { - Object referringConnectorClaimObject = null; String referringConnectorClaim = null; var claims = participantAgent.getClaims(); - referringConnectorClaimObject = claims.get(REFERRING_CONNECTOR_CLAIM); + var referringConnectorClaimObject = claims.get(REFERRING_CONNECTOR_CLAIM); if (referringConnectorClaimObject instanceof String) { referringConnectorClaim = (String) referringConnectorClaimObject; @@ -144,13 +147,13 @@ private String getReferringConnectorClaim(ParticipantAgent participantAgent) { private boolean isBusinessPartnerNumber(String referringConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { if (businessPartnerNumber == null) { - final String message = format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, "null"); + var message = format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, "null"); monitor.warning(message); policyContext.reportProblem(message); return false; } - if (!(businessPartnerNumber instanceof String)) { - final String message = + if (!(businessPartnerNumber instanceof String businessPartnerNumberStr)) { + var message = format( FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, businessPartnerNumber.getClass().getName()); @@ -159,7 +162,6 @@ private boolean isBusinessPartnerNumber(String referringConnectorClaim, Object b return false; } - var businessPartnerNumberStr = (String) businessPartnerNumber; var agreement = policyContext.getContextData(ContractAgreement.class); var isCorrectBusinessPartner = isCorrectBusinessPartner(referringConnectorClaim, businessPartnerNumberStr); diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerDutyFunction.java similarity index 50% rename from edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java rename to edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerDutyFunction.java index 061d7fd7d..ba44fa0ce 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerDutyFunction.java @@ -1,24 +1,25 @@ /* - * 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. + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 * - * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.tractusx.edc.validation.businesspartner.functions; +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; @@ -28,7 +29,10 @@ /** * 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 { 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 new file mode 100644 index 000000000..b0c074086 --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerPermissionFunction.java @@ -0,0 +1,47 @@ +/* + * + * Copyright (c) 2023 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.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 new file mode 100644 index 000000000..56a02f96d --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/legacy/BusinessPartnerProhibitionFunction.java @@ -0,0 +1,47 @@ +/* + * + * Copyright (c) 2023 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.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/charts/tractusx-connector-legacy/subcharts/omejdn/templates/service.yaml b/edc-extensions/bpn-validation/bpn-validation-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 61% rename from charts/tractusx-connector-legacy/subcharts/omejdn/templates/service.yaml rename to edc-extensions/bpn-validation/bpn-validation-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 947e69742..80602ce92 100644 --- a/charts/tractusx-connector-legacy/subcharts/omejdn/templates/service.yaml +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -1,4 +1,6 @@ -# Copyright (c) 2023 Contributors to the Eclipse Foundation +# +# 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. @@ -15,20 +17,6 @@ # # SPDX-License-Identifier: Apache-2.0 # - ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "omejdn.fullname" . }} - labels: - {{- include "omejdn.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "omejdn.selectorLabels" . | nindent 4 }} +org.eclipse.tractusx.edc.validation.businesspartner.BusinessPartnerValidationExtension +org.eclipse.tractusx.edc.validation.businesspartner.defaults.DefaultStoreProviderExtension +org.eclipse.tractusx.edc.validation.businesspartner.LegacyBusinessPartnerValidationExtension diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/InMemoryBusinessPartnerStoreTest.java b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/InMemoryBusinessPartnerStoreTest.java new file mode 100644 index 000000000..3de2a4914 --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/InMemoryBusinessPartnerStoreTest.java @@ -0,0 +1,30 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.validation.businesspartner; + +import org.eclipse.tractusx.edc.validation.businesspartner.defaults.InMemoryBusinessPartnerStore; +import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; +import org.eclipse.tractusx.edc.validation.businesspartner.store.BusinessPartnerStoreTestBase; + +class InMemoryBusinessPartnerStoreTest extends BusinessPartnerStoreTestBase { + + + private final InMemoryBusinessPartnerStore store = new InMemoryBusinessPartnerStore(); + + @Override + protected BusinessPartnerStore getStore() { + return store; + } +} \ No newline at end of file diff --git a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/LegacyBusinessPartnerValidationExtensionTest.java similarity index 61% rename from edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java rename to edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/LegacyBusinessPartnerValidationExtensionTest.java index dcea3be41..209bc0c3f 100644 --- a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/LegacyBusinessPartnerValidationExtensionTest.java @@ -1,21 +1,22 @@ /* - * 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. + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 * - * SPDX-License-Identifier: Apache-2.0 */ package org.eclipse.tractusx.edc.validation.businesspartner; @@ -27,23 +28,23 @@ 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.BusinessPartnerPermissionFunction; +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 org.mockito.Mockito; 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 BusinessPartnerValidationExtensionTest { +class LegacyBusinessPartnerValidationExtensionTest { - private BusinessPartnerValidationExtension extension; + private LegacyBusinessPartnerValidationExtension extension; // mocks private ServiceExtensionContext serviceExtensionContext; @@ -61,7 +62,7 @@ void setup() { when(serviceExtensionContext.getMonitor()).thenReturn(monitor); - extension = new BusinessPartnerValidationExtension(ruleBindingRegistry, policyEngine); + extension = new LegacyBusinessPartnerValidationExtension(ruleBindingRegistry, policyEngine); } @Test @@ -71,11 +72,11 @@ void testRegisterDutyFunction() { extension.initialize(serviceExtensionContext); // verify - verify(policyEngine) + verify(policyEngine, times(3)) .registerFunction( anyString(), eq(Duty.class), - eq(BusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), + eq(LegacyBusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), any()); } @@ -86,11 +87,11 @@ void testRegisterPermissionFunction() { extension.initialize(serviceExtensionContext); // verify - verify(policyEngine, Mockito.times(1)) + verify(policyEngine, times(3)) .registerFunction( anyString(), eq(Permission.class), - eq(BusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), + eq(LegacyBusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), any()); } @@ -101,29 +102,29 @@ void testRegisterProhibitionFunction() { extension.initialize(serviceExtensionContext); // verify - verify(policyEngine, Mockito.times(1)) + verify(policyEngine, times(3)) .registerFunction( anyString(), eq(Prohibition.class), - eq(BusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), + eq(LegacyBusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), any()); } @Test void testLogConfiguration() { - when(serviceExtensionContext.getSetting(BusinessPartnerValidationExtension.BUSINESS_PARTNER_VALIDATION_LOG_AGREEMENT_VALIDATION, "true")).thenReturn("false"); + 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) + verify(policyEngine, times(3)) .registerFunction( anyString(), eq(Permission.class), - eq(BusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), + eq(LegacyBusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), captor.capture()); assertThat(captor.getValue().isLogAgreementEvaluation()).isFalse(); diff --git a/edc-extensions/business-partner-validation/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 similarity index 86% rename from edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java rename to edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java index d7ad4db9f..2427ea4dd 100644 --- a/edc-extensions/business-partner-validation/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 @@ -1,21 +1,22 @@ /* - * 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. + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 * - * SPDX-License-Identifier: Apache-2.0 */ package org.eclipse.tractusx.edc.validation.businesspartner.functions; @@ -26,6 +27,7 @@ import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.agent.ParticipantAgent; import org.eclipse.edc.spi.monitor.Monitor; +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; @@ -55,7 +57,7 @@ void beforeEach() { this.policyContext = Mockito.mock(PolicyContext.class); this.participantAgent = Mockito.mock(ParticipantAgent.class); - Mockito.when(policyContext.getParticipantAgent()).thenReturn(participantAgent); + Mockito.when(policyContext.getContextData(eq(ParticipantAgent.class))).thenReturn(participantAgent); validation = new AbstractBusinessPartnerValidation(monitor, true) { }; 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 new file mode 100644 index 000000000..c2379c592 --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerGroupFunctionTest.java @@ -0,0 +1,191 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.validation.businesspartner.functions; + +import org.eclipse.edc.policy.engine.spi.PolicyContext; +import org.eclipse.edc.policy.model.AtomicConstraint; +import org.eclipse.edc.policy.model.LiteralExpression; +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.StoreResult; +import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; +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.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +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.edc.policy.model.Operator.HAS_PART; +import static org.eclipse.edc.policy.model.Operator.IN; +import static org.eclipse.edc.policy.model.Operator.IS_A; +import static org.eclipse.edc.policy.model.Operator.IS_ALL_OF; +import static org.eclipse.edc.policy.model.Operator.IS_ANY_OF; +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.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; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class BusinessPartnerGroupFunctionTest { + + public static final String TEST_GROUP_1 = "test-group-1"; + public static final String TEST_GROUP_2 = "test-group-2"; + private static final String TEST_BPN = "BPN000TEST"; + private final PolicyContext context = mock(); + private BusinessPartnerGroupFunction function; + private BusinessPartnerStore store; + + @BeforeEach + void setUp() { + store = mock(); + function = new BusinessPartnerGroupFunction(store); + } + + @Test + @DisplayName("PolicyContext does not carry ParticipantAgent") + void evaluate_noParticipantAgentOnContext() { + reset(context); + assertThat(function.evaluate(EQ, "test-group", createPermission(EQ, List.of()), context)).isFalse(); + verify(context).reportProblem(eq("ParticipantAgent not found on PolicyContext")); + } + + @ParameterizedTest(name = "Invalid operator {0}") + @ArgumentsSource(InvalidOperatorProvider.class) + @DisplayName("Invalid operators, expect report in policy context") + void evaluate_invalidOperator(Operator invalidOperator) { + when(context.getContextData(eq(ParticipantAgent.class))).thenReturn(new ParticipantAgent(Map.of(), Map.of())); + assertThat(function.evaluate(invalidOperator, "test-group", createPermission(invalidOperator, List.of()), context)).isFalse(); + verify(context).reportProblem(endsWith("but was [" + invalidOperator.name() + "]")); + } + + @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"))); + + 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(); + assertThat(function.evaluate(EQ, true, createPermission(EQ, List.of("test-group")), context)).isFalse(); + assertThat(function.evaluate(EQ, new Object(), createPermission(EQ, List.of("test-group")), context)).isFalse(); + + verify(context).reportProblem("Right operand expected to be either String or a Collection, but was " + Integer.class); + verify(context).reportProblem("Right operand expected to be either String or a Collection, but was " + Long.class); + verify(context).reportProblem("Right operand expected to be either String or a Collection, but was " + Boolean.class); + verify(context).reportProblem("Right operand expected to be either String or a Collection, but was " + Object.class); + } + + @ParameterizedTest(name = "{1} :: {0}") + @ArgumentsSource(ValidOperatorProvider.class) + @DisplayName("Valid operators, evaluating different circumstances") + 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(store.resolveForBpn(TEST_BPN)).thenReturn(StoreResult.success(assignedBpn)); + assertThat(function.evaluate(operator, allowedGroups, createPermission(operator, allowedGroups), context)).isEqualTo(expectedOutcome); + } + + @Test + 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(store.resolveForBpn(TEST_BPN)).thenReturn(StoreResult.notFound("foobar")); + + assertThat(function.evaluate(operator, allowedGroups, createPermission(operator, allowedGroups), context)).isFalse(); + } + + @Test + 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(store.resolveForBpn(TEST_BPN)).thenReturn(StoreResult.success(Collections.emptyList())); + + assertThat(function.evaluate(operator, allowedGroups, createPermission(operator, allowedGroups), context)).isFalse(); + } + + private Permission createPermission(Operator op, List rightOperand) { + return Permission.Builder.newInstance() + .constraint(AtomicConstraint.Builder.newInstance() + .leftExpression(new LiteralExpression(BUSINESS_PARTNER_CONSTRAINT_KEY)) + .operator(op) + .rightExpression(new LiteralExpression(rightOperand)).build()) + .build(); + } + + private static class InvalidOperatorProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) throws Exception { + return Stream.of( + Arguments.of(GEQ), + Arguments.of(GT), + Arguments.of(HAS_PART), + Arguments.of(LT), + Arguments.of(LEQ), + Arguments.of(IS_A) + ); + } + } + + private static class ValidOperatorProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) { + return Stream.of( + Arguments.of("Matching groups", EQ, List.of(TEST_GROUP_1, TEST_GROUP_2), true), + Arguments.of("Disjoint groups", EQ, List.of("test-group", "different-group"), false), + Arguments.of("Overlapping groups", EQ, List.of("different-group"), false), + + Arguments.of("Disjoint groups", NEQ, List.of("different-group", "another-different-group"), true), + Arguments.of("Overlapping groups", NEQ, List.of(TEST_GROUP_1, "different-group"), false), + Arguments.of("Matching groups", NEQ, List.of(TEST_GROUP_1, TEST_GROUP_2), false), + + Arguments.of("Matching groups", IN, List.of(TEST_GROUP_1, TEST_GROUP_2), true), + Arguments.of("Overlapping groups", IN, List.of(TEST_GROUP_1, "different-group"), true), + Arguments.of("Disjoint groups", IN, List.of("different-group", "another-different-group"), false), + + Arguments.of("Disjoint groups", IS_ALL_OF, List.of("different-group", "another-different-group"), false), + Arguments.of("Matching groups", IS_ALL_OF, List.of(TEST_GROUP_1, TEST_GROUP_2), true), + Arguments.of("Overlapping groups", IS_ALL_OF, List.of(TEST_GROUP_1, TEST_GROUP_2, "different-group", "another-different-group"), false), + + + Arguments.of("Disjoint groups", IS_ANY_OF, List.of("different-group", "another-different-group"), false), + Arguments.of("Matching groups", IS_ANY_OF, List.of(TEST_GROUP_1, TEST_GROUP_2), true), + Arguments.of("Overlapping groups (1 overlap)", IS_ANY_OF, List.of(TEST_GROUP_1, "different-group", "another-different-group"), true), + Arguments.of("Overlapping groups (2 overlap)", IS_ANY_OF, List.of(TEST_GROUP_1, TEST_GROUP_2, "different-group", "another-different-group"), true) + ); + } + } +} \ No newline at end of file diff --git a/edc-extensions/bpn-validation/bpn-validation-core/src/testFixtures/java/org/eclipse/tractusx/edc/validation/businesspartner/store/BusinessPartnerStoreTestBase.java b/edc-extensions/bpn-validation/bpn-validation-core/src/testFixtures/java/org/eclipse/tractusx/edc/validation/businesspartner/store/BusinessPartnerStoreTestBase.java new file mode 100644 index 000000000..08ee267ff77 --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-core/src/testFixtures/java/org/eclipse/tractusx/edc/validation/businesspartner/store/BusinessPartnerStoreTestBase.java @@ -0,0 +1,83 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.validation.businesspartner.store; + +import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public abstract class BusinessPartnerStoreTestBase { + + @Test + void resolveForBpn() { + getStore().save("test-bpn", List.of("group1", "group2", "group3")); + assertThat(getStore().resolveForBpn("test-bpn").getContent()).containsExactly("group1", "group2", "group3"); + } + + @Test + void resolveForBpn_notExists() { + assertThat(getStore().resolveForBpn("test-bpn").succeeded()).isFalse(); + } + + @Test + void resolveForBpn_existsNoGroups() { + getStore().save("test-bpn", List.of()); + assertThat(getStore().resolveForBpn("test-bpn").getContent()).isNotNull().isEmpty(); + } + + @Test + void save() { + getStore().save("test-bpn", List.of("group1", "group2", "group3")); + assertThat(getStore().resolveForBpn("test-bpn").getContent()).containsExactly("group1", "group2", "group3"); + } + + @Test + void save_exists() { + getStore().save("test-bpn", List.of("group1", "group2", "group3")); + assertThat(getStore().save("test-bpn", List.of("group4")).succeeded()).isFalse(); + } + + @Test + void delete() { + var businessPartnerNumber = "test-bpn"; + getStore().save(businessPartnerNumber, List.of("group1", "group2", "group3")); + var delete = getStore().delete(businessPartnerNumber); + assertThat(delete.succeeded()).withFailMessage(delete::getFailureDetail).isTrue(); + } + + @Test + void delete_notExist() { + var businessPartnerNumber = "test-bpn"; + getStore().delete(businessPartnerNumber); + assertThat(getStore().resolveForBpn(businessPartnerNumber).succeeded()).isFalse(); + } + + @Test + void update() { + var businessPartnerNumber = "test-bpn"; + getStore().save(businessPartnerNumber, List.of("group1", "group2", "group3")); + assertThat(getStore().update(businessPartnerNumber, List.of("group4", "group5")).succeeded()).isTrue(); + } + + @Test + void update_notExists() { + assertThat(getStore().update("test-bpn", List.of("foo", "bar")).succeeded()).isFalse(); + } + + protected abstract BusinessPartnerStore getStore(); +} diff --git a/edc-extensions/business-partner-validation/build.gradle.kts b/edc-extensions/bpn-validation/bpn-validation-spi/build.gradle.kts similarity index 96% rename from edc-extensions/business-partner-validation/build.gradle.kts rename to edc-extensions/bpn-validation/bpn-validation-spi/build.gradle.kts index 6d5b6059a..01584c21c 100644 --- a/edc-extensions/business-partner-validation/build.gradle.kts +++ b/edc-extensions/bpn-validation/bpn-validation-spi/build.gradle.kts @@ -25,6 +25,7 @@ plugins { dependencies { implementation(project(":spi:core-spi")) api(libs.edc.spi.core) + api(libs.edc.spi.aggregateservices) implementation(libs.edc.spi.policy) implementation(libs.edc.spi.contract) implementation(libs.edc.spi.policyengine) diff --git a/edc-extensions/bpn-validation/bpn-validation-spi/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/spi/BusinessPartnerStore.java b/edc-extensions/bpn-validation/bpn-validation-spi/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/spi/BusinessPartnerStore.java new file mode 100644 index 000000000..473371a4c --- /dev/null +++ b/edc-extensions/bpn-validation/bpn-validation-spi/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/spi/BusinessPartnerStore.java @@ -0,0 +1,34 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.validation.businesspartner.spi; + +import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; +import org.eclipse.edc.spi.result.StoreResult; + +import java.util.List; + +@ExtensionPoint +public interface BusinessPartnerStore { + String NOT_FOUND_TEMPLATE = "BPN %s was not found"; + String ALREADY_EXISTS_TEMPLATE = "BPN %s already exists in database"; + + StoreResult> resolveForBpn(String businessPartnerNumber); + + StoreResult save(String businessPartnerNumber, List groups); + + StoreResult delete(String businessPartnerNumber); + + StoreResult update(String businessPartnerNumber, List groups); +} diff --git a/edc-extensions/business-partner-validation/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/bpn-validation/bpn-validation-spi/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 90% rename from edc-extensions/business-partner-validation/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/bpn-validation/bpn-validation-spi/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index eaadbabd1..427c43cda 100644 --- a/edc-extensions/business-partner-validation/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/bpn-validation/bpn-validation-spi/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -18,3 +18,4 @@ # SPDX-License-Identifier: Apache-2.0 # org.eclipse.tractusx.edc.validation.businesspartner.BusinessPartnerValidationExtension +org.eclipse.tractusx.edc.validation.businesspartner.defaults.DefaultStoreProviderExtension diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoData.java b/edc-extensions/bpn-validation/build.gradle.kts similarity index 73% rename from edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoData.java rename to edc-extensions/bpn-validation/build.gradle.kts index 9dc59a8b0..b866b837b 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoData.java +++ b/edc-extensions/bpn-validation/build.gradle.kts @@ -1,5 +1,4 @@ /* - * 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 @@ -17,10 +16,14 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.tractusx.edc.data.encryption.data; -public interface CryptoData { - byte[] getBytes(); +plugins { + `java-library` + `maven-publish` +} - String getBase64(); +dependencies { + api(project(":edc-extensions:bpn-validation:bpn-validation-spi")) + api(project(":edc-extensions:bpn-validation:bpn-validation-api")) + api(project(":edc-extensions:bpn-validation:bpn-validation-core")) } diff --git a/edc-extensions/hashicorp-vault/build.gradle.kts b/edc-extensions/bpn-validation/business-partner-store-sql/build.gradle.kts similarity index 58% rename from edc-extensions/hashicorp-vault/build.gradle.kts rename to edc-extensions/bpn-validation/business-partner-store-sql/build.gradle.kts index 42b59548a..277e66c57 100644 --- a/edc-extensions/hashicorp-vault/build.gradle.kts +++ b/edc-extensions/bpn-validation/business-partner-store-sql/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -18,16 +18,21 @@ */ plugins { - `maven-publish` `java-library` + `maven-publish` } dependencies { + implementation(project(":edc-extensions:bpn-validation:bpn-validation-spi")) + implementation(libs.edc.spi.core) - implementation(libs.edc.junit) - implementation(libs.bouncyCastle.bcpkixJdk18on) - implementation(libs.okhttp) - implementation("org.testcontainers:vault:1.18.3") - implementation("org.testcontainers:junit-jupiter:1.18.3") - testImplementation(libs.mockito.inline) + implementation(libs.edc.spi.transaction.datasource) + implementation(libs.edc.spi.transactionspi) + implementation(libs.edc.core.sql) + + + testImplementation(libs.edc.transaction.local) + testImplementation(testFixtures(libs.edc.core.sql)) + testImplementation(testFixtures(libs.edc.junit)) + testImplementation(testFixtures(project(":edc-extensions:bpn-validation:bpn-validation-core"))) } diff --git a/edc-extensions/edr-cache-sql/docs/schema.sql b/edc-extensions/bpn-validation/business-partner-store-sql/docs/schema.sql similarity index 54% rename from edc-extensions/edr-cache-sql/docs/schema.sql rename to edc-extensions/bpn-validation/business-partner-store-sql/docs/schema.sql index fe4ef2104..aa7b7a08f 100644 --- a/edc-extensions/edr-cache-sql/docs/schema.sql +++ b/edc-extensions/bpn-validation/business-partner-store-sql/docs/schema.sql @@ -11,12 +11,11 @@ -- Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation -- -CREATE TABLE IF NOT EXISTS edc_edr_cache + +CREATE TABLE edc_business_partner_group ( - 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 + bpn VARCHAR NOT NULL + CONSTRAINT edc_business_partner_group_pk + PRIMARY KEY, + groups JSON DEFAULT '[]'::JSON NOT NULL ); diff --git a/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/SqlBusinessPartnerGroupStoreExtension.java b/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/SqlBusinessPartnerGroupStoreExtension.java new file mode 100644 index 000000000..07aab14ff --- /dev/null +++ b/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/SqlBusinessPartnerGroupStoreExtension.java @@ -0,0 +1,64 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.validation.businesspartner.store; + +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.edc.sql.QueryExecutor; +import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry; +import org.eclipse.edc.transaction.spi.TransactionContext; +import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; +import org.eclipse.tractusx.edc.validation.businesspartner.store.sql.BusinessPartnerGroupStatements; +import org.eclipse.tractusx.edc.validation.businesspartner.store.sql.PostgresBusinessPartnerGroupStatements; +import org.eclipse.tractusx.edc.validation.businesspartner.store.sql.SqlBusinessPartnerStore; + +@Extension("Registers an SQL implementation for the BusinessPartnerGroupStore") +public class SqlBusinessPartnerGroupStoreExtension implements ServiceExtension { + + private static final String DEFAULT_DATASOURCE_NAME = "bpn"; + @Setting(value = "Datasource name for the SQL BusinessPartnerGroup store", defaultValue = DEFAULT_DATASOURCE_NAME) + private static final String DATASOURCE_SETTING_NAME = "edc.datasource.bpn.name"; + private static final String NAME = "SQL Business Partner Store"; + @Inject + private DataSourceRegistry dataSourceRegistry; + @Inject + private TransactionContext transactionContext; + @Inject + private TypeManager typeManager; + @Inject + private QueryExecutor queryExecutor; + @Inject(required = false) + private BusinessPartnerGroupStatements statements; + + @Provider + public BusinessPartnerStore sqlStore(ServiceExtensionContext context) { + var dataSourceName = context.getConfig().getString(DATASOURCE_SETTING_NAME, DEFAULT_DATASOURCE_NAME); + return new SqlBusinessPartnerStore(dataSourceRegistry, dataSourceName, transactionContext, typeManager.getMapper(), queryExecutor, getStatements()); + } + + @Override + public String name() { + return NAME; + } + + private BusinessPartnerGroupStatements getStatements() { + return statements == null ? new PostgresBusinessPartnerGroupStatements() : statements; + } +} diff --git a/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/BusinessPartnerGroupStatements.java b/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/BusinessPartnerGroupStatements.java new file mode 100644 index 000000000..64663b9e4 --- /dev/null +++ b/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/BusinessPartnerGroupStatements.java @@ -0,0 +1,43 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.validation.businesspartner.store.sql; + +/** + * Statement templates and SQL table+column names required for the {@link SqlBusinessPartnerStore} + */ +public interface BusinessPartnerGroupStatements { + + default String getBpnColumn() { + return "bpn"; + } + + default String getGroupsColumn() { + return "groups"; + } + + default String getTable() { + return "edc_business_partner_group"; + } + + String findByBpnTemplate(); + + String insertTemplate(); + + String deleteTemplate(); + + String countQuery(); + + String updateTemplate(); +} diff --git a/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/PostgresBusinessPartnerGroupStatements.java b/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/PostgresBusinessPartnerGroupStatements.java new file mode 100644 index 000000000..51ca75c25 --- /dev/null +++ b/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/PostgresBusinessPartnerGroupStatements.java @@ -0,0 +1,55 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.validation.businesspartner.store.sql; + +import org.eclipse.edc.sql.dialect.PostgresDialect; +import org.jetbrains.annotations.NotNull; + +import static java.lang.String.format; + +/** + * Postgres-specific statement templates for the {@link SqlBusinessPartnerStore} + */ +public class PostgresBusinessPartnerGroupStatements implements BusinessPartnerGroupStatements { + @Override + public String findByBpnTemplate() { + return format("SELECT %s from %s WHERE %s = ?", getGroupsColumn(), getTable(), getBpnColumn()); + } + + @Override + public String insertTemplate() { + return format("INSERT INTO %s (%s, %s) VALUES (?, ?%s)", getTable(), getBpnColumn(), getGroupsColumn(), getFormatJsonOperator()); + } + + @Override + public String deleteTemplate() { + return format("DELETE FROM %s WHERE %s = ?", getTable(), getBpnColumn()); + } + + @Override + public String countQuery() { + return format("SELECT COUNT (*) FROM %s WHERE %s = ?", getTable(), getBpnColumn()); + } + + @Override + public String updateTemplate() { + return format("UPDATE %s SET %s=?%s WHERE %s=?", getTable(), getGroupsColumn(), getFormatJsonOperator(), getBpnColumn()); + } + + @NotNull + private String getFormatJsonOperator() { + return PostgresDialect.getJsonCastOperator(); + } +} diff --git a/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/SqlBusinessPartnerStore.java b/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/SqlBusinessPartnerStore.java new file mode 100644 index 000000000..ba872e4b0 --- /dev/null +++ b/edc-extensions/bpn-validation/business-partner-store-sql/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/SqlBusinessPartnerStore.java @@ -0,0 +1,122 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.validation.businesspartner.store.sql; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.spi.persistence.EdcPersistenceException; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.sql.QueryExecutor; +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.validation.businesspartner.spi.BusinessPartnerStore; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.Objects; + +public class SqlBusinessPartnerStore extends AbstractSqlStore implements BusinessPartnerStore { + private static final TypeReference> LIST_OF_STRING = new TypeReference<>() { + }; + private final BusinessPartnerGroupStatements statements; + + public SqlBusinessPartnerStore(DataSourceRegistry dataSourceRegistry, String dataSourceName, TransactionContext transactionContext, + ObjectMapper objectMapper, QueryExecutor queryExecutor, BusinessPartnerGroupStatements statements) { + super(dataSourceRegistry, dataSourceName, transactionContext, objectMapper, queryExecutor); + this.statements = statements; + } + + @Override + public StoreResult> resolveForBpn(String businessPartnerNumber) { + Objects.requireNonNull(businessPartnerNumber); + return transactionContext.execute(() -> { + try (var connection = getConnection()) { + var sql = statements.findByBpnTemplate(); + var list = queryExecutor.single(connection, true, this::mapJson, sql, businessPartnerNumber); + return list == null ? + StoreResult.notFound(NOT_FOUND_TEMPLATE.formatted(businessPartnerNumber)) : + StoreResult.success(list); + } catch (SQLException e) { + throw new EdcPersistenceException(e); + } + }); + } + + @Override + public StoreResult save(String businessPartnerNumber, List groups) { + Objects.requireNonNull(businessPartnerNumber); + return transactionContext.execute(() -> { + try (var connection = getConnection()) { + if (exists(businessPartnerNumber, connection)) { + return StoreResult.alreadyExists(ALREADY_EXISTS_TEMPLATE.formatted(businessPartnerNumber)); + } + var sql = statements.insertTemplate(); + queryExecutor.execute(connection, sql, businessPartnerNumber, toJson(groups)); + return StoreResult.success(); + } catch (SQLException e) { + throw new EdcPersistenceException(e); + } + }); + } + + + @Override + public StoreResult delete(String businessPartnerNumber) { + Objects.requireNonNull(businessPartnerNumber); + return transactionContext.execute(() -> { + try (var connection = getConnection()) { + if (!exists(businessPartnerNumber, connection)) { + return StoreResult.notFound(NOT_FOUND_TEMPLATE.formatted(businessPartnerNumber)); + } + var sql = statements.deleteTemplate(); + queryExecutor.execute(connection, sql, businessPartnerNumber); + return StoreResult.success(); + } catch (SQLException e) { + throw new EdcPersistenceException(e); + } + }); + } + + @Override + public StoreResult update(String businessPartnerNumber, List groups) { + Objects.requireNonNull(businessPartnerNumber); + return transactionContext.execute(() -> { + try (var connection = getConnection()) { + if (!exists(businessPartnerNumber, connection)) { + return StoreResult.notFound(NOT_FOUND_TEMPLATE.formatted(businessPartnerNumber)); + } + var sql = statements.updateTemplate(); + queryExecutor.execute(connection, sql, toJson(groups), businessPartnerNumber); + return StoreResult.success(); + } catch (SQLException e) { + throw new EdcPersistenceException(e); + } + }); + } + + private List mapJson(ResultSet resultSet) throws SQLException { + return fromJson(resultSet.getString(statements.getGroupsColumn()), LIST_OF_STRING); + } + + private boolean exists(String businessPartnerNumber, Connection connection) { + var countQuery = statements.countQuery(); + try (var stream = queryExecutor.query(connection, false, r -> r.getInt("COUNT"), countQuery, businessPartnerNumber)) { + return stream.findFirst().orElse(0) > 0; + } + } +} diff --git a/edc-extensions/bpn-validation/business-partner-store-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/bpn-validation/business-partner-store-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..eae3fe7cb --- /dev/null +++ b/edc-extensions/bpn-validation/business-partner-store-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,20 @@ +# +# 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 +# +org.eclipse.tractusx.edc.validation.businesspartner.store.SqlBusinessPartnerGroupStoreExtension 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 new file mode 100644 index 000000000..2ab1a79da --- /dev/null +++ b/edc-extensions/bpn-validation/business-partner-store-sql/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/store/sql/SqlBusinessPartnerStoreTest.java @@ -0,0 +1,52 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.validation.businesspartner.store.sql; + +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.sql.QueryExecutor; +import org.eclipse.edc.sql.testfixtures.PostgresqlStoreSetupExtension; +import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; +import org.eclipse.tractusx.edc.validation.businesspartner.store.BusinessPartnerStoreTestBase; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +@ExtendWith(PostgresqlStoreSetupExtension.class) +class SqlBusinessPartnerStoreTest extends BusinessPartnerStoreTestBase { + private final TypeManager typeManager = new TypeManager(); + private final BusinessPartnerGroupStatements statements = new PostgresBusinessPartnerGroupStatements(); + private SqlBusinessPartnerStore store; + + @BeforeEach + void setUp(PostgresqlStoreSetupExtension extension, QueryExecutor queryExecutor) throws IOException { + store = new SqlBusinessPartnerStore(extension.getDataSourceRegistry(), extension.getDatasourceName(), extension.getTransactionContext(), typeManager.getMapper(), queryExecutor, statements); + var schema = Files.readString(Paths.get("./docs/schema.sql")); + extension.runQuery(schema); + } + + + @AfterEach + void tearDown(PostgresqlStoreSetupExtension extension) { + extension.runQuery("DROP TABLE " + statements.getTable() + " CASCADE"); + } + + protected BusinessPartnerStore getStore() { + return store; + } +} \ No newline at end of file diff --git a/edc-extensions/build.gradle.kts b/edc-extensions/build.gradle.kts index f047330fd..a6769cfcd 100644 --- a/edc-extensions/build.gradle.kts +++ b/edc-extensions/build.gradle.kts @@ -22,11 +22,10 @@ plugins { } dependencies { - implementation(project(":edc-extensions:business-partner-validation")) + implementation(project(":edc-extensions:bpn-validation")) implementation(project(":edc-extensions:cx-oauth2")) implementation(project(":edc-extensions:data-encryption")) implementation(project(":edc-extensions:dataplane-selector-configuration")) - implementation(project(":edc-extensions:hashicorp-vault")) implementation(project(":edc-extensions:postgresql-migration")) implementation(project(":edc-extensions:provision-additional-headers")) implementation(project(":edc-extensions:transferprocess-sftp-client")) diff --git a/edc-extensions/business-partner-validation/README.md b/edc-extensions/business-partner-validation/README.md deleted file mode 100644 index 339417771..000000000 --- a/edc-extensions/business-partner-validation/README.md +++ /dev/null @@ -1,189 +0,0 @@ -# Business Partner Validation Extension - -Using the Business Partner Validation Extension it's possible to add configurable validation against -BPNs in the `ContractDefinition.AccessPolicy`. Using a BPN in `ContractDefinition.ContractPolicy` is possible, too, but once the contract is complete there is no policy enforcement in place from the EDC. - -It is recommended to have a basic understanding of the EDC contract/policy domain before using this extension. The -corresponding documentation can be found in the [EDC GitHub Repository](https://github.com/eclipse-edc/Connector). - -The business partner number of another connector is part of its DAPS token. Once a BPN constraint is used in an access -policy the connector checks the token before sending out contract offers. - -Example of business partner constraint: - -```json -{ - "leftExpression": { - "value": "BusinessPartner" - }, - "rightExpression": { - "value": "BPNLCDQ90000X42KU" - }, - "operator": "EQ" -} -``` - -The `leftExpression` must always contain 'BusinessPartner', so that the policy functions of this extension are invoked. -Additionally, the only `operator` that is supported by these policy functions is 'EQ'. Finally, the `rightExpression` -must contain the Business Partner Number. - -## Single BusinessPartnerNumber example - -The most simple BPN policy would allow the usage of certain data to a single Business Partner. An example `Policy` is -shown below. In this example the `edctype` properties are added, so that this policy may even be sent to the Management API. - -```json -{ - "uid": "", - "prohibitions": [], - "obligations": [], - "permissions": [ - { - "edctype": "dataspaceconnector:permission", - "action": { - "type": "USE" - }, - "constraints": [ - { - "edctype": "AtomicConstraint", - "leftExpression": { - "edctype": "dataspaceconnector:literalexpression", - "value": "BusinessPartnerNumber" - }, - "rightExpression": { - "edctype": "dataspaceconnector:literalexpression", - "value": "" - }, - "operator": "EQ" - } - ] - } - ] -} -``` - -## Multiple BusinessPartnerNumber example - -To define multiple BPN and allow multiple participants to use the data the `orconstraint` should be used. -It will permit the constraints contained to be evaluated using the `OR` operator. - -```json -{ - "permissions": [ - { - "edctype": "dataspaceconnector:permission", - "action": { - "type": "USE" - }, - "constraints": [ - { - "edctype": "dataspaceconnector:orconstraint", - "constraints": [ - { - "edctype": "AtomicConstraint", - "leftExpression": { - "edctype": "dataspaceconnector:literalexpression", - "value": "BusinessPartnerNumber" - }, - "rightExpression": { - "edctype": "dataspaceconnector:literalexpression", - "value": "" - }, - "operator": "EQ" - }, - { - "edctype": "AtomicConstraint", - "leftExpression": { - "edctype": "dataspaceconnector:literalexpression", - "value": "BusinessPartnerNumber" - }, - "rightExpression": { - "edctype": "dataspaceconnector:literalexpression", - "value": "" - }, - "operator": "EQ" - }, - - ... - - // other constraints can be added - ] - } - ], - "duties": [] - } - ] -} -``` - -## Important: EDC Policies are input sensitive - -Please be aware that the EDC ignores all Rules and Constraint it does not understand. This could cause your constrained policies to be public. - -### Example 1 for accidentially public - -```json -{ - "uid": "1", - "prohibitions": [], - "obligations": [], - "permissions": [ - { - "edctype": "dataspaceconnector:permission", - "action": { - "type": "MY-USE" - }, - "constraints": [ - { - "edctype": "AtomicConstraint", - "leftExpression": { - "edctype": "dataspaceconnector:literalexpression", - "value": "BusinessPartnerNumber" - }, - "rightExpression": { - "edctype": "dataspaceconnector:literalexpression", - "value": "BPNLCDQ90000X42KU" - }, - "operator": "EQ" - } - ] - } - ] -} -``` - -This policy is public available, even though the constraint is described correct. The reason is, that this extension only registeres the Policy.Action `USE` within the EDC. Any other Action Type will have the EDC ignore the corresponding permission, hence interpret the polics as public policy. - -### Example 2 for accidentially public - -```json -{ - "uid": "1", - "prohibitions": [], - "obligations": [], - "permissions": [ - { - "edctype": "dataspaceconnector:permission", - "action": { - "type": "USE" - }, - "constraints": [ - { - "edctype": "AtomicConstraint", - "leftExpression": { - "edctype": "dataspaceconnector:literalexpression", - "value": "BusinesPartnerNumber" - }, - "rightExpression": { - "edctype": "dataspaceconnector:literalexpression", - "value": "BPNLCDQ90000X42KU" - }, - "operator": "EQ" - } - ] - } - ] -} -``` - -This policy is public available, too. The cause is a typo in the left-expression of the constraint. This extension only registers the `Constraint.LeftExpression` `BusinessPartnerNumber` within the EDC. Any other term will have the EDC ignore the corresponding constraint, hence interpret the policies as public policy. diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java deleted file mode 100644 index d88293a72..000000000 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java +++ /dev/null @@ -1,110 +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.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.monitor.Monitor; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerDutyFunction; -import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerPermissionFunction; -import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerProhibitionFunction; - -import static org.eclipse.edc.policy.engine.spi.PolicyEngine.ALL_SCOPES; - -public class BusinessPartnerValidationExtension 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 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 BusinessPartnerValidationExtension() { - } - - public BusinessPartnerValidationExtension( - 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) { - - final Monitor monitor = context.getMonitor(); - - var logAgreementEvaluation = logAgreementEvaluationSetting(context); - - final BusinessPartnerDutyFunction dutyFunction = new BusinessPartnerDutyFunction(monitor, logAgreementEvaluation); - final BusinessPartnerPermissionFunction permissionFunction = - new BusinessPartnerPermissionFunction(monitor, logAgreementEvaluation); - final BusinessPartnerProhibitionFunction prohibitionFunction = - new BusinessPartnerProhibitionFunction(monitor, logAgreementEvaluation); - - ruleBindingRegistry.bind("USE", ALL_SCOPES); - ruleBindingRegistry.bind(BUSINESS_PARTNER_CONSTRAINT_KEY, ALL_SCOPES); - - policyEngine.registerFunction( - ALL_SCOPES, Duty.class, BUSINESS_PARTNER_CONSTRAINT_KEY, dutyFunction); - policyEngine.registerFunction( - ALL_SCOPES, Permission.class, BUSINESS_PARTNER_CONSTRAINT_KEY, permissionFunction); - policyEngine.registerFunction( - ALL_SCOPES, Prohibition.class, BUSINESS_PARTNER_CONSTRAINT_KEY, prohibitionFunction); - } - - 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/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java deleted file mode 100644 index b6713c477..000000000 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.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.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.monitor.Monitor; - -/** - * AtomicConstraintFunction to validate business partner numbers for edc permissions. - */ -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/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java deleted file mode 100644 index 79e318741..000000000 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.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.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.Prohibition; -import org.eclipse.edc.spi.monitor.Monitor; - -/** - * AtomicConstraintFunction to validate business partner numbers for edc prohibitions. - */ -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/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrController.java b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrController.java deleted file mode 100644 index 557fa3a95..000000000 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrController.java +++ /dev/null @@ -1,113 +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 - * - */ - -package org.eclipse.tractusx.edc.api.cp.adapter; - -import jakarta.json.JsonObject; -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.QueryParam; -import jakarta.ws.rs.core.MediaType; -import org.eclipse.edc.api.model.IdResponseDto; -import org.eclipse.edc.jsonld.spi.JsonLd; -import org.eclipse.edc.spi.EdcException; -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.edr.EndpointDataReference; -import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import org.eclipse.edc.web.spi.exception.InvalidRequestException; -import org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; -import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService; - -import java.util.List; -import java.util.stream.Collectors; - -import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.exceptionMapper; - -@Consumes({ MediaType.APPLICATION_JSON }) -@Produces({ MediaType.APPLICATION_JSON }) -@Path("/edrs") -public class AdapterEdrController implements AdapterEdrApi { - - private final AdapterTransferProcessService adapterTransferProcessService; - private final TypeTransformerRegistry transformerRegistry; - private final JsonLd jsonLdService; - - private Monitor monitor; - - public AdapterEdrController(AdapterTransferProcessService adapterTransferProcessService, JsonLd jsonLdService, TypeTransformerRegistry transformerRegistry) { - this.adapterTransferProcessService = adapterTransferProcessService; - this.jsonLdService = jsonLdService; - this.transformerRegistry = transformerRegistry; - } - - @POST - @Override - public JsonObject initiateEdrNegotiation(JsonObject requestObject) { - var edrNegotiationRequest = jsonLdService.expand(requestObject) - .compose(expanded -> transformerRegistry.transform(expanded, NegotiateEdrRequestDto.class)) - .compose(dto -> transformerRegistry.transform(dto, NegotiateEdrRequest.class)) - .orElseThrow(InvalidRequestException::new); - - var contractNegotiation = adapterTransferProcessService.initiateEdrNegotiation(edrNegotiationRequest).orElseThrow(exceptionMapper(NegotiateEdrRequest.class)); - - var responseDto = IdResponseDto.Builder.newInstance() - .id(contractNegotiation.getId()) - .createdAt(contractNegotiation.getCreatedAt()) - .build(); - - return transformerRegistry.transform(responseDto, JsonObject.class) - .compose(jsonLdService::compact) - .orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail())); - } - - @GET - @Override - public List queryEdrs(@QueryParam("assetId") String assetId, @QueryParam("agreementId") String agreementId) { - if (assetId == null && agreementId == null) { - throw new InvalidRequestException("At least one of this query parameter is required [assetId,agreementId]"); - } - return adapterTransferProcessService.findByAssetAndAgreement(assetId, agreementId) - .orElseThrow(exceptionMapper(EndpointDataReferenceEntry.class)) - .stream() - .map(edrCached -> transformerRegistry.transform(edrCached, JsonObject.class) - .compose(jsonLdService::compact)) - .peek(this::logIfError) - .filter(Result::succeeded) - .map(Result::getContent) - .collect(Collectors.toList()); - } - - @GET - @Path("/{id}") - @Override - public JsonObject getEdr(@PathParam("id") String transferProcessId) { - var edr = adapterTransferProcessService.findByTransferProcessId(transferProcessId).orElseThrow(exceptionMapper(EndpointDataReference.class, transferProcessId)); - return transformerRegistry.transform(edr, DataAddress.class) - .compose(dataAddress -> transformerRegistry.transform(dataAddress, JsonObject.class)) - .compose(jsonLdService::compact) - .orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail())); - } - - private void logIfError(Result result) { - result.onFailure(f -> monitor.warning(f.getFailureDetail())); - } -} diff --git a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImpl.java b/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImpl.java deleted file mode 100644 index 90a3290f6..000000000 --- a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImpl.java +++ /dev/null @@ -1,113 +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 - * - */ - -package org.eclipse.tractusx.edc.cp.adapter.callback; - -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.contract.spi.types.negotiation.ContractRequestData; -import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService; -import org.eclipse.edc.service.spi.result.ServiceResult; -import org.eclipse.edc.spi.query.Criterion; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; -import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService; - -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static java.lang.String.format; -import static org.eclipse.edc.service.spi.result.ServiceResult.notFound; -import static org.eclipse.edc.service.spi.result.ServiceResult.success; - -public class AdapterTransferProcessServiceImpl implements AdapterTransferProcessService { - - 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 ContractNegotiationService contractNegotiationService; - - private final EndpointDataReferenceCache endpointDataReferenceCache; - - public AdapterTransferProcessServiceImpl(ContractNegotiationService contractNegotiationService, EndpointDataReferenceCache endpointDataReferenceCache) { - this.contractNegotiationService = contractNegotiationService; - this.endpointDataReferenceCache = endpointDataReferenceCache; - } - - @Override - public ServiceResult initiateEdrNegotiation(NegotiateEdrRequest request) { - var contractNegotiation = contractNegotiationService.initiateNegotiation(createContractRequest(request)); - return success(contractNegotiation); - } - - private ContractRequest createContractRequest(NegotiateEdrRequest request) { - var callbacks = Stream.concat(request.getCallbackAddresses().stream(), Stream.of(LOCAL_CALLBACK)).collect(Collectors.toList()); - - var requestData = ContractRequestData.Builder.newInstance() - .contractOffer(request.getOffer()) - .protocol(request.getProtocol()) - .counterPartyAddress(request.getConnectorAddress()) - .connectorId(request.getConnectorId()) - .build(); - - return ContractRequest.Builder.newInstance() - .requestData(requestData) - .callbackAddresses(callbacks).build(); - } - - @Override - public ServiceResult findByTransferProcessId(String transferProcessId) { - var edr = endpointDataReferenceCache.resolveReference(transferProcessId); - return Optional.ofNullable(edr) - .map(ServiceResult::success) - .orElse(notFound(format("No Edr found associated to the transfer process with id: %s", transferProcessId))); - } - - @Override - public ServiceResult> findByAssetAndAgreement(String assetId, String agreementId) { - var results = queryEdrs(assetId, agreementId).collect(Collectors.toList()); - return success(results); - } - - private Stream queryEdrs(String assetId, String agreementId) { - var queryBuilder = QuerySpec.Builder.newInstance(); - if (assetId != null) { - queryBuilder.filter(fieldFilter("assetId", assetId)); - } - if (agreementId != null) { - queryBuilder.filter(fieldFilter("agreementId", agreementId)); - } - return endpointDataReferenceCache.queryForEntries(queryBuilder.build()); - } - - - private Criterion fieldFilter(String field, String value) { - return Criterion.Builder.newInstance() - .operandLeft(field) - .operator("=") - .operandRight(value) - .build(); - } -} diff --git a/edc-extensions/cx-policy/build.gradle.kts b/edc-extensions/cx-policy/build.gradle.kts index b146181fc..4b7ffd4cf 100644 --- a/edc-extensions/cx-policy/build.gradle.kts +++ b/edc-extensions/cx-policy/build.gradle.kts @@ -17,6 +17,7 @@ plugins { } dependencies { + implementation(project(":spi:core-spi")) implementation(project(":spi:ssi-spi")) implementation(libs.edc.spi.policyengine) implementation(libs.jakartaJson) 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 index a7f27fe8f..8d6d5241f 100644 --- 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 @@ -18,8 +18,12 @@ 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; @@ -35,16 +39,26 @@ public class SummaryConstraintFunctionsProvider { /** * Mappings from policy constraint left operand values to the corresponding item value in the summary VP. */ - static final Map CREDENTIAL_MAPPINGS = Map.of( - "Membership", "MembershipCredential", - "Dismantler", "DismantlerCredential", - "FrameworkAgreement.pcf", "PcfCredential", - "FrameworkAgreement.sustainability", "SustainabilityCredential", - "FrameworkAgreement.quality", "QualityCredential", - "FrameworkAgreement.traceability", "TraceabilityCredential", - "FrameworkAgreement.behavioraltwin", "BehaviorTwinCredential", - "BPN", "BpnCredential" - ); + 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. @@ -84,6 +98,11 @@ public static void registerBindings(RuleBindingRegistry registry) { 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/data-encryption/README.md b/edc-extensions/data-encryption/README.md deleted file mode 100644 index 586dad775..000000000 --- a/edc-extensions/data-encryption/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Data Encryption Extension - -The Eclipse Dataspace Connector encrypts sensitive information inside a token it sends to other applications (from possibly other companies). This extension implements the encryption of this data and should be used with secure keys and algorithms at all times. - -## Algorithm Configuration - -| Key | Description | Mandatory | Default | -|:--------------------------------------------|:-----------------------------------------------------------------------------------------------------------------|-----------|------------------| -| edc.data.encryption.algorithm | Algorithm for encryption and decryption. Must be ether 'AES' or 'NONE'. | | AES | - -## Strategies - -### 1. AES - -The Advanced Encryption Standard (AES) is the default encryption algorithm. For Authenticated Encryption with Associated Data (AEAD) it uses the Galois/Counter Mode or GCM. - -When using AES-GCM the key length must be ether 128-, 196- or 256bit. Keys must be stored stored Base64 encoded in the Vault, separated by a comma. - -It's possible to generate Keys using OpenSSL - -```bash -# 128 Bit -openssl rand -base64 16 - -# 196 Bit -openssl rand -base64 24 - -# 256 Bit -openssl rand -base64 32 -``` - -#### AES Configuration - -| Key | Description | Mandatory | Default | -|:------------------------------------|:----------------------------------------------------------------------------|-----------|---------| -| edc.data.encryption.keys.alias | Symmetric Keys stored in the Vault under the configured alias. | X | | -| edc.data.encryption.caching.enabled | Enable caching to request only keys from the vault after the cache expires. | | false | -| edc.data.encryption.caching.seconds | Duration in seconds until the cache expires. | | 3600 | - -### 2. NONE - -This strategy does apply no encryption at all and should only be used for debugging purposes. Using NONE encryption may leak sensitive data to other connectors! diff --git a/edc-extensions/data-encryption/build.gradle.kts b/edc-extensions/data-encryption/build.gradle.kts index 79603708c..0cf1aefe1 100644 --- a/edc-extensions/data-encryption/build.gradle.kts +++ b/edc-extensions/data-encryption/build.gradle.kts @@ -1,29 +1,24 @@ /* - * Copyright (c) 2023 Contributors to the Eclipse Foundation + * 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 * - * This program and the accompanying materials are 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * - * SPDX-License-Identifier: Apache-2.0 */ plugins { - `maven-publish` `java-library` } dependencies { api(libs.edc.spi.core) implementation(libs.edc.spi.dataplane.transfer) - implementation(libs.bouncyCastle.bcpkixJdk18on) + implementation(libs.edc.dpf.transfer) + testImplementation(libs.edc.junit) } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtil.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/ArrayUtil.java similarity index 84% rename from edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtil.java rename to edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/ArrayUtil.java index 63c22a39f..973f672a7 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtil.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/ArrayUtil.java @@ -17,7 +17,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.tractusx.edc.data.encryption.util; +package org.eclipse.tractusx.edc.data.encryption; public class ArrayUtil { @@ -31,12 +31,6 @@ public static byte[] concat(byte[] a, byte[] b) { return c; } - public static String byteArrayToHex(byte[] a) { - StringBuilder sb = new StringBuilder(a.length * 2); - for (byte b : a) sb.append(String.format("%02x", b)); - return sb.toString(); - } - 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"); diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtension.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtension.java deleted file mode 100644 index 074956eac..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtension.java +++ /dev/null @@ -1,141 +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; - -import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; -import org.eclipse.edc.runtime.metamodel.annotation.Provides; -import org.eclipse.edc.runtime.metamodel.annotation.Requires; -import org.eclipse.edc.spi.EdcException; -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.data.encryption.encrypter.AesDataEncrypterConfiguration; -import org.eclipse.tractusx.edc.data.encryption.encrypter.DataEncrypterFactory; -import org.eclipse.tractusx.edc.data.encryption.key.AesKey; -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactory; -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactoryImpl; -import org.eclipse.tractusx.edc.data.encryption.provider.AesKeyProvider; - -import java.time.Duration; -import java.util.List; -import java.util.stream.Collectors; - -@Provides({DataEncrypter.class}) -@Requires({Vault.class}) -public class DataEncryptionExtension implements ServiceExtension { - - public static final String EXTENSION_NAME = "Data Encryption Extension"; - - public static final String ENCRYPTION_KEY_SET = "edc.data.encryption.keys.alias"; - - public static final String ENCRYPTION_ALGORITHM = "edc.data.encryption.algorithm"; - public static final String ENCRYPTION_ALGORITHM_DEFAULT = DataEncrypterFactory.AES_ALGORITHM; - - public static final String CACHING_ENABLED = "edc.data.encryption.caching.enabled"; - public static final boolean CACHING_ENABLED_DEFAULT = false; - - public static final String CACHING_SECONDS = "edc.data.encryption.caching.seconds"; - public static final int CACHING_SECONDS_DEFAULT = 3600; - - private static final CryptoKeyFactory CRYPTO_KEY_FACTORY = new CryptoKeyFactoryImpl(); - - private Monitor monitor; - private Vault vault; - private ServiceExtensionContext context; - - private static AesDataEncrypterConfiguration createAesConfiguration( - ServiceExtensionContext context) { - final String key = context.getSetting(ENCRYPTION_KEY_SET, null); - if (key == null) { - throw new EdcException(EXTENSION_NAME + ": Missing setting " + ENCRYPTION_KEY_SET); - } - - final boolean cachingEnabled = context.getSetting(CACHING_ENABLED, CACHING_ENABLED_DEFAULT); - final int cachingSeconds = context.getSetting(CACHING_SECONDS, CACHING_SECONDS_DEFAULT); - - return new AesDataEncrypterConfiguration( - key, cachingEnabled, Duration.ofSeconds(cachingSeconds)); - } - - @Override - public String name() { - return EXTENSION_NAME; - } - - @Override - public void start() { - - final String algorithm = context.getSetting(ENCRYPTION_ALGORITHM, ENCRYPTION_ALGORITHM_DEFAULT); - - if (DataEncrypterFactory.NONE.equalsIgnoreCase(algorithm)) { - return; // no start-up checks for NONE algorithm - } - - if (DataEncrypterFactory.AES_ALGORITHM.equals(algorithm)) { - - final AesDataEncrypterConfiguration configuration = createAesConfiguration(context); - final String keyAlias = configuration.getKeySetAlias(); - final String keySecret = vault.resolveSecret(keyAlias); - if (keySecret == null || keySecret.isEmpty()) { - throw new EdcException( - EXTENSION_NAME + ": No vault key secret found for alias " + keyAlias); - } - - try { - final AesKeyProvider aesKeyProvider = new AesKeyProvider(vault, keyAlias, CRYPTO_KEY_FACTORY); - final List keys = aesKeyProvider.getDecryptionKeySet().collect(Collectors.toList()); - monitor.debug( - String.format( - "Started " + EXTENSION_NAME + ": Found %s registered AES keys in vault.", - keys.size())); - } catch (Exception e) { - throw new EdcException( - EXTENSION_NAME + ": AES keys from vault must be comma separated and Base64 encoded.", - e); - } - } - } - - @Override - public void initialize(ServiceExtensionContext context) { - this.context = context; - this.monitor = context.getMonitor(); - this.vault = context.getService(Vault.class); - final DataEncrypterFactory factory = new DataEncrypterFactory(vault, monitor, CRYPTO_KEY_FACTORY); - - final DataEncrypter dataEncrypter; - final String algorithm = context.getSetting(ENCRYPTION_ALGORITHM, ENCRYPTION_ALGORITHM_DEFAULT); - if (DataEncrypterFactory.NONE.equalsIgnoreCase(algorithm)) { - dataEncrypter = factory.createNoneEncrypter(); - } else if (DataEncrypterFactory.AES_ALGORITHM.equalsIgnoreCase(algorithm)) { - final AesDataEncrypterConfiguration configuration = createAesConfiguration(context); - dataEncrypter = factory.createAesEncrypter(configuration); - } else { - final String msg = String.format(DataEncryptionExtension.EXTENSION_NAME + ": Unsupported encryption algorithm '%s'. Supported algorithms are '%s', '%s'.", - algorithm, - DataEncrypterFactory.AES_ALGORITHM, - DataEncrypterFactory.NONE); - throw new EdcException(msg); - } - - context.registerService(DataEncrypter.class, dataEncrypter); - } -} 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 new file mode 100644 index 000000000..28ca9afcd --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/TxEncryptorExtension.java @@ -0,0 +1,73 @@ +/* + * 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 + * + */ + +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(); + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..bfa3aacf0 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/aes/AesEncryptor.java @@ -0,0 +1,142 @@ +/* + * 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 + * + */ + +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/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/CryptoAlgorithm.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/CryptoAlgorithm.java deleted file mode 100644 index d457677b9..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/CryptoAlgorithm.java +++ /dev/null @@ -1,39 +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.algorithms; - -import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; -import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKey; - -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; - -public interface CryptoAlgorithm { - EncryptedData encrypt(DecryptedData data, T key) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, - NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException; - - DecryptedData decrypt(EncryptedData data, T key) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, - NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException; -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java deleted file mode 100644 index 6f463fc82..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2023 ZF Friedrichshafen AG - * 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.algorithms.aes; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.eclipse.tractusx.edc.data.encryption.algorithms.CryptoAlgorithm; -import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; -import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; -import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; -import org.eclipse.tractusx.edc.data.encryption.key.AesKey; -import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; - -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Objects; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class AesAlgorithm implements CryptoAlgorithm { - - private static final String AES_GCM = "AES/GCM/NoPadding"; - private static final String AES = "AES"; - private static final Object MONITOR = new Object(); - - private final SecureRandom secureRandom; - - private final CryptoDataFactory cryptoDataFactory; - private AesInitializationVectorIterator initializationVectorIterator; - - public AesAlgorithm(CryptoDataFactory cryptoDataFactory) { - this.cryptoDataFactory = Objects.requireNonNull(cryptoDataFactory); - - // We use new SecureRandom() and not SecureRandom.getInstanceStrong(), as the second one - // would use a blocking algorithm, which leads to an increased encryption time of up to 3 - // minutes. Since we have already used /dev/urandom, which only provides pseudo-randomness and - // is also non-blocking, switching to a non-blocking algorithm should not matter here either. - this.secureRandom = new SecureRandom(); - this.initializationVectorIterator = new AesInitializationVectorIterator(this.secureRandom); - } - - @Override - public synchronized EncryptedData encrypt(DecryptedData data, AesKey key) - throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, - NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { - - final byte[] initializationVector; - synchronized (MONITOR) { - if (!initializationVectorIterator.hasNext()) { - initializationVectorIterator = new AesInitializationVectorIterator(this.secureRandom); - } - - initializationVector = initializationVectorIterator.next(); - } - - Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); - final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); - final GCMParameterSpec gcmParameterSpec = - new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); - cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec); - byte[] encrypted = cipher.doFinal(data.getBytes()); - byte[] encryptedWithVector = ArrayUtil.concat(initializationVector, encrypted); - - return cryptoDataFactory.encryptedFromBytes(encryptedWithVector); - } - - @Override - public DecryptedData decrypt(EncryptedData data, AesKey key) - throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, - NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { - byte[] encryptedWithVector = data.getBytes(); - byte[] initializationVector = ArrayUtil.subArray(encryptedWithVector, 0, 16); - byte[] encrypted = ArrayUtil.subArray(encryptedWithVector, 16, encryptedWithVector.length - 16); - - Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); - final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); - final GCMParameterSpec gcmParameterSpec = - new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); - cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec); - byte[] decryptedData = cipher.doFinal(encrypted); - return cryptoDataFactory.decryptedFromBytes(decryptedData); - } - - public String getAlgorithm() { - return this.secureRandom.getAlgorithm(); - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java deleted file mode 100644 index 73d02c3d5..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2023 ZF Friedrichshafen AG - * 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.algorithms.aes; - -import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; - -import java.security.SecureRandom; -import java.util.Iterator; -import java.util.NoSuchElementException; - -public class AesInitializationVectorIterator implements Iterator { - - public static final int RANDOM_SIZE = 12; - public static final int COUNTER_SIZE = 4; - - private final ByteCounter counter; - - private SecureRandom secureRandom; - - public AesInitializationVectorIterator(SecureRandom secureRandom) { - this.counter = new ByteCounter(COUNTER_SIZE); - this.secureRandom = secureRandom; - } - - public AesInitializationVectorIterator(ByteCounter byteCounter) { - this.counter = byteCounter; - } - - @Override - public boolean hasNext() { - return !counter.isMaxed(); - } - - @Override - public byte[] next() { - if (counter.isMaxed()) { - throw new NoSuchElementException(getClass().getSimpleName() + " has no more elements"); - } - - byte[] random = getNextRandom(); - counter.increment(); - - return ArrayUtil.concat(random, counter.getBytes()); - } - - public byte[] getNextRandom() { - byte[] newVector = new byte[RANDOM_SIZE]; - secureRandom.nextBytes(newVector); - return newVector; - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java deleted file mode 100644 index e7874c158..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java +++ /dev/null @@ -1,87 +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.algorithms.aes; - -/** - * Big Endian Byte Counter - */ -public class ByteCounter { - - private final byte[] counter; - - /** - * Constructs a new ByteCounter with the given number of bytes. E.g. a ByteCounter with 4 bytes - * will have a counter value of [0, 0, 0, 0]. - * - * @param size number of bytes used by the counter - */ - public ByteCounter(int size) { - this.counter = new byte[size]; - } - - /** - * Constructs a new ByteCounter with the given counter value. Counter cannot grow bigger than the - * size of the array. - * - * @param counter initial counter value - */ - public ByteCounter(byte[] counter) { - this.counter = counter; - } - - /** - * Returns the counter value as a byte array. - */ - public byte[] getBytes() { - return counter; - } - - /** - * Returns true if counter is maxed - */ - public boolean isMaxed() { - for (byte b : counter) { - if (b != (byte) 0xff) return false; - } - return true; - } - - /** - * Increments the counter by one. - * - * @throws IllegalStateException if the counter is already maxed - */ - public void increment() { - incrementByte(counter.length - 1); - } - - private void incrementByte(int index) { - if (isMaxed()) { - throw new IllegalStateException("Counter is already maxed"); - } - - if (counter[index] == (byte) 0xff) { - incrementByte(index - 1); - counter[index] = (byte) 0x00; - } else { - counter[index]++; - } - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java deleted file mode 100644 index a01331275..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java +++ /dev/null @@ -1,107 +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.data; - -import org.bouncycastle.util.encoders.Base64; - -public class CryptoDataFactoryImpl implements CryptoDataFactory { - - public DecryptedData decryptedFromText(String text) { - final byte[] bytes = text.getBytes(); - final String base64 = Base64.toBase64String(bytes); - return new DecryptedDataImpl(bytes, base64, text); - } - - public DecryptedData decryptedFromBase64(String base64) { - final byte[] bytes = Base64.decode(base64); - final String text = new String(bytes); - return new DecryptedDataImpl(bytes, base64, text); - } - - public DecryptedData decryptedFromBytes(byte[] bytes) { - final String base64 = Base64.toBase64String(bytes); - final String text = new String(bytes); - return new DecryptedDataImpl(bytes, base64, text); - } - - public EncryptedData encryptedFromText(String text) { - final byte[] bytes = text.getBytes(); - final String base64 = Base64.toBase64String(bytes); - return new EncryptedDataImpl(bytes, base64, text); - } - - public EncryptedData encryptedFromBase64(String base64) { - final byte[] bytes = Base64.decode(base64); - final String text = new String(bytes); - return new EncryptedDataImpl(bytes, base64, text); - } - - public EncryptedData encryptedFromBytes(byte[] bytes) { - final String base64 = Base64.toBase64String(bytes); - final String text = new String(bytes); - return new EncryptedDataImpl(bytes, base64, text); - } - - - private static class DecryptedDataImpl implements DecryptedData { - private final byte[] bytes; - private final String base64; - private final String text; - - private DecryptedDataImpl(byte[] bytes, String base64, String text) { - this.bytes = bytes; - this.base64 = base64; - this.text = text; - } - - @Override - public byte[] getBytes() { - return bytes; - } - - @Override - public String getBase64() { - return base64; - } - } - - - private static class EncryptedDataImpl implements EncryptedData { - private final byte[] bytes; - private final String base64; - private final String text; - - private EncryptedDataImpl(byte[] bytes, String base64, String text) { - this.bytes = bytes; - this.base64 = base64; - this.text = text; - } - - @Override - public byte[] getBytes() { - return bytes; - } - - @Override - public String getBase64() { - return base64; - } - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/DecryptedData.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/DecryptedData.java deleted file mode 100644 index 63c6cf2b1..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/DecryptedData.java +++ /dev/null @@ -1,22 +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.data; - -public interface DecryptedData extends CryptoData {} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/EncryptedData.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/EncryptedData.java deleted file mode 100644 index dd4e8241a..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/EncryptedData.java +++ /dev/null @@ -1,22 +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.data; - -public interface EncryptedData extends CryptoData {} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java deleted file mode 100644 index 0723306e4..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java +++ /dev/null @@ -1,48 +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.encrypter; - -import java.time.Duration; - - -public class AesDataEncrypterConfiguration { - private final String keySetAlias; - private final boolean cachingEnabled; - private final Duration cachingDuration; - - public AesDataEncrypterConfiguration(String keySetAlias, boolean cachingEnabled, Duration cachingDuration) { - this.keySetAlias = keySetAlias; - this.cachingEnabled = cachingEnabled; - this.cachingDuration = cachingDuration; - } - - public Duration getCachingDuration() { - return cachingDuration; - } - - public boolean isCachingEnabled() { - return cachingEnabled; - } - - public String getKeySetAlias() { - return keySetAlias; - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java deleted file mode 100644 index e638df98c..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java +++ /dev/null @@ -1,101 +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.encrypter; - -import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.tractusx.edc.data.encryption.DataEncryptionExtension; -import org.eclipse.tractusx.edc.data.encryption.algorithms.CryptoAlgorithm; -import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; -import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; -import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; -import org.eclipse.tractusx.edc.data.encryption.key.AesKey; -import org.eclipse.tractusx.edc.data.encryption.provider.KeyProvider; - -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Optional; -import javax.crypto.AEADBadTagException; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; - -public class AesDataEncrypterImpl implements DataEncrypter { - - private final CryptoAlgorithm encryptionStrategy; - private final Monitor monitor; - private final KeyProvider keyProvider; - private final CryptoAlgorithm algorithm; - private final CryptoDataFactory cryptoDataFactory; - - public AesDataEncrypterImpl(CryptoAlgorithm encryptionStrategy, Monitor monitor, KeyProvider keyProvider, CryptoAlgorithm algorithm, CryptoDataFactory cryptoDataFactory) { - this.encryptionStrategy = encryptionStrategy; - this.monitor = monitor; - this.keyProvider = keyProvider; - this.algorithm = algorithm; - this.cryptoDataFactory = cryptoDataFactory; - } - - @Override - public String encrypt(String value) { - DecryptedData decryptedData = cryptoDataFactory.decryptedFromText(value); - AesKey key = keyProvider.getEncryptionKey(); - - try { - EncryptedData encryptedData = algorithm.encrypt(decryptedData, key); - return encryptedData.getBase64(); - } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | - InvalidAlgorithmParameterException | NoSuchPaddingException | NoSuchAlgorithmException e) { - throw new EdcException(e); - } - } - - @Override - public String decrypt(String value) { - EncryptedData encryptedData = cryptoDataFactory.encryptedFromBase64(value); - - return keyProvider - .getDecryptionKeySet() - .map(key -> decrypt(encryptedData, key)) - .filter(Optional::isPresent) - .map(Optional::get) - .map(DecryptedData::getBytes) - .map(String::new) - .findFirst() - .orElseThrow(() -> - new EdcException(DataEncryptionExtension.EXTENSION_NAME + ": Failed to decrypt data. This can happen if the key set is empty, contains invalid keys, " + - "the decryption key rotated out of the key set or because the data was encrypted by a different algorithm.")); - } - - private Optional decrypt(EncryptedData data, AesKey key) { - try { - return Optional.of(encryptionStrategy.decrypt(data, key)); - } catch (AEADBadTagException e) { // thrown when wrong key is used for decryption - return Optional.empty(); - } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | NoSuchPaddingException | - NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { - monitor.warning(String.format(DataEncryptionExtension.EXTENSION_NAME + ": Exception decrypting data using key from rotating key set. %s", e.getMessage())); - throw new EdcException(e); - } - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java deleted file mode 100644 index c40e20b08..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2023 ZF Friedrichshafen AG - * 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.encrypter; - -import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.data.encryption.algorithms.aes.AesAlgorithm; -import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; -import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactoryImpl; -import org.eclipse.tractusx.edc.data.encryption.key.AesKey; -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactory; -import org.eclipse.tractusx.edc.data.encryption.provider.AesKeyProvider; -import org.eclipse.tractusx.edc.data.encryption.provider.CachingKeyProvider; -import org.eclipse.tractusx.edc.data.encryption.provider.KeyProvider; - -import static java.lang.String.format; - -public class DataEncrypterFactory { - - public static final String AES_ALGORITHM = "AES"; - public static final String NONE = "NONE"; - - private final Vault vault; - private final Monitor monitor; - private final CryptoKeyFactory keyFactory; - - public DataEncrypterFactory(Vault vault, Monitor monitor, CryptoKeyFactory keyFactory) { - this.vault = vault; - this.monitor = monitor; - this.keyFactory = keyFactory; - } - - public DataEncrypter createNoneEncrypter() { - return new DataEncrypter() { - @Override - public String encrypt(String data) { - return data; - } - - @Override - public String decrypt(String data) { - return data; - } - }; - } - - public DataEncrypter createAesEncrypter(AesDataEncrypterConfiguration configuration) { - KeyProvider keyProvider = - new AesKeyProvider(vault, configuration.getKeySetAlias(), keyFactory); - - if (configuration.isCachingEnabled()) { - keyProvider = new CachingKeyProvider<>(keyProvider, configuration.getCachingDuration()); - } - - final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); - final AesAlgorithm algorithm = new AesAlgorithm(cryptoDataFactory); - - monitor.debug( - format( - "AES algorithm was initialised with SecureRandom algorithm '%s'", - algorithm.getAlgorithm())); - return new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/AesKey.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/AesKey.java deleted file mode 100644 index feb3260fa..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/AesKey.java +++ /dev/null @@ -1,26 +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.key; - -public interface AesKey extends CryptoKey { - byte[] getBytes(); - - String getBase64(); -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKey.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKey.java deleted file mode 100644 index f7d8b3601..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKey.java +++ /dev/null @@ -1,22 +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.key; - -public interface CryptoKey {} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactory.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactory.java deleted file mode 100644 index b7d47808a..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactory.java +++ /dev/null @@ -1,26 +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.key; - -public interface CryptoKeyFactory { - AesKey fromBase64(String base64); - - AesKey fromBytes(byte[] key); -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java deleted file mode 100644 index dc8eb2083..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java +++ /dev/null @@ -1,59 +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.key; - -import org.bouncycastle.util.encoders.Base64; - -public class CryptoKeyFactoryImpl implements CryptoKeyFactory { - - public AesKey fromBase64(String base64) { - return fromBytes(Base64.decode(base64)); - } - - public AesKey fromBytes(byte[] key) { - int bitLength = key.length * Byte.SIZE; - if (!(bitLength == 128 || bitLength == 192 || bitLength == 256)) { - throw new IllegalArgumentException("Invalid AES key length: " + bitLength); - } - - return new AesKeyImpl(key, Base64.toBase64String(key)); - } - - - private static class AesKeyImpl implements AesKey { - private final byte[] bytes; - private final String base64; - - private AesKeyImpl(byte[] bytes, String base64) { - this.bytes = bytes; - this.base64 = base64; - } - - @Override - public byte[] getBytes() { - return bytes; - } - - @Override - public String getBase64() { - return base64; - } - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java deleted file mode 100644 index 586909282..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java +++ /dev/null @@ -1,70 +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.provider; - -import org.bouncycastle.util.encoders.Base64; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.data.encryption.DataEncryptionExtension; -import org.eclipse.tractusx.edc.data.encryption.key.AesKey; -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactory; - -import java.util.Arrays; -import java.util.function.Predicate; -import java.util.stream.Stream; - -public class AesKeyProvider implements KeyProvider { - - private static final String KEY_SEPARATOR = ","; - - private final Vault vault; - private final String vaultKeyAlias; - private final CryptoKeyFactory cryptoKeyFactory; - - public AesKeyProvider(Vault vault, String vaultKeyAlias, CryptoKeyFactory cryptoKeyFactory) { - this.vault = vault; - this.vaultKeyAlias = vaultKeyAlias; - this.cryptoKeyFactory = cryptoKeyFactory; - } - - @Override - public AesKey getEncryptionKey() { - return getKeysStream() - .findFirst() - .orElseThrow(() -> new RuntimeException(DataEncryptionExtension.EXTENSION_NAME + ": Vault must contain at least one key.")); - } - - @Override - public Stream getDecryptionKeySet() { - return getKeysStream(); - } - - private Stream getKeysStream() { - return Arrays.stream(getKeys().split(KEY_SEPARATOR)) - .map(String::trim) - .filter(Predicate.not(String::isEmpty)) - .map(Base64::decode) - .map(cryptoKeyFactory::fromBytes); - } - - private String getKeys() { - String keys = vault.resolveSecret(vaultKeyAlias); - return keys == null ? "" : keys; - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java deleted file mode 100644 index 4819b6386..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java +++ /dev/null @@ -1,92 +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.provider; - -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKey; - -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class CachingKeyProvider implements KeyProvider { - - private final KeyProvider decoratedProvider; - private final Clock clock; - private final Duration cacheExpiration; - - private CachedKeys cachedKeys; - - public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration) { - this(keyProvider, cacheExpiration, Clock.systemUTC()); - } - - public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration, Clock clock) { - this.decoratedProvider = Objects.requireNonNull(keyProvider); - this.cacheExpiration = Objects.requireNonNull(cacheExpiration); - this.clock = Objects.requireNonNull(clock); - } - - @Override - public T getEncryptionKey() { - checkCache(); - return cachedKeys.getEncryptionKey(); - } - - @Override - public Stream getDecryptionKeySet() { - checkCache(); - return cachedKeys.getDecryptionKeys().stream(); - } - - private void checkCache() { - if (cachedKeys == null || cachedKeys.expiration.isBefore(clock.instant())) { - T encryptionKey = decoratedProvider.getEncryptionKey(); - List decryptionKeys = decoratedProvider.getDecryptionKeySet().collect(Collectors.toList()); - cachedKeys = - new CachedKeys<>(encryptionKey, decryptionKeys, clock.instant().plus(cacheExpiration)); - } - } - - - private static class CachedKeys { - private final T encryptionKey; - private final List decryptionKeys; - private final Instant expiration; - - private CachedKeys(T encryptionKey, List decryptionKeys, Instant expiration) { - this.encryptionKey = encryptionKey; - this.decryptionKeys = decryptionKeys; - this.expiration = Objects.requireNonNull(expiration); - } - - public List getDecryptionKeys() { - return decryptionKeys; - } - - public T getEncryptionKey() { - return encryptionKey; - } - } -} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/KeyProvider.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/KeyProvider.java deleted file mode 100644 index 2a3f58d00..000000000 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/KeyProvider.java +++ /dev/null @@ -1,30 +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.provider; - -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKey; - -import java.util.stream.Stream; - -public interface KeyProvider { - T getEncryptionKey(); - - Stream getDecryptionKeySet(); -} diff --git a/edc-extensions/data-encryption/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/data-encryption/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index bce166d66..0eba76af1 100644 --- a/edc-extensions/data-encryption/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/data-encryption/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -1,20 +1,16 @@ # -# 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. +# 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 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, 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.data.encryption.DataEncryptionExtension +# Contributors: +# Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation +# +# + +org.eclipse.tractusx.edc.data.encryption.TxEncryptorExtension + diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtensionTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtensionTest.java deleted file mode 100644 index bf6a4d508..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtensionTest.java +++ /dev/null @@ -1,121 +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; - -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.data.encryption.encrypter.DataEncrypterFactory; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -class DataEncryptionExtensionTest { - - private DataEncryptionExtension extension; - - // mocks - private Monitor monitor; - private ServiceExtensionContext context; - private Vault vault; - - @BeforeEach - void setup() { - monitor = Mockito.mock(Monitor.class); - context = Mockito.mock(ServiceExtensionContext.class); - vault = Mockito.mock(Vault.class); - - extension = new DataEncryptionExtension(); - - Mockito.when(context.getMonitor()).thenReturn(monitor); - Mockito.when(context.getService(Vault.class)).thenReturn(vault); - - Mockito.when( - context.getSetting( - Mockito.eq(DataEncryptionExtension.CACHING_ENABLED), Mockito.anyBoolean())) - .thenAnswer((i) -> i.getArguments()[1]); - Mockito.when( - context.getSetting( - Mockito.eq(DataEncryptionExtension.ENCRYPTION_ALGORITHM), Mockito.anyString())) - .thenAnswer((i) -> i.getArguments()[1]); - Mockito.when( - context.getSetting( - Mockito.eq(DataEncryptionExtension.CACHING_SECONDS), Mockito.anyInt())) - .thenAnswer((i) -> i.getArguments()[1]); - } - - @Test - void testName() { - Assertions.assertEquals(DataEncryptionExtension.EXTENSION_NAME, extension.name()); - } - - @Test - void testExceptionOnMissingKeySetAlias() { - Mockito.when(context.getSetting(DataEncryptionExtension.ENCRYPTION_KEY_SET, null)) - .thenReturn(null); - Assertions.assertThrows(EdcException.class, () -> extension.initialize(context)); - } - - @Test - void testStartExceptionOnMissingKeySetInVault() { - final String keySetAlias = "foo"; - Mockito.when(context.getSetting(DataEncryptionExtension.ENCRYPTION_KEY_SET, null)) - .thenReturn(keySetAlias); - Mockito.when(vault.resolveSecret(keySetAlias)).thenReturn(""); - - extension.initialize(context); - - Assertions.assertThrows(EdcException.class, () -> extension.start()); - } - - @Test - void testStartExceptionOnStartWithWrongKeySetAlias() { - final String keySetAlias = "foo"; - Mockito.when( - context.getSetting( - DataEncryptionExtension.ENCRYPTION_ALGORITHM, DataEncrypterFactory.AES_ALGORITHM)) - .thenReturn(DataEncrypterFactory.AES_ALGORITHM); - Mockito.when(context.getSetting(DataEncryptionExtension.ENCRYPTION_KEY_SET, null)) - .thenReturn(keySetAlias); - Mockito.when(vault.resolveSecret(keySetAlias)).thenReturn("l8b2YHL7VpA=, invalid-key"); - - extension.initialize(context); - - Assertions.assertThrows(EdcException.class, () -> extension.start()); - } - - @Test - void testNonEncrypterRequiresNoOtherSetting() { - final String keySetAlias = "foo"; - Mockito.when( - context.getSetting( - DataEncryptionExtension.ENCRYPTION_ALGORITHM, DataEncrypterFactory.AES_ALGORITHM)) - .thenReturn(DataEncrypterFactory.NONE); - Mockito.when(context.getSetting(DataEncryptionExtension.ENCRYPTION_KEY_SET, null)) - .thenReturn(null); - Mockito.when(vault.resolveSecret(keySetAlias)).thenReturn(null); - - Assertions.assertDoesNotThrow(() -> extension.initialize(context)); - Assertions.assertDoesNotThrow(() -> extension.start()); - } -} 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 new file mode 100644 index 000000000..1b3cbdc2c --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/TxEncryptorExtensionTest.java @@ -0,0 +1,85 @@ +/* + * 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 + * + */ + +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.edc.spi.system.injection.ObjectFactory; +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.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(DependencyInjectionExtension.class) +class TxEncryptorExtensionTest { + + private final Monitor monitor = mock(); + private TxEncryptorExtension extension; + private ServiceExtensionContext context; + + @BeforeEach + void setup(ObjectFactory factory, ServiceExtensionContext c) { + c.registerService(Monitor.class, monitor); + context = spy(c); + when(context.getSetting(ENCRYPTION_KEY_ALIAS, null)).thenReturn("test-key"); + extension = factory.constructInstance(TxEncryptorExtension.class); + } + + @Test + void createEncryptor_noConfig_createsDefault() { + var encryptor = extension.createEncryptor(context); + assertThat(encryptor).isInstanceOf(AesEncryptor.class); + } + + @Test + void createEncryptor_otherAlgorithm_createsNoop() { + 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() { + 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) { + 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")); + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..8e6be6df8 --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/aes/AesEncryptorTest.java @@ -0,0 +1,122 @@ +/* + * 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 + * + */ + +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); + + } +} \ No newline at end of file diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java deleted file mode 100644 index d141887cf..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java +++ /dev/null @@ -1,98 +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.algorithms.aes; - -import org.bouncycastle.util.encoders.Base64; -import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; -import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactoryImpl; -import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; -import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; -import org.eclipse.tractusx.edc.data.encryption.key.AesKey; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class AesAlgorithmTest { - - private static final byte[] KEY_128_BIT = Base64.decode("dVUjmYJzbwVcntkFZU+lNQ=="); - private static final byte[] KEY_196_BIT = Base64.decode("NcgHzzRTUC+z396tWG9hqIbeihujz0m8"); - private static final byte[] KEY_256_BIT = - Base64.decode("OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="); - - private final AesAlgorithm strategy = new AesAlgorithm(new CryptoDataFactoryImpl()); - private final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); - - @Test - void test128BitKey() { - testKey(KEY_128_BIT); - } - - @Test - void test196BitKey() { - testKey(KEY_196_BIT); - } - - @Test - void test256BitKey() { - testKey(KEY_256_BIT); - } - - @Test - void testSameDataEncryptedDifferently() { - final AesKey aesKey = createKey(KEY_128_BIT); - final DecryptedData expected = cryptoDataFactory.decryptedFromText("same data"); - - try { - final EncryptedData result1 = strategy.encrypt(expected, aesKey); - final EncryptedData result2 = strategy.encrypt(expected, aesKey); - - Assertions.assertNotEquals(result1.getBase64(), result2.getBase64()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - - void testKey(byte[] key) { - final AesKey aesKey = createKey(key); - final DecryptedData expected = cryptoDataFactory.decryptedFromText("I will be encrypted"); - try { - final EncryptedData encryptedResult = strategy.encrypt(expected, aesKey); - final DecryptedData result = strategy.decrypt(encryptedResult, aesKey); - Assertions.assertEquals(expected.getBase64(), result.getBase64()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - AesKey createKey(byte[] key) { - return new AesKey() { - - @Override - public byte[] getBytes() { - return key; - } - - @Override - public String getBase64() { - return Base64.toBase64String(key); - } - }; - } -} diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java deleted file mode 100644 index f70a3bf70..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2023 ZF Friedrichshafen AG - * 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.algorithms.aes; - -import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.List; -import java.util.NoSuchElementException; - -class AesInitializationVectorIteratorTest { - - @Test - void testDistinctVectors() { - final int vectorCount = 100; - final SecureRandom secureRandom = new SecureRandom(); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(secureRandom); - - List vectors = new ArrayList<>(); - for (var i = 0; i < vectorCount; i++) { - vectors.add(iterator.next()); - } - - long distinctVectors = vectors.stream().map(ArrayUtil::byteArrayToHex).distinct().count(); - Assertions.assertEquals(vectorCount, distinctVectors); - } - - @Test - void testHasNextTrueOnCounterContinuing() { - ByteCounter counter = Mockito.mock(ByteCounter.class); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - - Mockito.when(counter.isMaxed()).thenReturn(false); - Assertions.assertTrue(iterator.hasNext()); - } - - @Test - void testHasNextFalseOnCounterEnd() { - ByteCounter counter = Mockito.mock(ByteCounter.class); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - - Mockito.when(counter.isMaxed()).thenReturn(true); - Assertions.assertFalse(iterator.hasNext()); - } - - @Test - void testNoSuchElementExceptionOnCounterEnd() { - ByteCounter counter = Mockito.mock(ByteCounter.class); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - - Mockito.when(counter.isMaxed()).thenReturn(true); - Assertions.assertThrows(NoSuchElementException.class, iterator::next); - } -} diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounterTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounterTest.java deleted file mode 100644 index b9236f189..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounterTest.java +++ /dev/null @@ -1,97 +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.algorithms.aes; - -import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; -import org.junit.jupiter.api.Assertions; -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; - -class ByteCounterTest { - - @ParameterizedTest - @ArgumentsSource(IncrementArgumentsProvider.class) - void testIncrements(byte[] counterValue, long numberOfIncrements, byte[] expected) { - - ByteCounter initializationVector = new ByteCounter(counterValue); - - for (int i = 0; i < numberOfIncrements; i++) { - initializationVector.increment(); - } - - var result = initializationVector.getBytes(); - Assertions.assertEquals(ArrayUtil.byteArrayToHex(expected), ArrayUtil.byteArrayToHex(result)); - } - - @Test - void testIsMaxed() { - - byte[] counterValue = new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; - ByteCounter initializationVector = new ByteCounter(counterValue); - - Assertions.assertTrue(initializationVector.isMaxed()); - } - - @Test - void testOverflow() { - - byte[] counterValue = new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; - ByteCounter initializationVector = new ByteCounter(counterValue); - - Assertions.assertThrows(IllegalStateException.class, initializationVector::increment); - } - - private static class IncrementArgumentsProvider implements ArgumentsProvider { - @Override - public Stream provideArguments(ExtensionContext context) throws Exception { - return Stream.of( - Arguments.of( - new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, - 0, - new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}), - Arguments.of( - new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, - 1, - new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01}), - Arguments.of( - new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, - 2, - new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02}), - Arguments.of( - new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, - 255, - new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xff}), - Arguments.of( - new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, - 65535, - new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff}), - Arguments.of( - new byte[]{(byte) 0xef, (byte) 0xff, (byte) 0xff, (byte) 0xff}, - 1, - new byte[]{(byte) 0xf0, (byte) 0x00, (byte) 0x00, (byte) 0x00})); - } - } -} diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java deleted file mode 100644 index 6dcd103cb..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java +++ /dev/null @@ -1,108 +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.encrypter; - -import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.data.encryption.algorithms.CryptoAlgorithm; -import org.eclipse.tractusx.edc.data.encryption.algorithms.aes.AesAlgorithm; -import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; -import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactoryImpl; -import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; -import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; -import org.eclipse.tractusx.edc.data.encryption.key.AesKey; -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactory; -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactoryImpl; -import org.eclipse.tractusx.edc.data.encryption.provider.AesKeyProvider; -import org.eclipse.tractusx.edc.data.encryption.provider.KeyProvider; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -@SuppressWarnings("FieldCanBeLocal") -class DataEncrypterAesComponentTest { - - private static final String KEY_128_BIT_BASE_64 = "7h6sh6t6tchCmNnHjK2kFA=="; - private static final String KEY_256_BIT_BASE_64 = "OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="; - - private DataEncrypter dataEncrypter; - private CryptoAlgorithm algorithm; - private KeyProvider keyProvider; - private CryptoKeyFactory cryptoKeyFactory; - private CryptoDataFactory cryptoDataFactory; - - // mocks - private Monitor monitor; - private Vault vault; - - @BeforeEach - void setup() { - monitor = Mockito.mock(Monitor.class); - vault = Mockito.mock(Vault.class); - - cryptoKeyFactory = new CryptoKeyFactoryImpl(); - cryptoDataFactory = new CryptoDataFactoryImpl(); - algorithm = new AesAlgorithm(cryptoDataFactory); - keyProvider = new AesKeyProvider(vault, "foo", cryptoKeyFactory); - - dataEncrypter = - new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); - } - - @Test - void testKeyRotation() { - Mockito.when(vault.resolveSecret(Mockito.anyString())) - .thenReturn( - String.format( - "%s, %s, %s, %s", - KEY_128_BIT_BASE_64, - KEY_128_BIT_BASE_64, - KEY_128_BIT_BASE_64, - KEY_256_BIT_BASE_64)); - - final AesKey key256Bit = cryptoKeyFactory.fromBase64(KEY_256_BIT_BASE_64); - final String expectedResult = "hello"; - final DecryptedData decryptedResult = cryptoDataFactory.decryptedFromText(expectedResult); - - try { - final EncryptedData encryptedResult = algorithm.encrypt(decryptedResult, key256Bit); - - var result = dataEncrypter.decrypt(encryptedResult.getBase64()); - - Assertions.assertEquals(expectedResult, result); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Test - void testEncryption() { - Mockito.when(vault.resolveSecret(Mockito.anyString())).thenReturn(KEY_128_BIT_BASE_64); - - final String expectedResult = "hello world!"; - - var encryptedResult = dataEncrypter.encrypt(expectedResult); - var result = dataEncrypter.decrypt(encryptedResult); - - Assertions.assertEquals(expectedResult, result); - } -} diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java deleted file mode 100644 index 7564931c1..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java +++ /dev/null @@ -1,83 +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.encrypter; - -import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactoryImpl; -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.ValueSource; -import org.mockito.Mockito; - -import java.time.Duration; - -@SuppressWarnings("FieldCanBeLocal") -class DataEncrypterFactoryTest { - - private static final String KEY_SET_ALIAS = "keySetAlias"; - - DataEncrypterFactory factory; - - // mocks - private Vault vault; - private Monitor monitor; - - @BeforeEach - void setup() { - vault = Mockito.mock(Vault.class); - monitor = Mockito.mock(Monitor.class); - - factory = new DataEncrypterFactory(vault, monitor, new CryptoKeyFactoryImpl()); - } - - @ParameterizedTest - @ValueSource(strings = {DataEncrypterFactory.AES_ALGORITHM, DataEncrypterFactory.NONE}) - void testValidStrategies(String strategy) { - final AesDataEncrypterConfiguration configuration = newConfiguration(false); - Assertions.assertDoesNotThrow(() -> factory.createAesEncrypter(configuration)); - } - - @Test - void testEncrypterWithCaching() { - Mockito.when(vault.resolveSecret(KEY_SET_ALIAS)).thenReturn("7h6sh6t6tchCmNnHjK2kFA=="); - - final AesDataEncrypterConfiguration configuration = newConfiguration(true); - final DataEncrypter dataEncrypter = factory.createAesEncrypter(configuration); - - final String foo1 = dataEncrypter.encrypt("foo1"); - dataEncrypter.decrypt(foo1); - final String foo2 = dataEncrypter.encrypt("foo2"); - dataEncrypter.decrypt(foo2); - final String foo3 = dataEncrypter.encrypt("foo3"); - dataEncrypter.decrypt(foo3); - - // one invoke to get encryption- and one to cache decryption key - Mockito.verify(vault, Mockito.times(2)).resolveSecret(KEY_SET_ALIAS); - } - - private AesDataEncrypterConfiguration newConfiguration(boolean isCachingEnabled) { - return new AesDataEncrypterConfiguration( - KEY_SET_ALIAS, isCachingEnabled, Duration.ofMinutes(1)); - } -} diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImplTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImplTest.java deleted file mode 100644 index 60f68e86d..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImplTest.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.key; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -class CryptoKeyFactoryImplTest { - - @ParameterizedTest - @ValueSource(ints = {32, 64, 512, 1024, 2048, 4096}) - void throwsIllegalArgumentExceptionWhenInvalidAesKeyLength(int bitLength) { - CryptoKeyFactory cryptoKeyFactory = new CryptoKeyFactoryImpl(); - Assertions.assertThrows( - IllegalArgumentException.class, - () -> cryptoKeyFactory.fromBytes(new byte[bitLength / Byte.SIZE])); - } - - @ParameterizedTest - @ValueSource(ints = {128, 192, 256}) - void throwsNotOnValidAesKeyLength(int bitLength) { - CryptoKeyFactory cryptoKeyFactory = new CryptoKeyFactoryImpl(); - Assertions.assertDoesNotThrow( - () -> cryptoKeyFactory.fromBytes(new byte[bitLength / Byte.SIZE])); - } -} diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProviderTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProviderTest.java deleted file mode 100644 index 07152a95b..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProviderTest.java +++ /dev/null @@ -1,81 +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.provider; - -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.tractusx.edc.data.encryption.key.AesKey; -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactoryImpl; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import java.util.List; -import java.util.stream.Collectors; - -class AesKeyProviderTest { - - private static final String KEY_1 = "dVUjmYJzbwVcntkFZU+lNQ=="; - private static final String KEY_2 = "7h6sh6t6tchCmNnHjK2kFA=="; - private static final String KEY_3 = "uyNfJzhsnvfEe9OtQyR9Og=="; - - private static final String KEY_ALIAS = "foo"; - - private AesKeyProvider keyProvider; - - // mocks - private Vault vault; - - @BeforeEach - void setup() { - vault = Mockito.mock(Vault.class); - keyProvider = new AesKeyProvider(vault, KEY_ALIAS, new CryptoKeyFactoryImpl()); - } - - @Test - void testEncryptionKeyAlwaysFirstKey() { - Mockito.when(vault.resolveSecret(KEY_ALIAS)) - .thenReturn(String.format("%s,%s,%s", KEY_1, KEY_2, KEY_3)); - - AesKey key = keyProvider.getEncryptionKey(); - - Assertions.assertEquals(KEY_1, key.getBase64()); - } - - @Test - void testEncryptionThrowsOnNoKey() { - Mockito.when(vault.resolveSecret(KEY_ALIAS)).thenReturn(" "); - - Assertions.assertThrows(RuntimeException.class, () -> keyProvider.getEncryptionKey()); - } - - @Test - void testGetKeys() { - Mockito.when(vault.resolveSecret(KEY_ALIAS)) - .thenReturn(String.format("%s, ,,%s,%s", KEY_1, KEY_2, KEY_3)); - - List keys = - keyProvider.getDecryptionKeySet().map(AesKey::getBase64).collect(Collectors.toList()); - List expected = List.of(KEY_1, KEY_2, KEY_3); - - Assertions.assertEquals(expected, keys); - } -} diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProviderTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProviderTest.java deleted file mode 100644 index 28ee89b33..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProviderTest.java +++ /dev/null @@ -1,102 +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.provider; - -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKey; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.util.stream.Stream; - -@SuppressWarnings("FieldCanBeLocal") -class CachingKeyProviderTest { - - private CachingKeyProvider cachingKeyProvider; - - private CryptoKey encryptionKey; - private CryptoKey decryptionKey; - - // mocks - private KeyProvider decoratedProvider; - private Duration cacheExpiration; - private Clock clock; - - @BeforeEach - void setup() { - decoratedProvider = Mockito.mock(KeyProvider.class); - cacheExpiration = Duration.ofSeconds(2); - clock = Mockito.mock(Clock.class); - encryptionKey = Mockito.mock(CryptoKey.class); - decryptionKey = Mockito.mock(CryptoKey.class); - - cachingKeyProvider = - new CachingKeyProvider(decoratedProvider, cacheExpiration, clock); - - Mockito.when(decoratedProvider.getEncryptionKey()).thenReturn(encryptionKey); - Mockito.when(decoratedProvider.getDecryptionKeySet()) - .thenAnswer((i) -> Stream.of(decryptionKey)); - } - - @Test - void testCaching() { - - Mockito.when(clock.instant()).thenAnswer((i) -> Instant.now()); - - cachingKeyProvider.getDecryptionKeySet(); - cachingKeyProvider.getEncryptionKey(); - - cachingKeyProvider.getDecryptionKeySet(); - cachingKeyProvider.getEncryptionKey(); - - cachingKeyProvider.getDecryptionKeySet(); - cachingKeyProvider.getEncryptionKey(); - - cachingKeyProvider.getDecryptionKeySet(); - cachingKeyProvider.getEncryptionKey(); - - Mockito.verify(decoratedProvider, Mockito.times(1)).getDecryptionKeySet(); - Mockito.verify(decoratedProvider, Mockito.times(1)).getEncryptionKey(); - } - - @Test - void testCacheUpdate() { - - Mockito.when(clock.instant()).thenAnswer((i) -> Instant.now()); - - cachingKeyProvider.getDecryptionKeySet(); - cachingKeyProvider.getEncryptionKey(); - - cachingKeyProvider.getDecryptionKeySet(); - cachingKeyProvider.getEncryptionKey(); - - Mockito.when(clock.instant()) - .thenAnswer((i) -> Instant.now().plus(cacheExpiration.plusSeconds(1))); - - cachingKeyProvider.getDecryptionKeySet(); - cachingKeyProvider.getEncryptionKey(); - - Mockito.verify(decoratedProvider, Mockito.times(2)).getDecryptionKeySet(); - Mockito.verify(decoratedProvider, Mockito.times(2)).getEncryptionKey(); - } -} diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtilTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtilTest.java deleted file mode 100644 index 2ddd5694d..000000000 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtilTest.java +++ /dev/null @@ -1,73 +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.util; - -import org.junit.jupiter.api.Assertions; -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; - -class ArrayUtilTest { - - @ParameterizedTest - @ArgumentsSource(ArrayArgumentsProvider.class) - void testConcat(byte[] a, byte[] b, byte[] expected) { - var result = ArrayUtil.concat(a, b); - Assertions.assertEquals(ArrayUtil.byteArrayToHex(expected), ArrayUtil.byteArrayToHex(result)); - } - - @Test - void testSubArray() { - final byte[] expected = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05}; - final byte[] array = new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; - final byte[] subArray = ArrayUtil.subArray(array, 1, 5); - - Assertions.assertEquals(ArrayUtil.byteArrayToHex(expected), ArrayUtil.byteArrayToHex(subArray)); - } - - private static class ArrayArgumentsProvider implements ArgumentsProvider { - - @Override - public Stream provideArguments(ExtensionContext context) throws Exception { - return Stream.of( - Arguments.of( - new byte[]{0x00, 0x01, 0x02, 0x03}, - new byte[]{0x04, 0x05, 0x06, 0x07}, - new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}), - Arguments.of( - new byte[]{0x00}, - new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, - new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}), - Arguments.of( - new byte[]{}, - new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, - new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}), - Arguments.of( - new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, - new byte[]{}, - new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07})); - } - } -} diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/README.md b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/README.md index c5a34edb9..ec590c1a2 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/README.md +++ b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/README.md @@ -17,6 +17,27 @@ The path is `/aas/request` and the body is something like this exa The body should contain the `assetId` or the `transferProcessId` which identify the data that we want to fetch and an `endpointUrl` which is the provider gateway on which the data is available. More info [here](../edc-dataplane-proxy-provider-api/README.md) on the gateway. +Alternatively if the `endpointUrl` is not known or the gateway on the provider side is not configured, it can be omitted and the `Edr#endpointUrl` +will be used. In this scenario if needed users can provide additional properties to the request for composing the final +url: + +- `pathSegments` sub path to append to the base url +- `queryParams` query parameters to add to the url + +Example with base url `http://localhost:8080/test` + +```json +{ + "assetId": "1", + "pathSegments": "/sub", + "queryParams": "foo=bar" +} +``` + +The final url will look like `http://localhost:8080/test/sub?foo=bar` composed by the DataPlane manager with the Http request flow, + +> Note: the endpoint is not protected with configured `AuthenticationService`, which most likely will be the token based (auth key) one. + ## Configuration | Key | Required | Default | Description | diff --git a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/build.gradle.kts b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/build.gradle.kts index ee5edb153..fbe98c1a5 100644 --- a/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/build.gradle.kts +++ b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/build.gradle.kts @@ -26,9 +26,12 @@ dependencies { implementation(libs.edc.dpf.framework) implementation(libs.edc.dpf.util) implementation(libs.edc.ext.http) + implementation(libs.edc.spi.auth) - implementation(project(":spi:edr-cache-spi")) + implementation(project(":spi:edr-spi")) testImplementation(libs.edc.junit) + testImplementation(testFixtures(libs.edc.core.jersey)) + testImplementation(libs.restAssured) } 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-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java index 048f56ead..3abbecbbc 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-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java @@ -14,6 +14,8 @@ package org.eclipse.tractusx.edc.dataplane.proxy.consumer.api; +import org.eclipse.edc.api.auth.spi.AuthenticationRequestFilter; +import org.eclipse.edc.api.auth.spi.AuthenticationService; import org.eclipse.edc.connector.dataplane.spi.manager.DataPlaneManager; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; @@ -27,7 +29,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.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; import java.util.concurrent.ExecutorService; @@ -63,6 +65,9 @@ public class DataPlaneProxyConsumerApiExtension implements ServiceExtension { @Inject private WebServiceConfigurer configurer; + @Inject + private AuthenticationService authenticationService; + @Inject private Monitor monitor; @@ -80,6 +85,7 @@ public void initialize(ServiceExtensionContext context) { executorService = newFixedThreadPool(context.getSetting(THREAD_POOL_SIZE, DEFAULT_THREAD_POOL)); + webService.registerResource(CONSUMER_API_ALIAS, new AuthenticationRequestFilter(authenticationService)); webService.registerResource(CONSUMER_API_ALIAS, new ClientErrorExceptionMapper()); webService.registerResource(CONSUMER_API_ALIAS, new ConsumerAssetRequestController(edrCache, dataPlaneManager, executorService, monitor)); } 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-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java index 89b715fad..fce090e3a 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-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java @@ -25,12 +25,15 @@ import org.eclipse.edc.connector.dataplane.util.sink.AsyncStreamingDataSink; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.spi.types.domain.HttpDataAddress; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; import org.eclipse.tractusx.edc.dataplane.proxy.consumer.api.asset.model.AssetRequest; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; +import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ExecutorService; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; @@ -41,15 +44,17 @@ import static jakarta.ws.rs.core.Response.status; import static java.lang.String.format; 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; /** * Implements the HTTP proxy API. */ @Path("/aas") public class ConsumerAssetRequestController implements ConsumerAssetRequestApi { + public static final String BASE_URL = "baseUrl"; private static final String HTTP_DATA = "HttpData"; private static final String ASYNC_TYPE = "async"; - private static final String BASE_URL = "baseUrl"; private static final String HEADER_AUTHORIZATION = "header:authorization"; private static final String BEARER_PREFIX = "Bearer "; @@ -76,22 +81,27 @@ public void requestAsset(AssetRequest request, @Suspended AsyncResponse response // resolve the EDR and add it to the request var edr = resolveEdr(request); - var sourceAddress = DataAddress.Builder.newInstance() - .type(HTTP_DATA) - .property(BASE_URL, request.getEndpointUrl()) - .property(HEADER_AUTHORIZATION, BEARER_PREFIX + edr.getAuthCode()) - .build(); + var sourceAddress = Optional.ofNullable(request.getEndpointUrl()) + .map(url -> gatewayAddress(url, edr)) + .orElseGet(() -> dataPlaneAddress(edr)); + 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() .processId(randomUUID().toString()) .trackable(false) .sourceDataAddress(sourceAddress) .destinationDataAddress(destinationAddress) .traceContext(Map.of()) + .properties(properties) .build(); // transfer the data asynchronously @@ -104,6 +114,30 @@ public void requestAsset(AssetRequest request, @Suspended AsyncResponse response } } + + private Map dataPlaneProperties(AssetRequest request) { + var props = new HashMap(); + Optional.ofNullable(request.getQueryParams()).ifPresent((queryParams) -> props.put(QUERY_PARAMS, queryParams)); + Optional.ofNullable(request.getPathSegments()).ifPresent((path) -> props.put(PATH, path)); + 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) { + return HttpDataAddress.Builder.newInstance() + .baseUrl(edr.getEndpoint()) + .proxyQueryParams("true") + .proxyPath("true") + .property(HEADER_AUTHORIZATION, edr.getAuthCode()) + .build(); + } + private EndpointDataReference resolveEdr(AssetRequest request) { if (request.getTransferProcessId() != null) { var edr = edrCache.resolveReference(request.getTransferProcessId()); @@ -112,7 +146,7 @@ private EndpointDataReference resolveEdr(AssetRequest request) { } return edr; } else { - var resolvedEdrs = edrCache.referencesForAsset(request.getAssetId()); + var resolvedEdrs = edrCache.referencesForAsset(request.getAssetId(), request.getProviderId()); if (resolvedEdrs.isEmpty()) { throw new BadRequestException("No EDR for asset: " + request.getAssetId()); } else if (resolvedEdrs.size() > 1) { @@ -128,17 +162,10 @@ private EndpointDataReference resolveEdr(AssetRequest request) { 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()); + case NOT_FOUND -> response.resume(status(NOT_FOUND).type(APPLICATION_JSON).build()); + case NOT_AUTHORIZED -> response.resume(status(UNAUTHORIZED).type(APPLICATION_JSON).build()); + case GENERAL_ERROR -> response.resume(status(INTERNAL_SERVER_ERROR).type(APPLICATION_JSON).build()); + default -> throw new IllegalStateException("Unexpected value: " + result.reason()); } } else if (throwable != null) { reportError(response, throwable); 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-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequest.java index 79fbf70dd..562b9b612 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-proxy/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequest.java @@ -19,8 +19,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import static java.util.Objects.requireNonNull; - /** * A request for asset data. The request may contain a transfer process ID or asset ID and must specify an endpoint for retrieving the data. */ @@ -29,8 +27,17 @@ public class AssetRequest { private String transferProcessId; private String assetId; + + private String providerId; private String endpointUrl; + private String queryParams; + + private String pathSegments; + + private AssetRequest() { + } + public String getTransferProcessId() { return transferProcessId; } @@ -43,13 +50,26 @@ public String getEndpointUrl() { return endpointUrl; } - private AssetRequest() { + public String getProviderId() { + return providerId; + } + + public String getQueryParams() { + return queryParams; + } + + public String getPathSegments() { + return pathSegments; } @JsonPOJOBuilder(withPrefix = "") public static class Builder { private final AssetRequest request; + private Builder() { + request = new AssetRequest(); + } + @JsonCreator public static Builder newInstance() { return new Builder(); @@ -70,16 +90,26 @@ public Builder endpointUrl(String endpointUrl) { return this; } + public Builder providerId(String providerId) { + request.providerId = providerId; + return this; + } + + public Builder queryParams(String queryParams) { + request.queryParams = queryParams; + return this; + } + + public Builder pathSegments(String pathSegments) { + request.pathSegments = pathSegments; + return this; + } + public AssetRequest build() { if (request.assetId == null && request.transferProcessId == null) { throw new NullPointerException("An assetId or endpointReferenceId must be set"); } - requireNonNull(request.endpointUrl, "endpointUrl"); return request; } - - private Builder() { - request = new AssetRequest(); - } } } 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-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequestTest.java index 5855b20dd..4a81ff653 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-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/AssetRequestTest.java @@ -27,7 +27,15 @@ class AssetRequestTest { void verify_SerializeDeserialize() throws JsonProcessingException { var mapper = new ObjectMapper(); - var request = AssetRequest.Builder.newInstance().assetId("asset1").endpointUrl("https://test.com").transferProcessId("tp1").build(); + var request = AssetRequest.Builder.newInstance() + .assetId("asset1") + .endpointUrl("https://test.com") + .providerId("providerId") + .transferProcessId("tp1") + .queryParams("params") + .pathSegments("path") + .build(); + var serialized = mapper.writeValueAsString(request); var deserialized = mapper.readValue(serialized, AssetRequest.class); @@ -35,12 +43,15 @@ 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()); + } @Test void verify_NullArguments() { - assertThatThrownBy(() -> AssetRequest.Builder.newInstance().endpointUrl("https://test.com").build()).isInstanceOf(NullPointerException.class); - assertThatThrownBy(() -> AssetRequest.Builder.newInstance().assetId("asset1").build()).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> AssetRequest.Builder.newInstance().build()).isInstanceOf(NullPointerException.class); } @Test 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-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/ConsumerAssetRequestControllerTest.java new file mode 100644 index 000000000..25891101a --- /dev/null +++ b/edc-extensions/dataplane-proxy/edc-dataplane-proxy-consumer-api/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/model/ConsumerAssetRequestControllerTest.java @@ -0,0 +1,318 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.dataplane.proxy.consumer.api.asset.model; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.restassured.specification.RequestSpecification; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.edc.connector.dataplane.spi.manager.DataPlaneManager; +import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSink; +import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSource; +import org.eclipse.edc.connector.dataplane.spi.pipeline.StreamResult; +import org.eclipse.edc.connector.dataplane.util.sink.AsyncStreamingDataSink; +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.web.jersey.testfixtures.RestControllerTestBase; +import org.eclipse.tractusx.edc.dataplane.proxy.consumer.api.asset.ConsumerAssetRequestController; +import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.stream.Stream; + +import static io.restassured.RestAssured.given; +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 org.assertj.core.api.Assertions.assertThat; +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.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ApiTest +public class ConsumerAssetRequestControllerTest extends RestControllerTestBase { + + public static final String ASSET_REQUEST_PATH = "/aas/request"; + private final EndpointDataReferenceCache cache = mock(EndpointDataReferenceCache.class); + private final DataPlaneManager dataPlaneManager = mock(DataPlaneManager.class); + 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") + .endpoint(url) + .build(); + + var response = Map.of("response", "ok"); + var responseBytes = mapper.writeValueAsBytes(response); + + var datasource = mock(DataSource.class); + var partStream = mock(DataSource.Part.class); + + 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(dataPlaneManager.transfer(any(DataSink.class), any())) + .thenAnswer(a -> { + AsyncStreamingDataSink sink = a.getArgument(0); + return sink.transfer(datasource); + }); + + var proxyResponseBytes = baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .post(ASSET_REQUEST_PATH) + .then() + .statusCode(200) + .extract().body().asByteArray(); + + var proxyResponse = mapper.readValue(proxyResponseBytes, new TypeReference>() { + }); + + assertThat(proxyResponse).containsAllEntriesOf(response); + } + + @ParameterizedTest + @MethodSource("provideServiceResultForProxyCall") + void requestAsset_shouldReturnError_WhenProxyCallFails(StreamResult result, Integer responseCode) 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") + .endpoint(url) + .build(); + + when(cache.referencesForAsset(assetId, null)).thenReturn(List.of(edr)); + when(dataPlaneManager.transfer(any(DataSink.class), any())) + .thenReturn(CompletableFuture.completedFuture(result)); + + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .post(ASSET_REQUEST_PATH) + .then() + .statusCode(responseCode); + + } + + @Test + void requestAsset_shouldReturnError_whenEdrByAssetIdNotFound() { + + var assetId = "assetId"; + var url = "http://localhost:8080/test"; + var request = Map.of("assetId", assetId, "endpointUrl", url); + + when(cache.referencesForAsset(assetId, null)).thenReturn(List.of()); + + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .post(ASSET_REQUEST_PATH) + .then() + .statusCode(400); + + } + + @Test + 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") + .endpoint(url) + .build(); + + when(cache.referencesForAsset(assetId, null)).thenReturn(List.of(edr, edr)); + + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .post(ASSET_REQUEST_PATH) + .then() + .statusCode(428); + + } + + @Test + 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") + .endpoint(url) + .build(); + + var response = Map.of("response", "ok"); + var responseBytes = mapper.writeValueAsBytes(response); + + var datasource = mock(DataSource.class); + var partStream = mock(DataSource.Part.class); + + when(datasource.openPartStream()).thenReturn(StreamResult.success(Stream.of(partStream))); + when(partStream.openStream()).thenReturn(new ByteArrayInputStream(responseBytes)); + + when(cache.resolveReference(transferProcessId)).thenReturn(edr); + when(dataPlaneManager.transfer(any(DataSink.class), any())) + .thenAnswer(a -> { + AsyncStreamingDataSink sink = a.getArgument(0); + return sink.transfer(datasource); + }); + + var proxyResponseBytes = baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .post(ASSET_REQUEST_PATH) + .then() + .statusCode(200) + .extract().body().asByteArray(); + + var proxyResponse = mapper.readValue(proxyResponseBytes, new TypeReference>() { + }); + + assertThat(proxyResponse).containsAllEntriesOf(response); + } + + @Test + void requestAsset_shouldReturnError_whenEdrByTransferProcessIdNotFound() { + + var tp = "tp"; + var url = "http://localhost:8080/test"; + var request = Map.of("transferProcessId", tp, "endpointUrl", url); + + when(cache.resolveReference(tp)).thenReturn(null); + + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .post(ASSET_REQUEST_PATH) + .then() + .statusCode(400); + + } + + @Test + 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") + .endpoint(url) + .build(); + + var response = Map.of("response", "ok"); + var responseBytes = mapper.writeValueAsBytes(response); + + var datasource = mock(DataSource.class); + var partStream = mock(DataSource.Part.class); + + when(datasource.openPartStream()).thenReturn(StreamResult.success(Stream.of(partStream))); + when(partStream.openStream()).thenReturn(new ByteArrayInputStream(responseBytes)); + + when(cache.resolveReference(transferProcessId)).thenReturn(edr); + when(dataPlaneManager.transfer(any(DataSink.class), any())) + .thenAnswer(a -> { + AsyncStreamingDataSink sink = a.getArgument(0); + return sink.transfer(datasource); + }); + + var proxyResponseBytes = baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .post(ASSET_REQUEST_PATH) + .then() + .statusCode(200) + .extract().body().asByteArray(); + + var proxyResponse = mapper.readValue(proxyResponseBytes, new TypeReference>() { + }); + + assertThat(proxyResponse).containsAllEntriesOf(response); + + var captor = ArgumentCaptor.forClass(DataFlowRequest.class); + verify(dataPlaneManager).transfer(any(DataSink.class), captor.capture()); + + + var flowRequest = captor.getValue(); + + assertThat(flowRequest.getSourceDataAddress().getStringProperty(BASE_URL)).isEqualTo(edr.getEndpoint()); + + assertThat(flowRequest.getProperties().get(QUERY_PARAMS)).isEqualTo(request.get(QUERY_PARAMS)); + assertThat(flowRequest.getProperties().get(PATH)).isEqualTo(request.get(PATH)); + + } + + @Override + protected Object controller() { + return new ConsumerAssetRequestController(cache, dataPlaneManager, Executors.newSingleThreadExecutor(), mock(Monitor.class)); + } + + private RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port) + .basePath("/") + .when(); + } +} diff --git a/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCache.java b/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCache.java deleted file mode 100644 index 2af831aa8..000000000 --- a/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCache.java +++ /dev/null @@ -1,188 +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 - * - */ - -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.ResultSetMapper; -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.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.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.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static java.lang.String.format; -import static org.eclipse.edc.sql.SqlQueryExecutor.executeQuery; -import static org.eclipse.edc.sql.SqlQueryExecutor.executeQuerySingle; - -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 Clock clock; - private final Vault vault; - - - public SqlEndpointDataReferenceCache(DataSourceRegistry dataSourceRegistry, String dataSourceName, TransactionContext transactionContext, EdrStatements statements, ObjectMapper objectMapper, Vault vault, Clock clock) { - super(dataSourceRegistry, dataSourceName, transactionContext, objectMapper); - this.statements = statements; - this.clock = clock; - this.vault = vault; - } - - @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); - } - }); - } - - private T findById(Connection connection, String id, ResultSetMapper resultSetMapper) { - var sql = statements.getFindByTransferProcessIdTemplate(); - return executeQuerySingle(connection, false, resultSetMapper, sql, id); - } - - @Override - public @NotNull List referencesForAsset(String assetId) { - return internalQuery(queryFor("assetId", assetId), this::mapToEdrId).map(this::referenceFromEntry).collect(Collectors.toList()); - } - - @NotNull - private Stream internalQuery(QuerySpec spec, ResultSetMapper resultSetMapper) { - try { - var queryStmt = statements.createQuery(spec); - return executeQuery(getConnection(), true, resultSetMapper, queryStmt.getQueryAsString(), queryStmt.getParameters()); - } catch (SQLException exception) { - throw new EdcPersistenceException(exception); - } - } - - @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(); - var createdAt = clock.millis(); - executeQuery(connection, sql, entry.getTransferProcessId(), entry.getAssetId(), entry.getAgreementId(), edr.getId(), createdAt, createdAt); - vault.storeSecret(VAULT_PREFIX + edr.getId(), toJson(edr)).orElseThrow((failure) -> new EdcPersistenceException(failure.getFailureDetail())); - } catch (Exception 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) { - executeQuery(connection, statements.getDeleteByIdTemplate(), id); - vault.deleteSecret(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 (Exception exception) { - throw new EdcPersistenceException(exception); - } - }); - } - - - private EndpointDataReferenceEntry mapResultSet(ResultSet resultSet) throws SQLException { - return EndpointDataReferenceEntry.Builder.newInstance() - .transferProcessId(resultSet.getString(statements.getTransferProcessIdColumn())) - .assetId(resultSet.getString(statements.getAssetIdColumn())) - .agreementId(resultSet.getString(statements.getAgreementIdColumn())) - .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(VAULT_PREFIX + edrId); - if (edr != null) { - return fromJson(edr, EndpointDataReference.class); - } - return null; - } - - private QuerySpec queryFor(String field, String value) { - var filter = Criterion.Builder.newInstance() - .operandLeft(field) - .operator("=") - .operandRight(value) - .build(); - - return QuerySpec.Builder.newInstance().filter(filter).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-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/BaseSqlEdrStatements.java b/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/BaseSqlEdrStatements.java deleted file mode 100644 index 1e4111928..000000000 --- a/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/BaseSqlEdrStatements.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020 - 2022 Microsoft Corporation - * - * This program and the accompanying materials are 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.tractusx.edc.edr.store.sql.schema; - -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.sql.translation.SqlQueryStatement; - -import static java.lang.String.format; - -public class BaseSqlEdrStatements implements EdrStatements { - - @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)); - } - - @Override - public String getInsertTemplate() { - return format("INSERT INTO %s (%s, %s, %s, %s,%s, %s) VALUES (?, ?, ?, ?, ?, ?)", - getEdrTable(), - getTransferProcessIdColumn(), - getAssetIdColumn(), - getAgreementIdColumn(), - getEdrId(), - getCreatedAtColumn(), - getUpdatedAtColumn() - ); - } - - @Override - public String getDeleteByIdTemplate() { - return format("DELETE FROM %s WHERE %s = ?", - getEdrTable(), - getTransferProcessIdColumn()); - } -} diff --git a/edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/PostgresqlTransactionalStoreSetupExtension.java b/edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/PostgresqlTransactionalStoreSetupExtension.java deleted file mode 100644 index 119484baf..000000000 --- a/edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/PostgresqlTransactionalStoreSetupExtension.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * This program and the accompanying materials are 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.tractusx.edc.edr.store.sql; - -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.sql.testfixtures.PostgresqlLocalInstance; -import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry; -import org.eclipse.edc.transaction.local.LocalDataSourceRegistry; -import org.eclipse.edc.transaction.local.LocalTransactionContext; -import org.eclipse.edc.transaction.spi.TransactionContext; -import org.junit.jupiter.api.extension.AfterEachCallback; -import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ParameterContext; -import org.junit.jupiter.api.extension.ParameterResolutionException; -import org.junit.jupiter.api.extension.ParameterResolver; - -import java.sql.Connection; -import java.util.UUID; -import javax.sql.DataSource; - -import static org.eclipse.edc.sql.SqlQueryExecutor.executeQuery; -import static org.mockito.Mockito.doCallRealMethod; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -/** - * Extension for running PG SQL store implementation. It automatically creates a test database and provided all the base data structure - * for a SQL store to run such as {@link DataSourceRegistry}, {@link TransactionContext} and data source name which is automatically generated - */ -public class PostgresqlTransactionalStoreSetupExtension implements BeforeEachCallback, AfterEachCallback, BeforeAllCallback, ParameterResolver { - - private final String datasourceName; - private DataSourceRegistry dataSourceRegistry = null; - private DataSource dataSource = null; - private Connection connection = null; - private LocalTransactionContext transactionContext = null; - private Monitor monitor = mock(Monitor.class); - - public PostgresqlTransactionalStoreSetupExtension(String datasourceName) { - this.datasourceName = datasourceName; - } - - public PostgresqlTransactionalStoreSetupExtension() { - this(UUID.randomUUID().toString()); - } - - - public DataSource getDataSource() { - return dataSource; - } - - public String getDatasourceName() { - return datasourceName; - } - - public Connection getConnection() { - return connection; - } - - public int runQuery(String query) { - return transactionContext.execute(() -> executeQuery(connection, query)); - } - - - public TransactionContext getTransactionContext() { - return transactionContext; - } - - public DataSourceRegistry getDataSourceRegistry() { - return dataSourceRegistry; - } - - @Override - public void beforeEach(ExtensionContext context) throws Exception { - transactionContext = new LocalTransactionContext(monitor); - dataSourceRegistry = new LocalDataSourceRegistry(transactionContext); - dataSource = mock(DataSource.class); - dataSourceRegistry.register(datasourceName, dataSource); - connection = spy(PostgresqlLocalInstance.getTestConnection()); - when(dataSource.getConnection()).thenReturn(connection); - doNothing().when(connection).close(); - } - - @Override - public void afterEach(ExtensionContext context) throws Exception { - doCallRealMethod().when(connection).close(); - connection.close(); - } - - @Override - public void beforeAll(ExtensionContext context) throws Exception { - PostgresqlLocalInstance.createTestDatabase(); - } - - @Override - public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { - var type = parameterContext.getParameter().getParameterizedType(); - return type.equals(PostgresqlTransactionalStoreSetupExtension.class); - } - - @Override - public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws - ParameterResolutionException { - var type = parameterContext.getParameter().getParameterizedType(); - if (type.equals(PostgresqlTransactionalStoreSetupExtension.class)) { - return this; - } - return null; - } -} diff --git a/edc-extensions/control-plane-adapter-api/README.md b/edc-extensions/edr/edr-api/README.md similarity index 79% rename from edc-extensions/control-plane-adapter-api/README.md rename to edc-extensions/edr/edr-api/README.md index 5d6b09e2a..084715dcc 100644 --- a/edc-extensions/control-plane-adapter-api/README.md +++ b/edc-extensions/edr/edr-api/README.md @@ -1,4 +1,4 @@ -# Control Plane Adapter API (EDR management) +# Control Plane EDR API This module provides extensions to the EDC management API for dealing with EDR tokens. @@ -15,6 +15,6 @@ This module for now provides three APIs: 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) +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) +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/build.gradle.kts b/edc-extensions/edr/edr-api/build.gradle.kts new file mode 100644 index 000000000..cc3e8cad7 --- /dev/null +++ b/edc-extensions/edr/edr-api/build.gradle.kts @@ -0,0 +1,36 @@ +/* + * 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 + * + */ + +plugins { + `java-library` + `maven-publish` + id("io.swagger.core.v3.swagger-gradle-plugin") +} + +dependencies { + implementation(project(":spi:callback-spi")) + implementation(project(":spi:edr-spi")) + implementation(project(":spi:core-spi")) + + implementation(libs.edc.api.management) + implementation(libs.edc.spi.aggregateservices) + implementation(libs.edc.core.validator) + 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) +} diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApi.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApi.java similarity index 60% rename from edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApi.java rename to edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApi.java index 36d05c073..a09601b36 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApi.java +++ b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApi.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter; +package org.eclipse.tractusx.edc.api.edr; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; @@ -21,42 +21,40 @@ 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.DataAddressDto; -import org.eclipse.edc.api.model.IdResponseDto; +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.cp.adapter.dto.NegotiateEdrRequestDto; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; - -import java.util.List; +import org.eclipse.tractusx.edc.api.edr.schema.EdrSchema; @OpenAPIDefinition -@Tag(name = "Control Plane Adapter EDR Api") -public interface AdapterEdrApi { +@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 = IdResponseDto.class))), + 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 = NegotiateEdrRequestDto.class) JsonObject dto); + JsonObject initiateEdrNegotiation(@Schema(implementation = EdrSchema.NegotiateEdrRequestSchema.class) JsonObject dto); @Operation(description = "Returns all EndpointDataReference entry according to a query", responses = { - @ApiResponse(responseCode = "200", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = EndpointDataReferenceEntry.class)))), + @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))))} + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) } ) - List queryEdrs(String assetId, String agreementId); + JsonArray queryEdrs(String assetId, String agreementId, String providerId); - @Operation(description = "Gets an EDR with the given transfer process ID)", + @Operation(description = "Gets an EDR with the given transfer process ID", responses = { @ApiResponse(responseCode = "200", description = "The EDR cached", - content = @Content(schema = @Schema(implementation = DataAddressDto.class))), + 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", @@ -64,4 +62,15 @@ public interface AdapterEdrApi { } ) 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/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterApiExtension.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtension.java similarity index 55% rename from edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterApiExtension.java rename to edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtension.java index d9e11e41b..ba28aa1ba 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterApiExtension.java +++ b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtension.java @@ -12,25 +12,29 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter; +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.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.cp.adapter.transform.EndpointDataReferenceToDataAddressTransformer; -import org.eclipse.tractusx.edc.api.cp.adapter.transform.JsonObjectFromEndpointDataReferenceEntryTransformer; -import org.eclipse.tractusx.edc.api.cp.adapter.transform.JsonObjectToNegotiateEdrRequestDtoTransformer; -import org.eclipse.tractusx.edc.api.cp.adapter.transform.NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer; -import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService; +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.edr.spi.service.EdrService; +import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE; import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_PREFIX; -public class AdapterApiExtension implements ServiceExtension { +public class EdrApiExtension implements ServiceExtension { @Inject private WebService webService; @@ -38,14 +42,20 @@ public class AdapterApiExtension implements ServiceExtension { private ManagementApiConfiguration apiConfig; @Inject - private AdapterTransferProcessService adapterTransferProcessService; + private EdrService edrService; @Inject - private TypeTransformerRegistry transformerRegistry; + private ManagementApiTypeTransformerRegistry transformerRegistry; @Inject private JsonLd jsonLdService; + @Inject + private JsonObjectValidatorRegistry validatorRegistry; + + @Inject + private Monitor monitor; + @Override public void initialize(ServiceExtensionContext context) { jsonLdService.registerNamespace(TX_PREFIX, TX_NAMESPACE); @@ -53,6 +63,7 @@ public void initialize(ServiceExtensionContext context) { transformerRegistry.register(new JsonObjectToNegotiateEdrRequestDtoTransformer()); transformerRegistry.register(new JsonObjectFromEndpointDataReferenceEntryTransformer()); transformerRegistry.register(new EndpointDataReferenceToDataAddressTransformer()); - webService.registerResource(apiConfig.getContextAlias(), new AdapterEdrController(adapterTransferProcessService, jsonLdService, transformerRegistry)); + validatorRegistry.register(EDR_REQUEST_DTO_TYPE, NegotiateEdrRequestDtoValidator.instance()); + webService.registerResource(apiConfig.getContextAlias(), new EdrController(edrService, transformerRegistry, validatorRegistry, monitor)); } } 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 new file mode 100644 index 000000000..97a5344dd --- /dev/null +++ b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/EdrController.java @@ -0,0 +1,149 @@ +/* + * 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 + * + */ + +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.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("providerId") String providerId) { + if (assetId == null && agreementId == null) { + throw new InvalidRequestException("At least one of this query parameter is required [assetId,agreementId]"); + } + return edrService.findBy(querySpec(assetId, agreementId, 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 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 (providerId != null) { + queryBuilder.filter(fieldFilter(PROVIDER_ID, agreementId)); + } + 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/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/dto/NegotiateEdrRequestDto.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/dto/NegotiateEdrRequestDto.java similarity index 95% rename from edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/dto/NegotiateEdrRequestDto.java rename to edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/dto/NegotiateEdrRequestDto.java index b49e928c0..318cfb575 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/dto/NegotiateEdrRequestDto.java +++ b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/dto/NegotiateEdrRequestDto.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter.dto; +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; @@ -25,7 +25,8 @@ public class NegotiateEdrRequestDto { - public static final String EDR_REQUEST_DTO_TYPE = TX_NAMESPACE + "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_CONNECTOR_ADDRESS = EDC_NAMESPACE + "connectorAddress"; public static final String EDR_REQUEST_DTO_PROTOCOL = EDC_NAMESPACE + "protocol"; public static final String EDR_REQUEST_DTO_CONNECTOR_ID = EDC_NAMESPACE + "connectorId"; 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 new file mode 100644 index 000000000..3c1d6aa4f --- /dev/null +++ b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/schema/EdrSchema.java @@ -0,0 +1,104 @@ +/* + * 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 + * + */ + +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", + "connectorAddress": "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", + "edc:agreementId": "MQ==:MQ==:ZTY3MzQ4YWEtNTdmZC00YzA0LTg2ZmQtMGMxNzk0MWM3OTkw", + "edc:transferProcessId": "78a66945-d638-4c0a-be71-b35a0318a410", + "edc:assetId": "1", + "edc: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/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/EndpointDataReferenceToDataAddressTransformer.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformer.java similarity index 97% rename from edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/EndpointDataReferenceToDataAddressTransformer.java rename to edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformer.java index 60ff11e3d..817eb5bb2 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/EndpointDataReferenceToDataAddressTransformer.java +++ b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformer.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter.transform; +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; diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java similarity index 59% rename from edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java rename to edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java index 36676d0c3..ce0d1ef97 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java +++ b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java @@ -12,21 +12,24 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter.transform; +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.EndpointDataReferenceEntry; +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.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; -import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; -import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; -import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_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_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 { @@ -42,6 +45,9 @@ public JsonObjectFromEndpointDataReferenceEntryTransformer() { .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_PROVIDER_ID, dto.getProviderId()) + .add(EDR_ENTRY_STATE, dto.getEdrState()) + .add(EDR_ENTRY_EXPIRATION_DATE, dto.getExpirationTimestamp()) .build(); } diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java similarity index 78% rename from edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java rename to edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java index 8479852c6..f877cedda 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java +++ b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter.transform; +package org.eclipse.tractusx.edc.api.edr.transform; import jakarta.json.JsonObject; import jakarta.json.JsonValue; @@ -20,19 +20,19 @@ 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.cp.adapter.dto.NegotiateEdrRequestDto; +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.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_CALLBACK_ADDRESSES; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_CONNECTOR_ADDRESS; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_CONNECTOR_ID; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_OFFER; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_PROTOCOL; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_PROVIDER_ID; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE; +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_CONNECTOR_ADDRESS; +import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_CONNECTOR_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 { diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java similarity index 81% rename from edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java rename to edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java index 675955af9..8a09fcee6 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java +++ b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java @@ -12,17 +12,17 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter.transform; +package org.eclipse.tractusx.edc.api.edr.transform; -import org.eclipse.edc.api.transformer.DtoTransformer; import org.eclipse.edc.connector.contract.spi.types.offer.ContractOffer; import org.eclipse.edc.transform.spi.TransformerContext; -import org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto; -import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; +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 DtoTransformer { +public class NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer implements TypeTransformer { @Override public Class getInputType() { @@ -39,7 +39,6 @@ public Class getOutputType() { var contractOffer = ContractOffer.Builder.newInstance() .id(object.getOffer().getOfferId()) .assetId(object.getOffer().getAssetId()) - .providerId(getId(object.getProviderId(), object.getConnectorAddress())) .policy(object.getOffer().getPolicy()) .build(); 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 new file mode 100644 index 000000000..4807ab212 --- /dev/null +++ b/edc-extensions/edr/edr-api/src/main/java/org/eclipse/tractusx/edc/api/edr/validation/NegotiateEdrRequestDtoValidator.java @@ -0,0 +1,48 @@ +/* + * 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 + * + */ + +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_CONNECTOR_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_CONNECTOR_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/control-plane-adapter-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/edr/edr-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 87% rename from edc-extensions/control-plane-adapter-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/edr/edr-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 23ba7b21c..35dd325ee 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/edr/edr-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -12,4 +12,4 @@ # # -org.eclipse.tractusx.edc.api.cp.adapter.AdapterApiExtension +org.eclipse.tractusx.edc.api.edr.EdrApiExtension diff --git a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApiExtensionTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtensionTest.java similarity index 65% rename from edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApiExtensionTest.java rename to edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtensionTest.java index 8be15fa5e..caa4a4347 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApiExtensionTest.java +++ b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrApiExtensionTest.java @@ -12,17 +12,17 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter; +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.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.web.spi.WebService; -import org.eclipse.tractusx.edc.api.cp.adapter.transform.JsonObjectFromEndpointDataReferenceEntryTransformer; -import org.eclipse.tractusx.edc.api.cp.adapter.transform.JsonObjectToNegotiateEdrRequestDtoTransformer; -import org.eclipse.tractusx.edc.api.cp.adapter.transform.NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer; +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; @@ -34,22 +34,19 @@ import static org.mockito.Mockito.when; @ExtendWith(DependencyInjectionExtension.class) -public class AdapterEdrApiExtensionTest { +public class EdrApiExtensionTest { - AdapterApiExtension extension; - - TypeTransformerRegistry transformerRegistry = mock(TypeTransformerRegistry.class); - - WebService webService = mock(WebService.class); - - ManagementApiConfiguration configuration = mock(ManagementApiConfiguration.class); + private final ManagementApiTypeTransformerRegistry transformerRegistry = mock(); + private final WebService webService = mock(WebService.class); + private final ManagementApiConfiguration configuration = mock(ManagementApiConfiguration.class); + private EdrApiExtension extension; @BeforeEach void setUp(ObjectFactory factory, ServiceExtensionContext context) { context.registerService(WebService.class, webService); - context.registerService(TypeTransformerRegistry.class, transformerRegistry); + context.registerService(ManagementApiTypeTransformerRegistry.class, transformerRegistry); context.registerService(ManagementApiConfiguration.class, configuration); - extension = factory.constructInstance(AdapterApiExtension.class); + extension = factory.constructInstance(EdrApiExtension.class); } @Test @@ -59,7 +56,7 @@ void initialize_ShouldConfigureTheController(ServiceExtensionContext context) { when(configuration.getContextAlias()).thenReturn(alias); extension.initialize(context); - verify(webService).registerResource(eq(alias), isA(AdapterEdrController.class)); + 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 new file mode 100644 index 000000000..9808bcac9 --- /dev/null +++ b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrApiTest.java @@ -0,0 +1,117 @@ +/* + * 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 + * + */ + +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("edc:assetId")); + + assertThat(first(content, EDR_ENTRY_AGREEMENT_ID).getJsonString(VALUE).getString()) + .isEqualTo(jsonObject.getString("edc:agreementId")); + + assertThat(first(content, EDR_ENTRY_TRANSFER_PROCESS_ID).getJsonString(VALUE).getString()) + .isEqualTo(jsonObject.getString("edc:transferProcessId")); + + assertThat(first(content, EDR_ENTRY_PROVIDER_ID).getJsonString(VALUE).getString()) + .isEqualTo(jsonObject.getString("edc: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/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrControllerTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrControllerTest.java similarity index 54% rename from edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrControllerTest.java rename to edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrControllerTest.java index cfeee755d..18d47d67a 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrControllerTest.java +++ b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/EdrControllerTest.java @@ -12,27 +12,36 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter; +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.IdResponseDto; +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.service.spi.result.ServiceResult; +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.transform.spi.TypeTransformerRegistry; +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.cp.adapter.dto.NegotiateEdrRequestDto; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; -import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService; +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; @@ -40,30 +49,37 @@ import static io.restassured.RestAssured.given; import static java.lang.String.format; -import static org.eclipse.edc.api.model.IdResponseDto.EDC_ID_RESPONSE_DTO_TYPE; +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.cp.adapter.TestFunctions.negotiationRequest; -import static org.eclipse.tractusx.edc.api.cp.adapter.TestFunctions.openRequest; +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.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; -import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; -import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; -import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_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.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_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 AdapterEdrControllerTest extends RestControllerTestBase { +public class EdrControllerTest extends RestControllerTestBase { - public static final String ADAPTER_EDR_PATH = "/edrs"; + public static final String EDR_PATH = "/edrs"; private final JsonLd jsonLdService = new TitaniumJsonLd(monitor); - AdapterTransferProcessService adapterTransferProcessService = mock(AdapterTransferProcessService.class); - TypeTransformerRegistry transformerRegistry = mock(TypeTransformerRegistry.class); + EdrService edrService = mock(EdrService.class); + ManagementApiTypeTransformerRegistry transformerRegistry = mock(); + JsonObjectValidatorRegistry validatorRegistry = mock(); @BeforeEach void setup() { @@ -73,22 +89,23 @@ void setup() { @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, EDC_ID_RESPONSE_DTO_TYPE).add(ID, contractNegotiation.getId()).build(); + 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(adapterTransferProcessService.initiateEdrNegotiation(openRequest)).thenReturn(ServiceResult.success(contractNegotiation)); - when(transformerRegistry.transform(any(IdResponseDto.class), eq(JsonObject.class))).thenReturn(Result.success(responseBody)); + 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(ADAPTER_EDR_PATH) + .post(EDR_PATH) .then() .statusCode(200) .body(ID, is(contractNegotiation.getId())); @@ -97,6 +114,7 @@ void initEdrNegotiation_shouldWork_whenValidRequest() { @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")); @@ -104,21 +122,38 @@ void initEdrNegotiation_shouldReturnBadRequest_whenValidInvalidRequest() { baseRequest() .contentType(MediaType.APPLICATION_JSON) .body(request) - .post(ADAPTER_EDR_PATH) + .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(adapterTransferProcessService.findByTransferProcessId(transferProcessId)).thenReturn(ServiceResult.notFound("")); + when(edrService.findByTransferProcessId(transferProcessId)).thenReturn(ServiceResult.notFound("")); baseRequest() .contentType(MediaType.APPLICATION_JSON) - .get(ADAPTER_EDR_PATH + "/" + transferProcessId) + .get(EDR_PATH + "/" + transferProcessId) .then() .statusCode(404); } @@ -134,13 +169,13 @@ void getEdr_shouldReturnDataAddress_whenFound() { .build(); var dataAddress = DataAddress.Builder.newInstance().type("HttpData").build(); - when(adapterTransferProcessService.findByTransferProcessId(transferProcessId)).thenReturn(ServiceResult.success(edr)); + 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(ADAPTER_EDR_PATH + "/" + transferProcessId) + .get(EDR_PATH + "/" + transferProcessId) .then() .statusCode(200) .body("'edc:endpoint'", is(edr.getEndpoint())) @@ -151,14 +186,18 @@ void getEdr_shouldReturnDataAddress_whenFound() { @Test void queryEdrs_shouldReturnCachedEntries_whenAssetIdIsProvided() { - var assetId = "id"; - var transferProcessId = "id"; - var agreementId = "id"; + 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() @@ -166,19 +205,25 @@ void queryEdrs_shouldReturnCachedEntries_whenAssetIdIsProvided() { .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(); - when(adapterTransferProcessService.findByAssetAndAgreement(assetId, null)).thenReturn(ServiceResult.success(List.of(entry))); + 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(ADAPTER_EDR_PATH + format("?=assetId=%s", assetId)) + .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:assetId'", is(entry.getAssetId())) + .body("[0].'edc:providerId'", is(entry.getProviderId())) + .body("[0].'tx:edrState'", is(entry.getEdrState())); } @@ -187,11 +232,13 @@ void queryEdrs_shouldReturnCachedEntries_whenAgreementIdIsProvided() { var assetId = "id"; var transferProcessId = "id"; var agreementId = "id"; + var providerId = "id"; var entry = EndpointDataReferenceEntry.Builder.newInstance() .transferProcessId(transferProcessId) .agreementId(agreementId) .assetId(assetId) + .providerId(providerId) .build(); @@ -200,34 +247,72 @@ void queryEdrs_shouldReturnCachedEntries_whenAgreementIdIsProvided() { .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(adapterTransferProcessService.findByAssetAndAgreement(null, agreementId)).thenReturn(ServiceResult.success(List.of(entry))); + 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(ADAPTER_EDR_PATH + format("?=agreementId=%s", entry.getAgreementId())) + .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: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(ADAPTER_EDR_PATH) + .get(EDR_PATH) .then() .statusCode(400); } @Override protected Object controller() { - return new AdapterEdrController(adapterTransferProcessService, jsonLdService, transformerRegistry); + return new EdrController(edrService, transformerRegistry, validatorRegistry, monitor); + } + + @Override + protected Object additionalResource() { + final ObjectMapper objectMapper = JacksonJsonLd.createObjectMapper(); + return new JerseyJsonLdInterceptor(this.jsonLdService, objectMapper); } private RequestSpecification baseRequest() { @@ -245,4 +330,12 @@ private ContractNegotiation getContractNegotiation() { .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/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/TestFunctions.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/TestFunctions.java similarity index 93% rename from edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/TestFunctions.java rename to edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/TestFunctions.java index c3ad7947a..b326009aa 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/TestFunctions.java +++ b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/TestFunctions.java @@ -12,15 +12,15 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter; +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.connector.contract.spi.types.offer.ContractOffer; import org.eclipse.edc.policy.model.Policy; -import org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto; -import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; +import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto; +import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; import java.util.UUID; diff --git a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/EndpointDataReferenceToDataAddressTransformerTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformerTest.java similarity index 83% rename from edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/EndpointDataReferenceToDataAddressTransformerTest.java rename to edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformerTest.java index 6fa27db70..a4c88bb88 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/EndpointDataReferenceToDataAddressTransformerTest.java +++ b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/EndpointDataReferenceToDataAddressTransformerTest.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter.transform; +package org.eclipse.tractusx.edc.api.edr.transform; import org.eclipse.edc.transform.spi.TransformerContext; import org.junit.jupiter.api.BeforeEach; @@ -51,10 +51,10 @@ void transform() { assertThat(dataAddress).isNotNull(); assertThat(dataAddress.getType()).isEqualTo(EDR_SIMPLE_TYPE); - assertThat(dataAddress.getProperty(ID)).isEqualTo(dto.getId()); - assertThat(dataAddress.getProperty(ENDPOINT)).isEqualTo(dto.getEndpoint()); - assertThat(dataAddress.getProperty(AUTH_KEY)).isEqualTo(dto.getAuthKey()); - assertThat(dataAddress.getProperty(AUTH_CODE)).isEqualTo(dto.getAuthCode()); + 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/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java similarity index 55% rename from edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java rename to edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java index 79e0ba5ae..a038ee1e0 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java +++ b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java @@ -12,17 +12,23 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter.transform; +package org.eclipse.tractusx.edc.api.edr.transform; import org.eclipse.edc.transform.spi.TransformerContext; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; +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 static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; -import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; -import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_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_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.mockito.Mockito.mock; class JsonObjectFromEndpointDataReferenceEntryTransformerTest { @@ -42,6 +48,9 @@ void transform() { .assetId("id") .transferProcessId("tpId") .agreementId("aId") + .providerId("providerId") + .state(EndpointDataReferenceEntryStates.NEGOTIATED.code()) + .expirationTimestamp(Instant.now().toEpochMilli()) .build(); var jsonObject = transformer.transform(dto, context); @@ -50,5 +59,9 @@ void transform() { assertThat(jsonObject.getJsonString(EDR_ENTRY_AGREEMENT_ID).getString()).isNotNull().isEqualTo(dto.getAgreementId()); 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()); + } } \ No newline at end of file diff --git a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java similarity index 85% rename from edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java rename to edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java index 435be2a05..8d7d5ca46 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java +++ b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java @@ -12,14 +12,13 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter.transform; +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.connector.api.management.contractnegotiation.model.NegotiationInitiateRequestDto; import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.spi.monitor.Monitor; @@ -35,12 +34,13 @@ 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.api.management.contractnegotiation.model.NegotiationInitiateRequestDto.CALLBACK_ADDRESSES; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.NegotiationInitiateRequestDto.CONNECTOR_ADDRESS; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.NegotiationInitiateRequestDto.CONNECTOR_ID; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.NegotiationInitiateRequestDto.OFFER; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.NegotiationInitiateRequestDto.PROTOCOL; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.NegotiationInitiateRequestDto.PROVIDER_ID; +import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.CALLBACK_ADDRESSES; +import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.CONNECTOR_ADDRESS; +import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.CONNECTOR_ID; +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; @@ -72,7 +72,7 @@ void setUp() { @Test void transform() { var jsonObject = Json.createObjectBuilder() - .add(TYPE, NegotiationInitiateRequestDto.TYPE) + .add(TYPE, CONTRACT_REQUEST_TYPE) .add(CONNECTOR_ADDRESS, "test-address") .add(PROTOCOL, "test-protocol") .add(CONNECTOR_ID, "test-conn-id") @@ -115,7 +115,7 @@ void transform_reportErrors() { when(context.problem()).thenReturn(new ProblemBuilder(context)); var jsonObject = Json.createObjectBuilder() - .add(TYPE, NegotiationInitiateRequestDto.TYPE) + .add(TYPE, CONTRACT_REQUEST_TYPE) .add(EDC_NAMESPACE + "notFound", "test-address") .build(); diff --git a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java similarity index 88% rename from edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java rename to edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java index 70656ad71..7fa2a4b7e 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java +++ b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java @@ -12,17 +12,17 @@ * */ -package org.eclipse.tractusx.edc.api.cp.adapter.transform; +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.cp.adapter.dto.NegotiateEdrRequestDto; +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.cp.adapter.TestFunctions.createOffer; +import static org.eclipse.tractusx.edc.api.edr.TestFunctions.createOffer; import static org.mockito.Mockito.mock; public class NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest { @@ -75,7 +75,6 @@ void verify_transfor_withNoProviderId() { var request = transformer.transform(dto, context); assertThat(request).isNotNull(); - assertThat(request.getOffer().getProviderId()).asString().isEqualTo(dto.getConnectorAddress()); } @Test @@ -91,6 +90,5 @@ void verify_transform_withNoConsumerId() { var request = transformer.transform(dto, context); assertThat(request).isNotNull(); - assertThat(request.getOffer().getProviderId()).asString().isEqualTo("urn:connector:test-provider"); } } 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 new file mode 100644 index 000000000..20ba6dbc5 --- /dev/null +++ b/edc-extensions/edr/edr-api/src/test/java/org/eclipse/tractusx/edc/api/edr/validation/NegotiateEdrRequestDtoValidatorTest.java @@ -0,0 +1,95 @@ +/* + * 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 + * + */ + +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_CONNECTOR_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_CONNECTOR_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_CONNECTOR_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_CONNECTOR_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-cache-sql/README.md b/edc-extensions/edr/edr-cache-sql/README.md similarity index 80% rename from edc-extensions/edr-cache-sql/README.md rename to edc-extensions/edr/edr-cache-sql/README.md index 712aeb944..270d1f266 100644 --- a/edc-extensions/edr-cache-sql/README.md +++ b/edc-extensions/edr/edr-cache-sql/README.md @@ -1,15 +1,15 @@ # SQL-based `EndpointDataReferenceCache` extension -This extensions provide a persistent implementation of `EndpointDataReferenceCache`. +This extension provide a persistent implementation of `EndpointDataReferenceCache`. It will store in the database this fields: -- tranferProcessId +- transferProcessId - agreementId - assetId - edrId -It represent a single EDR negotiation done with the new Control Plane Adapter APIs. +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__`. diff --git a/edc-extensions/edr-cache-sql/build.gradle.kts b/edc-extensions/edr/edr-cache-sql/build.gradle.kts similarity index 80% rename from edc-extensions/edr-cache-sql/build.gradle.kts rename to edc-extensions/edr/edr-cache-sql/build.gradle.kts index c1da99b15..d456a3053 100644 --- a/edc-extensions/edr-cache-sql/build.gradle.kts +++ b/edc-extensions/edr/edr-cache-sql/build.gradle.kts @@ -18,17 +18,19 @@ plugins { } dependencies { - implementation(project(":spi:edr-cache-spi")) + implementation(project(":spi:edr-spi")) implementation(libs.edc.spi.core) implementation(libs.edc.core.sql) implementation(libs.edc.spi.transactionspi) implementation(libs.edc.spi.transaction.datasource) + implementation(libs.edc.sql.lease) testImplementation(libs.edc.transaction.local) - testImplementation(testFixtures(project(":spi:edr-cache-spi"))) + testImplementation(testFixtures(project(":spi:edr-spi"))) testImplementation(testFixtures(libs.edc.core.sql)) + testImplementation(testFixtures(libs.edc.sql.lease)) testImplementation(testFixtures(libs.edc.junit)) diff --git a/edc-extensions/edr/edr-cache-sql/docs/schema.sql b/edc-extensions/edr/edr-cache-sql/docs/schema.sql new file mode 100644 index 000000000..9e707af1b --- /dev/null +++ b/edc-extensions/edr/edr-cache-sql/docs/schema.sql @@ -0,0 +1,55 @@ +-- +-- 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, + 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 new file mode 100644 index 000000000..12bc7676a --- /dev/null +++ b/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCache.java @@ -0,0 +1,303 @@ +/* + * 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 + * + */ + +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 Clock clock; + private final Vault vault; + + private final SqlLeaseContextBuilder leaseContext; + + private final String leaseHolder; + + + public SqlEndpointDataReferenceCache(DataSourceRegistry dataSourceRegistry, String dataSourceName, + TransactionContext transactionContext, EdrStatements statements, + ObjectMapper objectMapper, Vault vault, Clock clock, + QueryExecutor queryExecutor, String connectorId) { + super(dataSourceRegistry, dataSourceName, transactionContext, objectMapper, queryExecutor); + this.statements = statements; + this.clock = clock; + this.vault = vault; + this.leaseHolder = connectorId; + 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 StoreResult findByCorrelationIdAndLease(String correlationId) { + return findByIdAndLease(correlationId); + } + + @Override + public void save(EndpointDataReferenceEntry entity) { + throw new UnsupportedOperationException("Please use save(EndpointDataReferenceEntry, EndpointDataReference) instead!"); + } + + @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.getExpirationTimestamp(), + entry.getState(), + entry.getStateCount(), + entry.getStateTimestamp(), + entry.getErrorDetail(), + entry.getCreatedAt(), + entry.getUpdatedAt()); + vault.storeSecret(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(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 @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); + } + }); + } + + 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())) + .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(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-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 similarity index 91% rename from edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtension.java rename to edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtension.java index 1add47e9a..dcb4cb211 100644 --- a/edc-extensions/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 @@ -22,9 +22,10 @@ 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.EndpointDataReferenceCache; +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; @@ -51,6 +52,9 @@ public class SqlEndpointDataReferenceCacheExtension implements ServiceExtension @Inject private Vault vault; + @Inject + private QueryExecutor queryExecutor; + @Override public String name() { return NAME; @@ -59,7 +63,7 @@ public String name() { @Provider public EndpointDataReferenceCache edrCache(ServiceExtensionContext context) { var dataSourceName = context.getConfig().getString(DATASOURCE_SETTING_NAME, DEFAULT_DATASOURCE_NAME); - return new SqlEndpointDataReferenceCache(dataSourceRegistry, dataSourceName, transactionContext, getStatementImpl(), typeManager.getMapper(), vault, clock); + return new SqlEndpointDataReferenceCache(dataSourceRegistry, dataSourceName, transactionContext, getStatementImpl(), typeManager.getMapper(), vault, clock, queryExecutor, context.getConnectorId()); } private EdrStatements getStatementImpl() { 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 new file mode 100644 index 000000000..ddd2d462d --- /dev/null +++ b/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/BaseSqlEdrStatements.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020 - 2022 Microsoft Corporation + * + * This program and the accompanying materials are 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: + * Microsoft Corporation - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.edr.store.sql.schema; + +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.sql.translation.SqlQueryStatement; + +import static java.lang.String.format; + +public class BaseSqlEdrStatements implements EdrStatements { + + @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)); + } + + @Override + public String getInsertTemplate() { + return format("INSERT INTO %s (%s, %s, %s, %s,%s, %s, %s, %s, %s, %s, %s, %s) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + getEdrTable(), + getTransferProcessIdColumn(), + getAssetIdColumn(), + getAgreementIdColumn(), + getEdrId(), + getProviderIdColumn(), + 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-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 similarity index 82% rename from edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrMapping.java rename to edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrMapping.java index fbc615bff..fa75652a3 100644 --- a/edc-extensions/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 @@ -15,7 +15,7 @@ package org.eclipse.tractusx.edc.edr.store.sql.schema; import org.eclipse.edc.sql.translation.TranslationMapping; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; +import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; /** * Maps fields of a {@link EndpointDataReferenceEntry} onto the @@ -25,5 +25,7 @@ public class EdrMapping extends TranslationMapping { public EdrMapping(EdrStatements statements) { add("assetId", statements.getAssetIdColumn()); add("agreementId", statements.getAgreementIdColumn()); + add("providerId", statements.getProviderIdColumn()); + add("state", statements.getStateColumn()); } } diff --git a/edc-extensions/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 similarity index 68% rename from edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrStatements.java rename to edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrStatements.java index d755dc2e4..9414a3d1b 100644 --- a/edc-extensions/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 @@ -15,12 +15,13 @@ 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 { +public interface EdrStatements extends LeaseStatements { default String getEdrTable() { return "edc_edr_cache"; @@ -34,6 +35,10 @@ default String getAgreementIdColumn() { return "agreement_id"; } + default String getProviderIdColumn() { + return "provider_id"; + } + default String getAssetIdColumn() { return "asset_id"; } @@ -50,6 +55,25 @@ 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(); @@ -57,6 +81,8 @@ default String getUpdatedAtColumn() { String getInsertTemplate(); + String getUpdateTemplate(); + String getDeleteByIdTemplate(); } diff --git a/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/postgres/PostgresEdrStatements.java b/edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/postgres/PostgresEdrStatements.java similarity index 100% rename from edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/postgres/PostgresEdrStatements.java rename to edc-extensions/edr/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/postgres/PostgresEdrStatements.java diff --git a/edc-extensions/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 similarity index 100% rename from edc-extensions/edr-cache-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-extensions/edr/edr-cache-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension diff --git a/edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtensionTest.java b/edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtensionTest.java similarity index 100% rename from edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtensionTest.java rename to edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtensionTest.java diff --git a/edc-extensions/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 similarity index 80% rename from edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTest.java rename to edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTest.java index f89b786e0..2b80d0e21 100644 --- a/edc-extensions/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 @@ -18,9 +18,11 @@ 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.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCacheBaseTest; +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; @@ -33,6 +35,7 @@ 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; @@ -47,7 +50,7 @@ @PostgresqlDbIntegrationTest @ExtendWith(PostgresqlStoreSetupExtension.class) -public class SqlEndpointDataReferenceCacheTest extends EndpointDataReferenceCacheBaseTest { +public class SqlEndpointDataReferenceCacheTest extends EndpointDataReferenceCacheTestBase { EdrStatements statements = new PostgresEdrStatements(); SqlEndpointDataReferenceCache cache; @@ -58,17 +61,19 @@ public class SqlEndpointDataReferenceCacheTest extends EndpointDataReferenceCach TypeManager typeManager = new TypeManager(); + LeaseUtil leaseUtil; @BeforeEach - void setUp(PostgresqlStoreSetupExtension extension) throws IOException { + 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); + 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); } @@ -96,6 +101,16 @@ 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-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 similarity index 88% rename from edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTransactionalTest.java rename to edc-extensions/edr/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTransactionalTest.java index b88ce8f12..20ffe673c 100644 --- a/edc-extensions/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 @@ -22,6 +22,8 @@ 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; @@ -38,6 +40,7 @@ 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; @@ -49,7 +52,7 @@ import static org.mockito.Mockito.when; @PostgresqlDbIntegrationTest -@ExtendWith(PostgresqlTransactionalStoreSetupExtension.class) +@ExtendWith(PostgresqlStoreSetupExtension.class) public class SqlEndpointDataReferenceCacheTransactionalTest { EdrStatements statements = new PostgresEdrStatements(); @@ -62,12 +65,12 @@ public class SqlEndpointDataReferenceCacheTransactionalTest { TypeManager typeManager = new TypeManager(); @BeforeEach - void setUp(PostgresqlTransactionalStoreSetupExtension extension) throws IOException { + 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); + 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); @@ -113,7 +116,7 @@ void save() { .extracting(EndpointDataReference::getId) .isEqualTo(edrId); - var edrs = cache.referencesForAsset(assetId); + var edrs = cache.referencesForAsset(assetId, null); assertThat(edrs.size()).isEqualTo(1); assertThat(edrs.get((0)).getId()).isEqualTo(edrId); @@ -134,7 +137,7 @@ void deleteByTransferProcessId_shouldDelete_WhenFound() { .isEqualTo(entry); assertThat(cache.resolveReference(entry.getTransferProcessId())).isNull(); - assertThat(cache.referencesForAsset(entry.getAssetId())).hasSize(0); + assertThat(cache.referencesForAsset(entry.getAssetId(), null)).hasSize(0); assertThat(cache.queryForEntries(QuerySpec.max())).hasSize(0); verify(vault).storeSecret(eq(VAULT_PREFIX + edr.getId()), any()); @@ -142,7 +145,7 @@ void deleteByTransferProcessId_shouldDelete_WhenFound() { } @AfterEach - void tearDown(PostgresqlTransactionalStoreSetupExtension extension) throws SQLException { + void tearDown(PostgresqlStoreSetupExtension extension) throws SQLException { extension.runQuery("DROP TABLE " + statements.getEdrTable() + " CASCADE"); } diff --git a/edc-extensions/control-plane-adapter-callback/build.gradle.kts b/edc-extensions/edr/edr-callback/build.gradle.kts similarity index 76% rename from edc-extensions/control-plane-adapter-callback/build.gradle.kts rename to edc-extensions/edr/edr-callback/build.gradle.kts index ffb126fed..5ce2eade1 100644 --- a/edc-extensions/control-plane-adapter-callback/build.gradle.kts +++ b/edc-extensions/edr/edr-callback/build.gradle.kts @@ -18,13 +18,14 @@ plugins { } dependencies { - implementation(project(":spi:control-plane-adapter-spi")) - implementation(project(":spi:edr-cache-spi")) + implementation(project(":spi:callback-spi")) + implementation(project(":spi:edr-spi")) + implementation(project(":spi:core-spi")) + implementation(libs.edc.spi.core) implementation(libs.edc.spi.transfer) - implementation(libs.edc.spi.contract) implementation(libs.edc.spi.controlplane) - implementation(libs.edc.spi.aggregateservices) + implementation(libs.nimbus.jwt) testImplementation(libs.edc.junit) } diff --git a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/ContractNegotiationCallback.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallback.java similarity index 74% rename from edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/ContractNegotiationCallback.java rename to edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallback.java index 80082a366..d9b29bc5b 100644 --- a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/ContractNegotiationCallback.java +++ b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallback.java @@ -12,18 +12,17 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +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.DataRequest; import org.eclipse.edc.connector.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; import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallback; +import org.eclipse.tractusx.edc.spi.callback.InProcessCallback; import java.util.UUID; @@ -51,20 +50,14 @@ public Result invoke(CallbackEventRemoteMessage messa private Result initiateTransfer(ContractNegotiationFinalized negotiationFinalized) { - var dataRequest = - DataRequest.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .assetId(negotiationFinalized.getContractAgreement().getAssetId()) - .contractId(negotiationFinalized.getContractAgreement().getId()) - .connectorId(negotiationFinalized.getCounterPartyId()) - .connectorAddress(negotiationFinalized.getCounterPartyAddress()) - .protocol(negotiationFinalized.getProtocol()) - .dataDestination(DATA_DESTINATION) - .managedResources(false) - .build(); - var transferRequest = TransferRequest.Builder.newInstance() - .dataRequest(dataRequest) + .id(UUID.randomUUID().toString()) + .assetId(negotiationFinalized.getContractAgreement().getAssetId()) + .contractId(negotiationFinalized.getContractAgreement().getId()) + .connectorId(negotiationFinalized.getCounterPartyId()) + .connectorAddress(negotiationFinalized.getCounterPartyAddress()) + .protocol(negotiationFinalized.getProtocol()) + .dataDestination(DATA_DESTINATION) .callbackAddresses(negotiationFinalized.getCallbackAddresses()) .build(); diff --git a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackMessageDispatcher.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackMessageDispatcher.java similarity index 93% rename from edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackMessageDispatcher.java rename to edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackMessageDispatcher.java index 21a106f42..e4faac1bc 100644 --- a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackMessageDispatcher.java +++ b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackMessageDispatcher.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +package org.eclipse.tractusx.edc.callback; import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; import org.eclipse.edc.spi.EdcException; @@ -21,7 +21,7 @@ import org.eclipse.edc.spi.response.ResponseStatus; import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.types.domain.message.RemoteMessage; -import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallbackRegistry; +import org.eclipse.tractusx.edc.spi.callback.InProcessCallbackRegistry; import java.util.concurrent.CompletableFuture; diff --git a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackRegistryExtension.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryExtension.java similarity index 88% rename from edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackRegistryExtension.java rename to edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryExtension.java index f7bb6ba06..9164a9c8c 100644 --- a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackRegistryExtension.java +++ b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryExtension.java @@ -12,12 +12,12 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +package org.eclipse.tractusx.edc.callback; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Provider; import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallbackRegistry; +import org.eclipse.tractusx.edc.spi.callback.InProcessCallbackRegistry; @Extension(InProcessCallbackRegistryExtension.NAME) public class InProcessCallbackRegistryExtension implements ServiceExtension { diff --git a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackRegistryImpl.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryImpl.java similarity index 85% rename from edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackRegistryImpl.java rename to edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryImpl.java index d65c4e5ae..f3942ebbc 100644 --- a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackRegistryImpl.java +++ b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryImpl.java @@ -12,13 +12,13 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +package org.eclipse.tractusx.edc.callback; import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; import org.eclipse.edc.spi.event.Event; import org.eclipse.edc.spi.result.Result; -import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallback; -import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallbackRegistry; +import org.eclipse.tractusx.edc.spi.callback.InProcessCallback; +import org.eclipse.tractusx.edc.spi.callback.InProcessCallbackRegistry; import java.util.ArrayList; import java.util.List; diff --git a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtension.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtension.java similarity index 76% rename from edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtension.java rename to edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtension.java index 6b60a4eff..1eb68859e 100644 --- a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtension.java +++ b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtension.java @@ -12,26 +12,23 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +package org.eclipse.tractusx.edc.callback; import org.eclipse.edc.connector.spi.callback.CallbackProtocolResolverRegistry; -import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService; import org.eclipse.edc.connector.spi.transferprocess.TransferProcessService; import org.eclipse.edc.connector.transfer.spi.store.TransferProcessStore; 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.message.RemoteMessageDispatcherRegistry; 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.transaction.spi.TransactionContext; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallbackRegistry; -import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService; +import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.spi.callback.InProcessCallbackRegistry; -import static org.eclipse.tractusx.edc.cp.adapter.callback.InProcessCallbackMessageDispatcher.CALLBACK_EVENT_LOCAL; +import static org.eclipse.tractusx.edc.callback.InProcessCallbackMessageDispatcher.CALLBACK_EVENT_LOCAL; @Extension(LocalCallbackExtension.NAME) public class LocalCallbackExtension implements ServiceExtension { @@ -47,9 +44,6 @@ public class LocalCallbackExtension implements ServiceExtension { @Inject private TransferProcessService transferProcessService; - @Inject - private ContractNegotiationService contractNegotiationService; - @Inject private TransferProcessStore transferProcessStore; @@ -80,18 +74,13 @@ public String name() { public void initialize(ServiceExtensionContext context) { callbackRegistry.registerHandler(new ContractNegotiationCallback(transferProcessService, monitor)); - callbackRegistry.registerHandler(new TransferProcessLocalCallback(edrCache, transferProcessStore, transformerRegistry, transactionContext)); + callbackRegistry.registerHandler(new TransferProcessLocalCallback(edrCache, transferProcessStore, transformerRegistry, transactionContext, monitor)); resolverRegistry.registerResolver(this::resolveProtocol); registry.register(new InProcessCallbackMessageDispatcher(callbackRegistry)); } - @Provider - public AdapterTransferProcessService adapterTransferProcessService() { - return new AdapterTransferProcessServiceImpl(contractNegotiationService, endpointDataReferenceCache); - } - private String resolveProtocol(String scheme) { if (scheme.equalsIgnoreCase(LOCAL)) { diff --git a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/TransferProcessLocalCallback.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallback.java similarity index 53% rename from edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/TransferProcessLocalCallback.java rename to edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallback.java index c46ab8720..50c1791a5 100644 --- a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/TransferProcessLocalCallback.java +++ b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallback.java @@ -12,19 +12,27 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +package org.eclipse.tractusx.edc.callback; +import com.nimbusds.jwt.SignedJWT; import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; import org.eclipse.edc.connector.transfer.spi.event.TransferProcessStarted; 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.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallback; +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 static java.lang.String.format; @@ -36,11 +44,14 @@ public class TransferProcessLocalCallback implements InProcessCallback { private final TransactionContext transactionContext; - public TransferProcessLocalCallback(EndpointDataReferenceCache edrCache, TransferProcessStore transferProcessStore, TypeTransformerRegistry transformerRegistry, TransactionContext transactionContext) { + private final Monitor monitor; + + public TransferProcessLocalCallback(EndpointDataReferenceCache edrCache, TransferProcessStore transferProcessStore, TypeTransformerRegistry transformerRegistry, TransactionContext transactionContext, Monitor monitor) { this.edrCache = edrCache; this.transferProcessStore = transferProcessStore; this.transformerRegistry = transformerRegistry; this.transactionContext = transactionContext; + this.monitor = monitor; } @Override @@ -57,16 +68,25 @@ public Result invoke(CallbackEventRemoteMessage messa private Result storeEdr(EndpointDataReference edr) { return transactionContext.execute(() -> { - // TODO upstream api for getting the TP with the DataRequest#id var transferProcess = transferProcessStore.findForCorrelationId(edr.getId()); if (transferProcess != null) { + 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(transferProcess.getDataRequest().getConnectorId()) + .state(EndpointDataReferenceEntryStates.NEGOTIATED.code()) + .expirationTimestamp(expirationTime.getContent()) .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())); @@ -74,4 +94,43 @@ private Result storeEdr(EndpointDataReference edr) { }); } + + 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); + })); + } + + 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, String value) { + return Criterion.Builder.newInstance() + .operandLeft(field) + .operator("=") + .operandRight(value) + .build(); + } } diff --git a/edc-extensions/edr/edr-callback/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/edr/edr-callback/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..aa0b83674 --- /dev/null +++ b/edc-extensions/edr/edr-callback/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,16 @@ +# +# 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 +# +# + +org.eclipse.tractusx.edc.callback.InProcessCallbackRegistryExtension +org.eclipse.tractusx.edc.callback.LocalCallbackExtension diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/ContractNegotiationCallbackTest.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallbackTest.java similarity index 63% rename from edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/ContractNegotiationCallbackTest.java rename to edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallbackTest.java index 62545a8b5..afe4dbea5 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/ContractNegotiationCallbackTest.java +++ b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/ContractNegotiationCallbackTest.java @@ -12,20 +12,22 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +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.ContractNegotiationConfirmed; -import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationDeclined; +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.ContractNegotiationFailed; +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.contract.spi.types.agreement.ContractAgreement; 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.policy.model.Policy; import org.eclipse.edc.service.spi.result.ServiceResult; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; @@ -43,9 +45,9 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.cp.adapter.callback.ContractNegotiationCallback.DATA_DESTINATION; -import static org.eclipse.tractusx.edc.cp.adapter.callback.TestFunctions.getNegotiationFinalizedEvent; -import static org.eclipse.tractusx.edc.cp.adapter.callback.TestFunctions.remoteMessage; +import static org.eclipse.tractusx.edc.callback.ContractNegotiationCallback.DATA_DESTINATION; +import static org.eclipse.tractusx.edc.callback.TestFunctions.getNegotiationFinalizedEvent; +import static org.eclipse.tractusx.edc.callback.TestFunctions.remoteMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -61,6 +63,16 @@ public class ContractNegotiationCallbackTest { ContractNegotiationCallback callback; + private static > B baseBuilder(B builder) { + var callbacks = List.of(CallbackAddress.Builder.newInstance().uri("http://local").events(Set.of("test")).build()); + return builder + .contractNegotiationId("id") + .protocol("test") + .callbackAddresses(callbacks) + .counterPartyAddress("addr") + .counterPartyId("provider"); + } + @BeforeEach void setup() { callback = new ContractNegotiationCallback(transferProcessService, monitor); @@ -82,18 +94,17 @@ void invoke_shouldStartTransferProcess() { verify(transferProcessService).initiateTransfer(captor.capture()); - var tp = captor.getValue(); - - assertThat(tp.getCallbackAddresses()).usingRecursiveFieldByFieldElementComparator().containsAll(event.getCallbackAddresses()); + var transferRequest = captor.getValue(); - assertThat(tp.getDataRequest()).satisfies(dataRequest -> { + assertThat(transferRequest.getCallbackAddresses()).usingRecursiveFieldByFieldElementComparator().containsAll(event.getCallbackAddresses()); - assertThat(dataRequest.getContractId()).isEqualTo(event.getContractAgreement().getId()); - assertThat(dataRequest.getAssetId()).isEqualTo(event.getContractAgreement().getAssetId()); - assertThat(dataRequest.getConnectorAddress()).isEqualTo(event.getCounterPartyAddress()); - assertThat(dataRequest.getConnectorId()).isEqualTo(event.getCounterPartyId()); - assertThat(dataRequest.getProtocol()).isEqualTo(event.getProtocol()); - assertThat(dataRequest.getDataDestination()).usingRecursiveComparison().isEqualTo(DATA_DESTINATION); + assertThat(transferRequest).satisfies(tp -> { + assertThat(tp.getContractId()).isEqualTo(event.getContractAgreement().getId()); + assertThat(tp.getAssetId()).isEqualTo(event.getContractAgreement().getAssetId()); + assertThat(tp.getConnectorAddress()).isEqualTo(event.getCounterPartyAddress()); + assertThat(tp.getConnectorId()).isEqualTo(event.getCounterPartyId()); + assertThat(tp.getProtocol()).isEqualTo(event.getProtocol()); + assertThat(tp.getDataDestination()).usingRecursiveComparison().isEqualTo(DATA_DESTINATION); }); } @@ -122,15 +133,31 @@ void invoke_shouldIgnoreOtherEvents(ContractNegotiationEvent event) { verifyNoInteractions(transferProcessService); } + @Test + void invoke_whenFinalized() { + var evt = baseBuilder(ContractNegotiationFinalized.Builder.newInstance().contractAgreement( + ContractAgreement.Builder.newInstance() + .providerId("test-provider") + .assetId("test-asset") + .policy(Policy.Builder.newInstance().build()) + .id("test-id") + .consumerId("test-consumer") + .build())).build(); + var message = remoteMessage(evt); + when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); + + callback.invoke(message); + verify(transferProcessService).initiateTransfer(any(TransferRequest.class)); + } + private static class EventInstances implements ArgumentsProvider { @Override public Stream provideArguments(ExtensionContext context) { return Stream.of( baseBuilder(ContractNegotiationAccepted.Builder.newInstance()).build(), - baseBuilder(ContractNegotiationConfirmed.Builder.newInstance()).build(), - baseBuilder(ContractNegotiationDeclined.Builder.newInstance()).build(), - baseBuilder(ContractNegotiationFailed.Builder.newInstance()).build(), + baseBuilder(ContractNegotiationAgreed.Builder.newInstance()).build(), + baseBuilder(ContractNegotiationVerified.Builder.newInstance()).build(), baseBuilder(ContractNegotiationInitiated.Builder.newInstance()).build(), baseBuilder(ContractNegotiationOffered.Builder.newInstance()).build(), baseBuilder(ContractNegotiationRequested.Builder.newInstance()).build(), @@ -139,14 +166,6 @@ public Stream provideArguments(ExtensionContext context) { } - private > B baseBuilder(B builder) { - var callbacks = List.of(CallbackAddress.Builder.newInstance().uri("http://local").events(Set.of("test")).build()); - return builder - .contractNegotiationId("id") - .protocol("test") - .callbackAddresses(callbacks) - .counterPartyAddress("addr") - .counterPartyId("provider"); - } + } } diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackMessageDispatcherTest.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/InProcessCallbackMessageDispatcherTest.java similarity index 87% rename from edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackMessageDispatcherTest.java rename to edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/InProcessCallbackMessageDispatcherTest.java index 54e5bef21..6d4763b1c 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackMessageDispatcherTest.java +++ b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/InProcessCallbackMessageDispatcherTest.java @@ -12,18 +12,18 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +package org.eclipse.tractusx.edc.callback; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.spi.types.domain.message.RemoteMessage; -import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallback; +import org.eclipse.tractusx.edc.spi.callback.InProcessCallback; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import static org.eclipse.tractusx.edc.cp.adapter.callback.TestFunctions.getNegotiationFinalizedEvent; -import static org.eclipse.tractusx.edc.cp.adapter.callback.TestFunctions.remoteMessage; +import static org.eclipse.tractusx.edc.callback.TestFunctions.getNegotiationFinalizedEvent; +import static org.eclipse.tractusx.edc.callback.TestFunctions.remoteMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackRegistryExtensionTest.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryExtensionTest.java similarity index 96% rename from edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackRegistryExtensionTest.java rename to edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryExtensionTest.java index 9aef1a0b2..3932688f1 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackRegistryExtensionTest.java +++ b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/InProcessCallbackRegistryExtensionTest.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +package org.eclipse.tractusx.edc.callback; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtensionTest.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtensionTest.java similarity index 86% rename from edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtensionTest.java rename to edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtensionTest.java index afff1d6a4..1f0f35261 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtensionTest.java +++ b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/LocalCallbackExtensionTest.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +package org.eclipse.tractusx.edc.callback; import org.eclipse.edc.connector.spi.callback.CallbackProtocolResolver; import org.eclipse.edc.connector.spi.callback.CallbackProtocolResolverRegistry; @@ -20,15 +20,15 @@ import org.eclipse.edc.spi.message.RemoteMessageDispatcherRegistry; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.spi.system.injection.ObjectFactory; -import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallback; -import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallbackRegistry; +import org.eclipse.tractusx.edc.spi.callback.InProcessCallback; +import org.eclipse.tractusx.edc.spi.callback.InProcessCallbackRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.cp.adapter.callback.InProcessCallbackMessageDispatcher.CALLBACK_EVENT_LOCAL; +import static org.eclipse.tractusx.edc.callback.InProcessCallbackMessageDispatcher.CALLBACK_EVENT_LOCAL; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -64,10 +64,6 @@ void shouldInitializeTheExtension(ServiceExtensionContext context) { assertThat(resolver.resolve("local")).isEqualTo(CALLBACK_EVENT_LOCAL); assertThat(resolver.resolve("test")).isNull(); - - var service = extension.adapterTransferProcessService(); - assertThat(service).isInstanceOf(AdapterTransferProcessServiceImpl.class); - var callbackArgumentCaptor = ArgumentCaptor.forClass(InProcessCallback.class); verify(inProcessCallbackRegistry, times(2)).registerHandler(callbackArgumentCaptor.capture()); diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/TestFunctions.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TestFunctions.java similarity index 75% rename from edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/TestFunctions.java rename to edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TestFunctions.java index ec8593667..a44b2fe4e 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/TestFunctions.java +++ b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TestFunctions.java @@ -12,8 +12,16 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +package org.eclipse.tractusx.edc.callback; +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.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.contract.spi.types.agreement.ContractAgreement; import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; @@ -25,6 +33,8 @@ 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; import java.util.List; import java.util.Set; import java.util.UUID; @@ -73,7 +83,7 @@ public static TransferProcessStarted getTransferProcessStartedEvent(DataAddress public static EndpointDataReference getEdr() { return EndpointDataReference.Builder.newInstance() .id("dataRequestId") - .authCode("authCode") + .authCode(createToken()) .authKey("authKey") .endpoint("http://endpoint") .build(); @@ -93,4 +103,22 @@ public static CallbackEventRemoteMessage remoteMessage(T ev .build(); return new CallbackEventRemoteMessage(callback, envelope, "local"); } + + private static String createToken() { + try { + var key = new RSAKeyGenerator(2048) + .keyUse(KeyUse.SIGNATURE) + .keyID(UUID.randomUUID().toString()) + .generate(); + + var claims = new JWTClaimsSet.Builder().expirationTime(new Date(Instant.now().toEpochMilli())).build(); + var header = new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(UUID.randomUUID().toString()).build(); + + var jwt = new SignedJWT(header, claims); + jwt.sign(new RSASSASigner(key.toPrivateKey())); + return jwt.serialize(); + } catch (JOSEException e) { + throw new RuntimeException(e); + } + } } diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/TransferProcessLocalCallbackTest.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallbackTest.java similarity index 87% rename from edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/TransferProcessLocalCallbackTest.java rename to edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallbackTest.java index d00fe250f..19c5d280e 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/TransferProcessLocalCallbackTest.java +++ b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallbackTest.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.cp.adapter.callback; +package org.eclipse.tractusx.edc.callback; import org.eclipse.edc.connector.transfer.spi.event.TransferProcessCompleted; import org.eclipse.edc.connector.transfer.spi.event.TransferProcessDeprovisioned; @@ -22,6 +22,7 @@ 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; @@ -29,8 +30,9 @@ 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.EndpointDataReferenceCache; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; +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; @@ -47,10 +49,11 @@ 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.cp.adapter.callback.TestFunctions.getEdr; -import static org.eclipse.tractusx.edc.cp.adapter.callback.TestFunctions.getTransferProcessStartedEvent; -import static org.eclipse.tractusx.edc.cp.adapter.callback.TestFunctions.remoteMessage; +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.remoteMessage; 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; @@ -72,7 +75,7 @@ public class TransferProcessLocalCallbackTest { @BeforeEach void setup() { - callback = new TransferProcessLocalCallback(edrCache, transferProcessStore, transformerRegistry, transactionContext); + callback = new TransferProcessLocalCallback(edrCache, transferProcessStore, transformerRegistry, transactionContext, mock(Monitor.class)); } @Test @@ -97,9 +100,15 @@ void invoke_shouldStoreTheEdrInCache_whenDataAddressIsPresent() { .dataRequest(dataRequest) .build(); + var edrEntry = EndpointDataReferenceEntry.Builder.newInstance() + .agreementId(contractId) + .transferProcessId(transferProcessId) + .assetId(assetId).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(edrCache.queryForEntries(any())).thenReturn(Stream.of(edrEntry)); var event = getTransferProcessStartedEvent(DataAddress.Builder.newInstance().type(EDR_SIMPLE_TYPE).build()); @@ -112,7 +121,8 @@ void invoke_shouldStoreTheEdrInCache_whenDataAddressIsPresent() { 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); } diff --git a/edc-extensions/hashicorp-vault/README.md b/edc-extensions/hashicorp-vault/README.md deleted file mode 100644 index f0e861b16..000000000 --- a/edc-extensions/hashicorp-vault/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# [HashiCorp Vault](https://www.vaultproject.io/) Extension - ---- - -**Please note:** -Using the HashiCorp vault it is possible to define multiple data entries per secret. Other vaults might allow only one -entry per secret (e.g. Azure Key Vault). - -Therefore, the HashiCorp vault extension **only** checks the '**content**' data entry! Please use this knowledge when -creating secrets the EDC should consume. - ---- - -## Configuration - -| Key | Description | Mandatory | Default | -|:--------------------------------------------|:-----------------------------------------------------------------------------------------------------------------|-----------|------------------| -| edc.vault.hashicorp.url | URL to connect to the HashiCorp Vault | X || | -| edc.vault.hashicorp.token | Value for [Token Authentication](https://www.vaultproject.io/docs/auth/token) with the vault | X || | -| edc.vault.hashicorp.timeout.seconds | Request timeout in seconds when contacting the vault | | `30` | -| edc.vault.hashicorp.health.check.enabled | Enable health checks to ensure vault is initialized, unsealed and active | | `true` | -| edc.vault.hashicorp.health.check.standby.ok | Specifies if a vault in standby is healthy. This is useful when Vault is behind a non-configurable load balancer | | `false` | -| edc.vault.hashicorp.api.secret.path | Path to the [secret api](https://www.vaultproject.io/api-docs/secret/kv/kv-v1) | | `/v1/secret` | -| edc.vault.hashicorp.api.health.check.path | Path to the [health api](https://www.vaultproject.io/api-docs/system/health) | | `/v1/sys/health` | - -## Health Check - -The HashiCorp Vault Extension is able to run health checks. A health check is successful when the vault is _initialized_, _active_ and _unsealed_. Successful health checks are logged with level _FINE_. Unsuccessful health checks will be logged -with level _WARNING_. - ---- - -### Health Checks - -If your project uses the Tractus-X HashiCorp Vault please set `edc.vault.hashicorp.health.check.standby.ok` to _true_. Otherwise, the health check would fail if the Vault is in standby. - -```plain -# Logs of successful check with standby vault -[2022-08-01 14:48:37] [FINE ] HashiCorp Vault HealthCheck successful. HashicorpVaultHealthResponsePayload(isInitialized=true, isSealed=false, isStandby=true, isPerformanceStandby=false, replicationPerformanceMode=disabled,replicationDrMode=disabled, serverTimeUtc=1659365317, version=1.9.2, clusterName=vault-cluster-4b193c26, clusterId=83fabd45-685d-7f8d-9495-18fab6f50d5e) -``` - ---- - -## Example: Create & Configure DAPS Key - -### Insert DAPS Key into HashiCorp Vault - -```bash -cat << EOF | /bin/vault kv put secret/my-daps-key content=- - -----BEGIN PRIVATE KEY----- - MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM - wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ - FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 - 8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ - ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE - sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc - RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z - d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm - hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm - cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh - FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F - MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e - uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb - ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 - z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 - h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ - vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB - 8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 - hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 - dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 - Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 - IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT - 3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr - 0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 - u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B - AjWFbUiBCFOo+gpAFcQGrkOQHA== - -----END PRIVATE KEY----- - EOF -``` - -### Configure Key in the EDC - -```bash -EDC_OAUTH_PRIVATE_KEY_ALIAS: my-daps-key -``` - -or - -```bash -edc.oauth.private.key.alias=my-daps-key -``` - -## Example: Argo CD Vault Configuration - -```properties -######### -# Vault # -######### - -edc.vault.hashicorp.url=https://vault.demo.tractus-x.net -# or even better configure token as k8 secret -edc.vault.hashicorp.token= -edc.vault.hashicorp.api.secret.path=/v1// -edc.vault.hashicorp.health.check.standby.ok=true - -######################## -# E.g. OAuth Extension # -######################## - -# from UI: secret stored in https://vault.demo.tractus-x.net/ui/vault/secrets//show/my-daps-key -edc.oauth.private.key.alias=my-daps-key -``` diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpVaultExtension.java deleted file mode 100644 index 237171daa..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpVaultExtension.java +++ /dev/null @@ -1,107 +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.hashicorpvault; - -import com.fasterxml.jackson.databind.ObjectMapper; -import okhttp3.OkHttpClient; -import org.eclipse.edc.spi.system.ServiceExtensionContext; - -import java.time.Duration; - -/** - * Temporary solution as long as the Vault components needs to be loaded as dedicated vault - * extension. Will be changed from EDC milestone 5. - */ -public class AbstractHashicorpVaultExtension { - - public static final String VAULT_URL = "edc.vault.hashicorp.url"; - - public static final String VAULT_TOKEN = "edc.vault.hashicorp.token"; - - public static final String VAULT_API_SECRET_PATH = "edc.vault.hashicorp.api.secret.path"; - - public static final String VAULT_API_SECRET_PATH_DEFAULT = "/v1/secret"; - - public static final String VAULT_API_HEALTH_PATH = "edc.vault.hashicorp.api.health.check.path"; - - public static final String VAULT_API_HEALTH_PATH_DEFAULT = "/v1/sys/health"; - - public static final String VAULT_HEALTH_CHECK_STANDBY_OK = - "edc.vault.hashicorp.health.check.standby.ok"; - - public static final boolean VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT = false; - - private static final String VAULT_TIMEOUT_SECONDS = "edc.vault.hashicorp.timeout.seconds"; - - protected HashicorpVaultClient createVaultClient(ServiceExtensionContext context, ObjectMapper mapper) { - var config = loadHashicorpVaultClientConfig(context); - - var okHttpClient = createOkHttpClient(config); - - return new HashicorpVaultClient(config, okHttpClient, mapper); - } - - protected OkHttpClient createOkHttpClient(HashicorpVaultClientConfig config) { - OkHttpClient.Builder builder = - new OkHttpClient.Builder() - .callTimeout(config.getTimeout()) - .readTimeout(config.getTimeout()); - - return builder.build(); - } - - protected HashicorpVaultClientConfig loadHashicorpVaultClientConfig( - ServiceExtensionContext context) { - - final String vaultUrl = context.getSetting(VAULT_URL, null); - if (vaultUrl == null) { - throw new HashicorpVaultException(String.format("Vault URL (%s) must be defined", VAULT_URL)); - } - - final int vaultTimeoutSeconds = Math.max(0, context.getSetting(VAULT_TIMEOUT_SECONDS, 30)); - final Duration vaultTimeoutDuration = Duration.ofSeconds(vaultTimeoutSeconds); - - final String vaultToken = context.getSetting(VAULT_TOKEN, null); - - if (vaultToken == null) { - throw new HashicorpVaultException( - String.format("For Vault authentication [%s] is required", VAULT_TOKEN)); - } - - final String apiSecretPath = - context.getSetting(VAULT_API_SECRET_PATH, VAULT_API_SECRET_PATH_DEFAULT); - - final String apiHealthPath = - context.getSetting(VAULT_API_HEALTH_PATH, VAULT_API_HEALTH_PATH_DEFAULT); - - final boolean isHealthStandbyOk = - context.getSetting(VAULT_HEALTH_CHECK_STANDBY_OK, VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT); - - return HashicorpVaultClientConfig.Builder.newInstance() - .vaultUrl(vaultUrl) - .vaultToken(vaultToken) - .vaultApiSecretPath(apiSecretPath) - .vaultApiHealthPath(apiHealthPath) - .isVaultApiHealthStandbyOk(isHealthStandbyOk) - .timeout(vaultTimeoutDuration) - .build(); - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolver.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolver.java deleted file mode 100644 index fbbfde119..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolver.java +++ /dev/null @@ -1,64 +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.hashicorpvault; - -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.security.CertificateResolver; -import org.eclipse.edc.spi.security.Vault; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.security.cert.X509Certificate; - -/** - * Resolves an X.509 certificate in Hashicorp vault. - */ -public class HashicorpCertificateResolver implements CertificateResolver { - private final Vault vault; - private final Monitor monitor; - - public HashicorpCertificateResolver(Vault vault, Monitor monitor) { - this.vault = vault; - this.monitor = monitor; - } - - @Override - public X509Certificate resolveCertificate(String id) { - String certificateRepresentation = vault.resolveSecret(id); - if (certificateRepresentation == null) { - return null; - } - try (InputStream inputStream = - new ByteArrayInputStream(certificateRepresentation.getBytes(StandardCharsets.UTF_8))) { - X509Certificate x509Certificate = PemUtil.readX509Certificate(inputStream); - if (x509Certificate == null) { - monitor.warning( - String.format("Expected PEM certificate on key %s, but value not PEM.", id)); - } - return x509Certificate; - } catch (IOException e) { - throw new EdcException(e.getMessage(), e); - } - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVault.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVault.java deleted file mode 100644 index b2ea9a0b1..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVault.java +++ /dev/null @@ -1,59 +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.hashicorpvault; - -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.security.Vault; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Implements a vault backed by Hashicorp Vault. - */ -class HashicorpVault implements Vault { - - private final HashicorpVaultClient hashicorpVaultClient; - - HashicorpVault(HashicorpVaultClient hashicorpVaultClient) { - this.hashicorpVaultClient = hashicorpVaultClient; - } - - @Override - public @Nullable String resolveSecret(String key) { - Result result = hashicorpVaultClient.getSecretValue(key); - - return result.succeeded() ? result.getContent() : null; - } - - @Override - @NotNull - public Result storeSecret(@NotNull String key, @NotNull String value) { - Result result = - hashicorpVaultClient.setSecret(key, value); - - return result.succeeded() ? Result.success() : Result.failure(result.getFailureMessages()); - } - - @Override - public Result deleteSecret(@NotNull String key) { - return hashicorpVaultClient.destroySecret(key); - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClient.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClient.java deleted file mode 100644 index a34cb96f7..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClient.java +++ /dev/null @@ -1,204 +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.hashicorpvault; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import okhttp3.Headers; -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.result.Result; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.Objects; - -public class HashicorpVaultClient { - static final String VAULT_DATA_ENTRY_NAME = "content"; - private static final String VAULT_TOKEN_HEADER = "X-Vault-Token"; - private static final String VAULT_REQUEST_HEADER = "X-Vault-Request"; - private static final String VAULT_SECRET_DATA_PATH = "data"; - private static final String VAULT_SECRET_METADATA_PATH = "metadata"; - private static final MediaType MEDIA_TYPE_APPLICATION_JSON = MediaType.get("application/json"); - private static final String CALL_UNSUCCESSFUL_ERROR_TEMPLATE = "Call unsuccessful: %s"; - - private final HashicorpVaultClientConfig config; - private final OkHttpClient okHttpClient; - private final ObjectMapper objectMapper; - - public HashicorpVaultClient(HashicorpVaultClientConfig config, OkHttpClient okHttpClient, ObjectMapper objectMapper) { - this.config = config; - this.okHttpClient = okHttpClient; - this.objectMapper = objectMapper; - } - - Result getSecretValue(String key) { - var requestUri = getSecretUrl(key, VAULT_SECRET_DATA_PATH); - var headers = getHeaders(); - var request = new Request.Builder().url(requestUri).headers(headers).get().build(); - - try (var response = okHttpClient.newCall(request).execute()) { - - if (response.code() == 404) { - return Result.failure(String.format(CALL_UNSUCCESSFUL_ERROR_TEMPLATE, "Secret not found")); - } - - if (response.isSuccessful()) { - var responseBody = Objects.requireNonNull(response.body()).string(); - var payload = objectMapper.readValue(responseBody, HashicorpVaultGetEntryResponsePayload.class); - var value = Objects.requireNonNull(payload.getData().getData().get(VAULT_DATA_ENTRY_NAME)); - - return Result.success(value); - } else { - return Result.failure(String.format(CALL_UNSUCCESSFUL_ERROR_TEMPLATE, response.code())); - } - - } catch (IOException e) { - return Result.failure(e.getMessage()); - } - } - - public HashicorpVaultHealthResponse getHealth() { - - var healthResponseBuilder = HashicorpVaultHealthResponse.Builder.newInstance(); - - var requestUri = getHealthUrl(); - var headers = getHeaders(); - var request = new Request.Builder().url(requestUri).headers(headers).get().build(); - try (var response = okHttpClient.newCall(request).execute()) { - final var code = response.code(); - healthResponseBuilder.code(code); - - try { - var responseBody = Objects.requireNonNull(response.body()).string(); - var responsePayload = objectMapper.readValue(responseBody, HashicorpVaultHealthResponsePayload.class); - healthResponseBuilder.payload(responsePayload); - } catch (JsonMappingException e) { - // ignore. status code not checked, so it may be possible that no payload was - // provided - } - } catch (IOException e) { - throw new EdcException(e); - } - - return healthResponseBuilder.build(); - } - - Result setSecret( - String key, String value) { - var requestUri = getSecretUrl(key, VAULT_SECRET_DATA_PATH); - var headers = getHeaders(); - var requestPayload = - HashicorpVaultCreateEntryRequestPayload.Builder.newInstance() - .data(Collections.singletonMap(VAULT_DATA_ENTRY_NAME, value)) - .build(); - var request = new Request.Builder() - .url(requestUri) - .headers(headers) - .post(createRequestBody(requestPayload)) - .build(); - - try (var response = okHttpClient.newCall(request).execute()) { - if (response.isSuccessful()) { - var responseBody = Objects.requireNonNull(response.body()).string(); - var responsePayload = - objectMapper.readValue(responseBody, HashicorpVaultCreateEntryResponsePayload.class); - return Result.success(responsePayload); - } else { - return Result.failure(String.format(CALL_UNSUCCESSFUL_ERROR_TEMPLATE, response.code())); - } - } catch (IOException e) { - return Result.failure(e.getMessage()); - } - } - - Result destroySecret(String key) { - var requestUri = getSecretUrl(key, VAULT_SECRET_METADATA_PATH); - var headers = getHeaders(); - var request = new Request.Builder().url(requestUri).headers(headers).delete().build(); - - try (var response = okHttpClient.newCall(request).execute()) { - return response.isSuccessful() || response.code() == 404 - ? Result.success() - : Result.failure(String.format(CALL_UNSUCCESSFUL_ERROR_TEMPLATE, response.code())); - } catch (IOException e) { - return Result.failure(e.getMessage()); - } - } - - @NotNull - private Headers getHeaders() { - return new Headers.Builder() - .add(VAULT_REQUEST_HEADER, Boolean.toString(true)) - .add(VAULT_TOKEN_HEADER, config.getVaultToken()) - .build(); - } - - private HttpUrl getSecretUrl(String key, String entryType) { - key = URLEncoder.encode(key, StandardCharsets.UTF_8); - - // restore '/' characters to allow sub-directories - key = key.replace("%2F", "/"); - - final var vaultApiPath = config.getVaultApiSecretPath(); - - return Objects.requireNonNull(HttpUrl.parse(config.getVaultUrl())) - .newBuilder() - .addPathSegments(PathUtil.trimLeadingOrEndingSlash(vaultApiPath)) - .addPathSegment(entryType) - .addPathSegments(key) - .build(); - } - - private HttpUrl getHealthUrl() { - final var vaultHealthPath = config.getVaultApiHealthPath(); - final var isVaultHealthStandbyOk = config.isVaultApiHealthStandbyOk(); - - // by setting 'standbyok' and/or 'perfstandbyok' the vault will return an active - // status - // code instead of the standby status codes - - return Objects.requireNonNull(HttpUrl.parse(config.getVaultUrl())) - .newBuilder() - .addPathSegments(PathUtil.trimLeadingOrEndingSlash(vaultHealthPath)) - .addQueryParameter("standbyok", isVaultHealthStandbyOk ? "true" : "false") - .addQueryParameter("perfstandbyok", isVaultHealthStandbyOk ? "true" : "false") - .build(); - } - - private RequestBody createRequestBody(Object requestPayload) { - String jsonRepresentation; - try { - jsonRepresentation = objectMapper.writeValueAsString(requestPayload); - } catch (JsonProcessingException e) { - throw new HashicorpVaultException(e.getMessage(), e); - } - return RequestBody.create(jsonRepresentation, MEDIA_TYPE_APPLICATION_JSON); - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientConfig.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientConfig.java deleted file mode 100644 index 684351252..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientConfig.java +++ /dev/null @@ -1,103 +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.hashicorpvault; - - -import java.time.Duration; - -class HashicorpVaultClientConfig { - private String vaultUrl; - private String vaultToken; - private String vaultApiSecretPath; - private String vaultApiHealthPath; - private Duration timeout; - private boolean isVaultApiHealthStandbyOk; - - public String getVaultUrl() { - return vaultUrl; - } - - public String getVaultToken() { - return vaultToken; - } - - public String getVaultApiSecretPath() { - return vaultApiSecretPath; - } - - public String getVaultApiHealthPath() { - return vaultApiHealthPath; - } - - public Duration getTimeout() { - return timeout; - } - - public boolean isVaultApiHealthStandbyOk() { - return isVaultApiHealthStandbyOk; - } - - public static final class Builder { - private final HashicorpVaultClientConfig config; - - private Builder() { - config = new HashicorpVaultClientConfig(); - } - - public static Builder newInstance() { - return new Builder(); - } - - public Builder vaultUrl(String vaultUrl) { - this.config.vaultUrl = vaultUrl; - return this; - } - - public Builder vaultToken(String vaultToken) { - this.config.vaultToken = vaultToken; - return this; - } - - public Builder vaultApiSecretPath(String vaultApiSecretPath) { - this.config.vaultApiSecretPath = vaultApiSecretPath; - return this; - } - - public Builder vaultApiHealthPath(String vaultApiHealthPath) { - this.config.vaultApiHealthPath = vaultApiHealthPath; - return this; - } - - public Builder timeout(Duration timeout) { - this.config.timeout = timeout; - return this; - } - - public Builder isVaultApiHealthStandbyOk(boolean isVaultApiHealthStandbyOk) { - this.config.isVaultApiHealthStandbyOk = isVaultApiHealthStandbyOk; - return this; - } - - public HashicorpVaultClientConfig build() { - return this.config; - } - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java deleted file mode 100644 index 65d472f97..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java +++ /dev/null @@ -1,85 +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.hashicorpvault; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Map; - -@JsonIgnoreProperties(ignoreUnknown = true) -class HashicorpVaultCreateEntryRequestPayload { - - @JsonProperty("options") - private Options options; - - @JsonProperty("data") - private Map data; - - private HashicorpVaultCreateEntryRequestPayload() { - } - - public Options getOptions() { - return options; - } - - public Map getData() { - return data; - } - - - @JsonIgnoreProperties(ignoreUnknown = true) - static class Options { - @JsonProperty("cas") - private Integer cas; - - public Integer getCas() { - return cas; - } - } - - public static final class Builder { - - private final HashicorpVaultCreateEntryRequestPayload payload; - - private Builder() { - payload = new HashicorpVaultCreateEntryRequestPayload(); - } - - public static Builder newInstance() { - return new Builder(); - } - - public Builder options(Options options) { - this.payload.options = options; - return this; - } - - public Builder data(Map data) { - this.payload.data = data; - return this; - } - - public HashicorpVaultCreateEntryRequestPayload build() { - return payload; - } - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java deleted file mode 100644 index d75a19355..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java +++ /dev/null @@ -1,35 +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.hashicorpvault; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonIgnoreProperties(ignoreUnknown = true) -class HashicorpVaultCreateEntryResponsePayload { - - @JsonProperty("data") - private HashicorpVaultEntryMetadata data; - - public HashicorpVaultEntryMetadata getData() { - return data; - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultEntryMetadata.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultEntryMetadata.java deleted file mode 100644 index 4f92bca85..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultEntryMetadata.java +++ /dev/null @@ -1,51 +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.hashicorpvault; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Map; - -@JsonIgnoreProperties(ignoreUnknown = true) -class HashicorpVaultEntryMetadata { - - @JsonProperty("custom_metadata") - private Map customMetadata; - - @JsonProperty("destroyed") - private Boolean destroyed; - - @JsonProperty("version") - private Integer version; - - public Map getCustomMetadata() { - return customMetadata; - } - - public Boolean getDestroyed() { - return destroyed; - } - - public Integer getVersion() { - return version; - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultException.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultException.java deleted file mode 100644 index 8488cfa34..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultException.java +++ /dev/null @@ -1,34 +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.hashicorpvault; - -import org.eclipse.edc.spi.EdcException; - -public class HashicorpVaultException extends EdcException { - - public HashicorpVaultException(String message) { - super(message); - } - - public HashicorpVaultException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java deleted file mode 100644 index 6e7c2763f..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java +++ /dev/null @@ -1,55 +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.hashicorpvault; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Map; - -@JsonIgnoreProperties(ignoreUnknown = true) -class HashicorpVaultGetEntryResponsePayload { - - @JsonProperty("data") - private GetVaultEntryData data; - - public GetVaultEntryData getData() { - return data; - } - - @JsonIgnoreProperties(ignoreUnknown = true) - static class GetVaultEntryData { - - @JsonProperty("data") - private Map data; - - @JsonProperty("metadata") - private HashicorpVaultEntryMetadata metadata; - - public Map getData() { - return data; - } - - public HashicorpVaultEntryMetadata getMetadata() { - return metadata; - } - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheck.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheck.java deleted file mode 100644 index 00cf14b7d..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheck.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * - * This program and the accompanying materials are 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: - * Mercedes-Benz Tech Innovation GmbH - Add vault health check - * - */ - -package org.eclipse.tractusx.edc.hashicorpvault; - -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.health.HealthCheckResult; -import org.eclipse.edc.spi.system.health.LivenessProvider; -import org.eclipse.edc.spi.system.health.ReadinessProvider; -import org.eclipse.edc.spi.system.health.StartupStatusProvider; - -import static java.lang.String.format; - -public class HashicorpVaultHealthCheck implements ReadinessProvider, LivenessProvider, StartupStatusProvider { - - private static final String HEALTH_CHECK_ERROR_TEMPLATE = - "HashiCorp Vault HealthCheck unsuccessful. %s %s"; - - private final HashicorpVaultClient client; - private final Monitor monitor; - - public HashicorpVaultHealthCheck(HashicorpVaultClient client, Monitor monitor) { - this.client = client; - this.monitor = monitor; - } - - @Override - public HealthCheckResult get() { - - HashicorpVaultHealthResponse response; - try { - response = client.getHealth(); - } catch (EdcException e) { // can be thrown by the client, e.g. on JSON parsing error, etc. - var exceptionMsg = format(HEALTH_CHECK_ERROR_TEMPLATE, "EdcException: " + e.getMessage(), ""); - monitor.severe(exceptionMsg, e); - return HealthCheckResult.failed(exceptionMsg); - } - - switch (response.getCodeAsEnum()) { - case INITIALIZED_UNSEALED_AND_ACTIVE: - monitor.debug("HashiCorp Vault HealthCheck successful. " + response.getPayload()); - return HealthCheckResult.success(); - case UNSEALED_AND_STANDBY: - final String standbyMsg = - format( - HEALTH_CHECK_ERROR_TEMPLATE, "Vault is in standby", response.getPayload()); - monitor.warning(standbyMsg); - return HealthCheckResult.failed(standbyMsg); - case DISASTER_RECOVERY_MODE_REPLICATION_SECONDARY_AND_ACTIVE: - final String recoveryModeMsg = - format( - HEALTH_CHECK_ERROR_TEMPLATE, "Vault is in recovery mode", response.getPayload()); - monitor.warning(recoveryModeMsg); - return HealthCheckResult.failed(recoveryModeMsg); - case PERFORMANCE_STANDBY: - final String performanceStandbyMsg = - format( - HEALTH_CHECK_ERROR_TEMPLATE, - "Vault is in performance standby", - response.getPayload()); - monitor.warning(performanceStandbyMsg); - return HealthCheckResult.failed(performanceStandbyMsg); - case NOT_INITIALIZED: - final String notInitializedMsg = - format( - HEALTH_CHECK_ERROR_TEMPLATE, "Vault is not initialized", response.getPayload()); - monitor.warning(notInitializedMsg); - return HealthCheckResult.failed(notInitializedMsg); - case SEALED: - final String sealedMsg = - format(HEALTH_CHECK_ERROR_TEMPLATE, "Vault is sealed", response.getPayload()); - monitor.warning(sealedMsg); - return HealthCheckResult.failed(sealedMsg); - case UNSPECIFIED: - default: - final String unspecifiedMsg = - format( - HEALTH_CHECK_ERROR_TEMPLATE, - "Unspecified response from vault. Code: " + response.getCode(), - response.getPayload()); - monitor.warning(unspecifiedMsg); - return HealthCheckResult.failed(unspecifiedMsg); - } - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthExtension.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthExtension.java deleted file mode 100644 index 8bbf4634f..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthExtension.java +++ /dev/null @@ -1,71 +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.hashicorpvault; - -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Requires; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.health.HealthCheckService; -import org.eclipse.edc.spi.types.TypeManager; - -@Requires(HealthCheckService.class) -public class HashicorpVaultHealthExtension extends AbstractHashicorpVaultExtension - implements ServiceExtension { - - public static final String VAULT_HEALTH_CHECK = "edc.vault.hashicorp.health.check.enabled"; - - public static final boolean VAULT_HEALTH_CHECK_DEFAULT = true; - - @Inject - private HealthCheckService healthCheckService; - - @Inject - private TypeManager typeManager; - - @Override - public String name() { - return "Hashicorp Vault Health Check"; - } - - - @Override - public void initialize(ServiceExtensionContext context) { - var client = createVaultClient(context, typeManager.getMapper()); - - configureHealthCheck(client, context); - - context.getMonitor().info("HashicorpVaultExtension: health check initialization complete."); - } - - private void configureHealthCheck(HashicorpVaultClient client, ServiceExtensionContext context) { - var healthCheckEnabled = - context.getSetting(VAULT_HEALTH_CHECK, VAULT_HEALTH_CHECK_DEFAULT); - if (!healthCheckEnabled) return; - - var healthCheck = - new HashicorpVaultHealthCheck(client, context.getMonitor()); - - healthCheckService.addLivenessProvider(healthCheck); - healthCheckService.addReadinessProvider(healthCheck); - healthCheckService.addStartupStatusProvider(healthCheck); - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponse.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponse.java deleted file mode 100644 index 5e48c8078..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponse.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * - * This program and the accompanying materials are 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: - * Mercedes-Benz Tech Innovation GmbH - Add vault health check - * - */ - -package org.eclipse.tractusx.edc.hashicorpvault; - -public class HashicorpVaultHealthResponse { - - private HashicorpVaultHealthResponsePayload payload; - private int code; - - private HashicorpVaultHealthResponse() { - } - - public int getCode() { - return code; - } - - public HashiCorpVaultHealthResponseCode getCodeAsEnum() { - switch (code) { - case 200: - return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode - .INITIALIZED_UNSEALED_AND_ACTIVE; - case 429: - return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode.UNSEALED_AND_STANDBY; - case 472: - return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode - .DISASTER_RECOVERY_MODE_REPLICATION_SECONDARY_AND_ACTIVE; - case 473: - return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode.PERFORMANCE_STANDBY; - case 501: - return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode.NOT_INITIALIZED; - case 503: - return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode.SEALED; - default: - return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode.UNSPECIFIED; - } - } - - public HashicorpVaultHealthResponsePayload getPayload() { - return payload; - } - - - public enum HashiCorpVaultHealthResponseCode { - UNSPECIFIED, // undefined status codes - INITIALIZED_UNSEALED_AND_ACTIVE, // status code 200 - UNSEALED_AND_STANDBY, // status code 429 - DISASTER_RECOVERY_MODE_REPLICATION_SECONDARY_AND_ACTIVE, // status code 472 - PERFORMANCE_STANDBY, // status code 473 - NOT_INITIALIZED, // status code 501 - SEALED // status code 503 - } - - public static final class Builder { - - private final HashicorpVaultHealthResponse response; - - private Builder() { - response = new HashicorpVaultHealthResponse(); - } - - public static Builder newInstance() { - return new Builder(); - } - - public Builder payload(HashicorpVaultHealthResponsePayload payload) { - this.response.payload = payload; - return this; - } - - public Builder code(int code) { - this.response.code = code; - return this; - } - - public HashicorpVaultHealthResponse build() { - return response; - } - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java deleted file mode 100644 index fd613a0a0..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * - * This program and the accompanying materials are 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: - * Mercedes-Benz Tech Innovation GmbH - Add vault health check - * - */ - -package org.eclipse.tractusx.edc.hashicorpvault; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonIgnoreProperties(ignoreUnknown = true) -public class HashicorpVaultHealthResponsePayload { - @JsonProperty("initialized") - private boolean isInitialized; - - @JsonProperty("sealed") - private boolean isSealed; - - @JsonProperty("standby") - private boolean isStandby; - - @JsonProperty("performance_standby") - private boolean isPerformanceStandby; - - @JsonProperty("replication_performance_mode") - private String replicationPerformanceMode; - - @JsonProperty("replication_dr_mode") - private String replicationDrMode; - - @JsonProperty("server_time_utc") - private long serverTimeUtc; - - @JsonProperty("version") - private String version; - - @JsonProperty("cluster_name") - private String clusterName; - - @JsonProperty("cluster_id") - private String clusterId; - - public boolean isInitialized() { - return isInitialized; - } - - public boolean isSealed() { - return isSealed; - } - - public boolean isStandby() { - return isStandby; - } - - public boolean isPerformanceStandby() { - return isPerformanceStandby; - } - - public String getReplicationPerformanceMode() { - return replicationPerformanceMode; - } - - public String getReplicationDrMode() { - return replicationDrMode; - } - - public long getServerTimeUtc() { - return serverTimeUtc; - } - - public String getVersion() { - return version; - } - - public String getClusterName() { - return clusterName; - } - - public String getClusterId() { - return clusterId; - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultVaultExtension.java deleted file mode 100644 index 3a979f639..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultVaultExtension.java +++ /dev/null @@ -1,60 +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.hashicorpvault; - -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provides; -import org.eclipse.edc.spi.security.CertificateResolver; -import org.eclipse.edc.spi.security.PrivateKeyResolver; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.spi.security.VaultPrivateKeyResolver; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.TypeManager; - -@Provides({Vault.class, CertificateResolver.class, PrivateKeyResolver.class}) -public class HashicorpVaultVaultExtension extends AbstractHashicorpVaultExtension - implements ServiceExtension { - - @Inject - private TypeManager typeManager; - - @Override - public String name() { - return "Hashicorp Vault"; - } - - @Override - public void initialize(ServiceExtensionContext context) { - var client = createVaultClient(context, typeManager.getMapper()); - - var vault = new HashicorpVault(client); - var certificateResolver = - new HashicorpCertificateResolver(vault, context.getMonitor()); - var privateKeyResolver = new VaultPrivateKeyResolver(vault); - - context.registerService(Vault.class, vault); - context.registerService(CertificateResolver.class, certificateResolver); - context.registerService(PrivateKeyResolver.class, privateKeyResolver); - - context.getMonitor().info("HashicorpVaultExtension: authentication/initialization complete."); - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtil.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtil.java deleted file mode 100644 index 1652ab758..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtil.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * - * This program and the accompanying materials are 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: - * Mercedes-Benz Tech Innovation GmbH - Make secret data & metadata paths configurable - * - */ - -package org.eclipse.tractusx.edc.hashicorpvault; - -final class PathUtil { - - private PathUtil() { - } - - static String trimLeadingOrEndingSlash(String path) { - var fixedPath = path; - - if (fixedPath.startsWith("/")) fixedPath = fixedPath.substring(1); - if (fixedPath.endsWith("/")) fixedPath = fixedPath.substring(0, fixedPath.length() - 1); - - return fixedPath; - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PemUtil.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PemUtil.java deleted file mode 100644 index 0bd902b75..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PemUtil.java +++ /dev/null @@ -1,67 +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.hashicorpvault; - -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMParser; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.security.Provider; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -final class PemUtil { - - private static final Provider PROVIDER = new BouncyCastleProvider(); - private static final JcaX509CertificateConverter X509_CONVERTER = - new JcaX509CertificateConverter().setProvider(PROVIDER); - - private PemUtil() { - throw new IllegalStateException("Private constructor invocation disallowed"); - } - - public static X509Certificate readX509Certificate(@NotNull InputStream inputStream) { - try { - X509CertificateHolder x509CertificateHolder = parsePem(inputStream); - if (x509CertificateHolder == null) { - return null; - } - return X509_CONVERTER.getCertificate(x509CertificateHolder); - } catch (IOException | CertificateException e) { - throw new RuntimeException(e); - } - - } - - @SuppressWarnings("unchecked") - private static T parsePem(@NotNull InputStream inputStream) throws IOException { - try (Reader reader = new InputStreamReader(inputStream)) { - PEMParser pemParser = new PEMParser(reader); - return (T) pemParser.readObject(); - } - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpIt.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpIt.java deleted file mode 100644 index 09108c77b..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpIt.java +++ /dev/null @@ -1,171 +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.hashicorpvault; - -import org.eclipse.edc.junit.annotations.ComponentTest; -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.eclipse.edc.spi.security.CertificateResolver; -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.health.HealthCheckResult; -import org.eclipse.edc.spi.system.health.HealthCheckService; -import org.eclipse.edc.spi.system.health.HealthStatus; -import org.eclipse.edc.spi.system.health.LivenessProvider; -import org.eclipse.edc.spi.system.health.ReadinessProvider; -import org.eclipse.edc.spi.system.health.StartupStatusProvider; -import org.junit.ClassRule; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; -import org.testcontainers.vault.VaultContainer; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.stream.Collectors; - -import static org.eclipse.tractusx.edc.hashicorpvault.HashicorpVaultClient.VAULT_DATA_ENTRY_NAME; -import static org.eclipse.tractusx.edc.hashicorpvault.HashicorpVaultVaultExtension.VAULT_TOKEN; -import static org.eclipse.tractusx.edc.hashicorpvault.HashicorpVaultVaultExtension.VAULT_URL; - -@ComponentTest -@Testcontainers -@ExtendWith(EdcExtension.class) -class AbstractHashicorpIt { - static final String DOCKER_IMAGE_NAME = "vault:1.9.6"; - static final String VAULT_ENTRY_KEY = "testing"; - static final String VAULT_ENTRY_VALUE = UUID.randomUUID().toString(); - static final String TOKEN = UUID.randomUUID().toString(); - @Container - @ClassRule - private static final VaultContainer VAULTCONTAINER = new VaultContainer<>(DockerImageName.parse(DOCKER_IMAGE_NAME)) - .withVaultToken(TOKEN) - .withSecretInVault( - "secret/" + VAULT_ENTRY_KEY, - String.format("%s=%s", VAULT_DATA_ENTRY_NAME, VAULT_ENTRY_VALUE)); - private final TestExtension testExtension = new TestExtension(); - - protected Vault getVault() { - return testExtension.getVault(); - } - - protected CertificateResolver getCertificateResolver() { - return testExtension.getCertificateResolver(); - } - - @BeforeEach - final void beforeEach(EdcExtension extension) { - extension.setConfiguration(getConfig()); - extension.registerServiceMock(HealthCheckService.class, new MyHealthCheckService()); - extension.registerSystemExtension(ServiceExtension.class, testExtension); - } - - protected Map getConfig() { - return new HashMap<>() { - { - put( - VAULT_URL, - String.format( - "http://%s:%s", VAULTCONTAINER.getHost(), VAULTCONTAINER.getFirstMappedPort())); - put(VAULT_TOKEN, TOKEN); - } - }; - } - - private static class TestExtension implements ServiceExtension { - private Vault vault; - private CertificateResolver certificateResolver; - - @Override - public void initialize(ServiceExtensionContext context) { - vault = context.getService(Vault.class); - certificateResolver = context.getService(CertificateResolver.class); - } - - public CertificateResolver getCertificateResolver() { - return certificateResolver; - } - - public Vault getVault() { - return vault; - } - } - - private static class MyHealthCheckService implements HealthCheckService { - private final List livenessProviders = new ArrayList<>(); - private final List readinessProviders = new ArrayList<>(); - private final List startupStatusProviders = new ArrayList<>(); - - @Override - public void addLivenessProvider(LivenessProvider provider) { - livenessProviders.add(provider); - } - - @Override - public void addReadinessProvider(ReadinessProvider provider) { - readinessProviders.add(provider); - } - - @Override - public void addStartupStatusProvider(StartupStatusProvider provider) { - startupStatusProviders.add(provider); - } - - @Override - public HealthStatus isLive() { - return new HealthStatus( - livenessProviders.stream() - .map( - p -> - p.get().failed() ? HealthCheckResult.failed("") : HealthCheckResult.success()) - .collect(Collectors.toList())); - } - - @Override - public HealthStatus isReady() { - return new HealthStatus( - readinessProviders.stream() - .map( - p -> - p.get().failed() ? HealthCheckResult.failed("") : HealthCheckResult.success()) - .collect(Collectors.toList())); - } - - @Override - public HealthStatus getStartupStatus() { - return new HealthStatus( - startupStatusProviders.stream() - .map( - p -> - p.get().failed() ? HealthCheckResult.failed("") : HealthCheckResult.success()) - .collect(Collectors.toList())); - } - - @Override - public void refresh() { - } - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverIntegrationTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverIntegrationTest.java deleted file mode 100644 index 6476dd2d2..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverIntegrationTest.java +++ /dev/null @@ -1,62 +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.hashicorpvault; - -import org.bouncycastle.operator.OperatorCreationException; -import org.eclipse.edc.spi.security.CertificateResolver; -import org.eclipse.edc.spi.security.Vault; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.UUID; - -class HashicorpCertificateResolverIntegrationTest extends AbstractHashicorpIt { - - @Test - void resolveCertificate_success() throws CertificateException, IOException, NoSuchAlgorithmException, OperatorCreationException { - String key = UUID.randomUUID().toString(); - X509Certificate certificateExpected = X509CertificateTestUtil.generateCertificate(5, "Test"); - String pem = X509CertificateTestUtil.convertToPem(certificateExpected); - - Vault vault = getVault(); - vault.storeSecret(key, pem); - CertificateResolver resolver = getCertificateResolver(); - X509Certificate certificateResult = resolver.resolveCertificate(key); - - Assertions.assertEquals(certificateExpected, certificateResult); - } - - @Test - void resolveCertificate_malformed() { - String key = UUID.randomUUID().toString(); - String value = UUID.randomUUID().toString(); - Vault vault = getVault(); - vault.storeSecret(key, value); - - CertificateResolver resolver = getCertificateResolver(); - X509Certificate certificateResult = resolver.resolveCertificate(key); - Assertions.assertNull(certificateResult); - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverTest.java deleted file mode 100644 index 2e2f4350d..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverTest.java +++ /dev/null @@ -1,74 +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.hashicorpvault; - -import org.bouncycastle.operator.OperatorCreationException; -import org.eclipse.edc.spi.monitor.Monitor; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -class HashicorpCertificateResolverTest { - private static final String KEY = "key"; - - // mocks - private HashicorpCertificateResolver certificateResolver; - private HashicorpVault vault; - - @BeforeEach - void setup() { - vault = Mockito.mock(HashicorpVault.class); - final Monitor monitor = Mockito.mock(Monitor.class); - certificateResolver = new HashicorpCertificateResolver(vault, monitor); - } - - @Test - void resolveCertificate() throws CertificateException, IOException, NoSuchAlgorithmException, OperatorCreationException { - // prepare - X509Certificate certificateExpected = X509CertificateTestUtil.generateCertificate(5, "Test"); - String pem = X509CertificateTestUtil.convertToPem(certificateExpected); - Mockito.when(vault.resolveSecret(KEY)).thenReturn(pem); - - // invoke - certificateResolver.resolveCertificate(KEY); - - // verify - Mockito.verify(vault, Mockito.times(1)).resolveSecret(KEY); - } - - @Test - void nullIfVaultEmpty() { - // prepare - Mockito.when(vault.resolveSecret(KEY)).thenReturn(null); - - // invoke - final X509Certificate certificate = certificateResolver.resolveCertificate(KEY); - - // verify - Assertions.assertNull(certificate); - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientTest.java deleted file mode 100644 index c8c442a77..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientTest.java +++ /dev/null @@ -1,255 +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.hashicorpvault; - -import com.fasterxml.jackson.databind.ObjectMapper; -import okhttp3.Call; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.ResponseBody; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.time.Duration; -import java.util.UUID; - -import static org.mockito.Mockito.any; -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 HashicorpVaultClientTest { - private static final String KEY = "key"; - private static final String CUSTOM_SECRET_PATH = "v1/test/secret"; - private static final String HEALTH_PATH = "sys/health"; - private static final Duration TIMEOUT = Duration.ofSeconds(30); - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - @Test - void getSecretValue() throws IOException { - // prepare - var vaultUrl = "https://mock.url"; - var vaultToken = UUID.randomUUID().toString(); - HashicorpVaultClientConfig hashicorpVaultClientConfig = - HashicorpVaultClientConfig.Builder.newInstance() - .vaultUrl(vaultUrl) - .vaultApiSecretPath(CUSTOM_SECRET_PATH) - .vaultApiHealthPath(HEALTH_PATH) - .isVaultApiHealthStandbyOk(false) - .vaultToken(vaultToken) - .timeout(TIMEOUT) - .build(); - - var okHttpClient = mock(OkHttpClient.class); - var vaultClient = - new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, OBJECT_MAPPER); - var call = mock(Call.class); - var response = mock(Response.class); - var body = mock(ResponseBody.class); - var payload = new HashicorpVaultGetEntryResponsePayload(); - - when(okHttpClient.newCall(any(Request.class))).thenReturn(call); - when(call.execute()).thenReturn(response); - when(response.code()).thenReturn(200); - when(response.body()).thenReturn(body); - when(body.string()).thenReturn(payload.toString()); - - // invoke - var result = vaultClient.getSecretValue(KEY); - - // verify - Assertions.assertNotNull(result); - verify(okHttpClient, times(1)) - .newCall(argThat(request -> request.method().equalsIgnoreCase("GET") && - request.url().encodedPath().contains(CUSTOM_SECRET_PATH + "/data") && - request.url().encodedPathSegments().contains(KEY))); - } - - @Test - void setSecretValue() throws IOException { - // prepare - var vaultUrl = "https://mock.url"; - var vaultToken = UUID.randomUUID().toString(); - var secretValue = UUID.randomUUID().toString(); - HashicorpVaultClientConfig hashicorpVaultClientConfig = - HashicorpVaultClientConfig.Builder.newInstance() - .vaultUrl(vaultUrl) - .vaultApiSecretPath(CUSTOM_SECRET_PATH) - .vaultApiHealthPath(HEALTH_PATH) - .isVaultApiHealthStandbyOk(false) - .vaultToken(vaultToken) - .timeout(TIMEOUT) - .build(); - - var okHttpClient = mock(OkHttpClient.class); - var vaultClient = - new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, OBJECT_MAPPER); - var payload = - new HashicorpVaultCreateEntryResponsePayload(); - - var call = mock(Call.class); - var response = mock(Response.class); - var body = mock(ResponseBody.class); - - when(okHttpClient.newCall(any(Request.class))).thenReturn(call); - when(call.execute()).thenReturn(response); - when(response.code()).thenReturn(200); - when(response.body()).thenReturn(body); - when(body.string()).thenReturn(payload.toString()); - - // invoke - var result = - vaultClient.setSecret(KEY, secretValue); - - // verify - Assertions.assertNotNull(result); - verify(okHttpClient, times(1)) - .newCall( - argThat( - request -> - request.method().equalsIgnoreCase("POST") && - request.url().encodedPath().contains(CUSTOM_SECRET_PATH + "/data") && - request.url().encodedPathSegments().contains(KEY))); - } - - @Test - void getHealth() throws IOException { - // prepare - var vaultUrl = "https://mock.url"; - var vaultToken = UUID.randomUUID().toString(); - var secretValue = UUID.randomUUID().toString(); - HashicorpVaultClientConfig hashicorpVaultClientConfig = - HashicorpVaultClientConfig.Builder.newInstance() - .vaultUrl(vaultUrl) - .vaultApiSecretPath(CUSTOM_SECRET_PATH) - .vaultApiHealthPath(HEALTH_PATH) - .isVaultApiHealthStandbyOk(false) - .vaultToken(vaultToken) - .timeout(TIMEOUT) - .build(); - - var okHttpClient = mock(OkHttpClient.class); - var vaultClient = - new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, OBJECT_MAPPER); - var payload = new HashicorpVaultHealthResponsePayload(); - - var call = mock(Call.class); - var response = mock(Response.class); - var body = mock(ResponseBody.class); - - when(okHttpClient.newCall(any(Request.class))).thenReturn(call); - when(call.execute()).thenReturn(response); - when(response.code()).thenReturn(200); - when(response.body()).thenReturn(body); - when(body.string()) - .thenReturn( - "{ " + - "\"initialized\": true, " + - "\"sealed\": false," + - "\"standby\": false," + - "\"performance_standby\": false," + - "\"replication_performance_mode\": \"mode\"," + - "\"replication_dr_mode\": \"mode\"," + - "\"server_time_utc\": 100," + - "\"version\": \"1.0.0\"," + - "\"cluster_name\": \"name\"," + - "\"cluster_id\": \"id\" " + - " }"); - - // invoke - var result = vaultClient.getHealth(); - - // verify - Assertions.assertNotNull(result); - verify(okHttpClient, times(1)) - .newCall( - argThat( - request -> - request.method().equalsIgnoreCase("GET") && - request.url().encodedPath().contains(HEALTH_PATH) && - request.url().queryParameter("standbyok").equals("false") && - request.url().queryParameter("perfstandbyok").equals("false"))); - Assertions.assertEquals(200, result.getCode()); - Assertions.assertEquals( - HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode - .INITIALIZED_UNSEALED_AND_ACTIVE, - result.getCodeAsEnum()); - - HashicorpVaultHealthResponsePayload resultPayload = result.getPayload(); - - Assertions.assertNotNull(resultPayload); - Assertions.assertTrue(resultPayload.isInitialized()); - Assertions.assertFalse(resultPayload.isSealed()); - Assertions.assertFalse(resultPayload.isStandby()); - Assertions.assertFalse(resultPayload.isPerformanceStandby()); - Assertions.assertEquals("mode", resultPayload.getReplicationPerformanceMode()); - Assertions.assertEquals("mode", resultPayload.getReplicationDrMode()); - Assertions.assertEquals(100, resultPayload.getServerTimeUtc()); - Assertions.assertEquals("1.0.0", resultPayload.getVersion()); - Assertions.assertEquals("id", resultPayload.getClusterId()); - Assertions.assertEquals("name", resultPayload.getClusterName()); - } - - @Test - void destroySecretValue() throws IOException { - // prepare - var vaultUrl = "https://mock.url"; - var vaultToken = UUID.randomUUID().toString(); - HashicorpVaultClientConfig hashicorpVaultClientConfig = - HashicorpVaultClientConfig.Builder.newInstance() - .vaultUrl(vaultUrl) - .vaultApiSecretPath(CUSTOM_SECRET_PATH) - .vaultApiHealthPath(HEALTH_PATH) - .isVaultApiHealthStandbyOk(false) - .vaultToken(vaultToken) - .timeout(TIMEOUT) - .build(); - - var okHttpClient = mock(OkHttpClient.class); - var vaultClient = - new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, OBJECT_MAPPER); - - var call = mock(Call.class); - var response = mock(Response.class); - var body = mock(ResponseBody.class); - when(okHttpClient.newCall(any(Request.class))).thenReturn(call); - when(call.execute()).thenReturn(response); - when(response.code()).thenReturn(200); - when(response.body()).thenReturn(body); - - // invoke - var result = vaultClient.destroySecret(KEY); - - // verify - Assertions.assertNotNull(result); - verify(okHttpClient, times(1)) - .newCall( - argThat( - request -> - request.method().equalsIgnoreCase("DELETE") && - request.url().encodedPath().contains(CUSTOM_SECRET_PATH + "/metadata") && - request.url().encodedPathSegments().contains(KEY))); - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultExtensionTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultExtensionTest.java deleted file mode 100644 index 7e355d48f..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultExtensionTest.java +++ /dev/null @@ -1,73 +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.hashicorpvault; - -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.health.HealthCheckService; -import org.eclipse.edc.spi.system.injection.ObjectFactory; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -@ExtendWith(DependencyInjectionExtension.class) -class HashicorpVaultExtensionTest { - - private static final String VAULT_URL = "https://example.com"; - private static final String VAULT_TOKEN = "token"; - - private HashicorpVaultVaultExtension extension; - - // mocks - private ServiceExtensionContext context; - private Monitor monitor; - private HealthCheckService healthCheckService; - - @BeforeEach - void setUp(ObjectFactory factory, ServiceExtensionContext context) { - this.context = spy(context); - context.registerService(HealthCheckService.class, healthCheckService); - monitor = mock(Monitor.class); - healthCheckService = mock(HealthCheckService.class); - extension = factory.constructInstance(HashicorpVaultVaultExtension.class); - } - - @Test - void throwsHashicorpVaultExceptionOnVaultUrlUndefined() { - when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)).thenReturn(null); - - Assertions.assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); - } - - @Test - void throwsHashicorpVaultExceptionOnVaultTokenUndefined() { - when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) - .thenReturn(null); - - Assertions.assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java deleted file mode 100644 index 57a32ae99..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java +++ /dev/null @@ -1,97 +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.hashicorpvault; - -import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.health.HealthCheckService; -import org.eclipse.edc.spi.system.injection.ObjectFactory; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(DependencyInjectionExtension.class) -class HashicorpVaultHealthCheckExtensionTest { - - private static final String VAULT_URL = "https://example.com"; - private static final String VAULT_TOKEN = "token"; - private final HealthCheckService healthCheckService = mock(HealthCheckService.class); - private HashicorpVaultHealthExtension extension; - private ServiceExtensionContext context; - - @BeforeEach - void setUp(ObjectFactory factory, ServiceExtensionContext context) { - context.registerService(HealthCheckService.class, healthCheckService); - this.context = spy(context); - extension = factory.constructInstance(HashicorpVaultHealthExtension.class); - when(this.context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)) - .thenReturn(VAULT_URL); - when(this.context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) - .thenReturn(VAULT_TOKEN); - } - - @Test - void registersHealthCheckIfEnabled() { - when(context.getSetting(HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK, true)) - .thenReturn(true); - - extension.initialize(context); - - verify(healthCheckService, times(1)).addReadinessProvider(any()); - verify(healthCheckService, times(1)).addLivenessProvider(any()); - verify(healthCheckService, times(1)).addStartupStatusProvider(any()); - } - - @Test - void registersNoHealthCheckIfDisabled() { - when(context.getSetting(HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK, true)) - .thenReturn(false); - - extension.initialize(context); - - verify(healthCheckService, times(0)).addReadinessProvider(any()); - verify(healthCheckService, times(0)).addLivenessProvider(any()); - verify(healthCheckService, times(0)).addStartupStatusProvider(any()); - } - - @Test - void throwsHashicorpVaultExceptionOnVaultUrlUndefined() { - when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)).thenReturn(null); - - assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); - } - - @Test - void throwsHashicorpVaultExceptionOnVaultTokenUndefined() { - when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) - .thenReturn(null); - - assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java deleted file mode 100644 index dae708c81..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java +++ /dev/null @@ -1,73 +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.hashicorpvault; - -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; -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.ValueSource; -import org.mockito.Mockito; - -class HashicorpVaultHealthCheckTest { - - private HashicorpVaultHealthCheck healthCheck; - - // mocks - private Monitor monitor; - private HashicorpVaultClient client; - - @BeforeEach - void setup() { - monitor = Mockito.mock(Monitor.class); - client = Mockito.mock(HashicorpVaultClient.class); - - healthCheck = new HashicorpVaultHealthCheck(client, monitor); - } - - @ParameterizedTest - @ValueSource(ints = {200, 409, 472, 473, 501, 503, 999}) - void testResponseFromCode(int code) { - - Mockito.when(client.getHealth()) - .thenReturn(HashicorpVaultHealthResponse.Builder.newInstance().payload(new HashicorpVaultHealthResponsePayload()).code(code).build()); - - var result = healthCheck.get(); - - if (code == 200) { - Mockito.verify(monitor, Mockito.times(1)).debug(Mockito.anyString()); - Assertions.assertTrue(result.succeeded()); - } else { - Assertions.assertTrue(result.failed()); - Mockito.verify(monitor, Mockito.times(1)).warning(Mockito.anyString()); - } - } - - @Test - void testResponseFromException() { - Mockito.when(client.getHealth()).thenThrow(new EdcException("foo-bar")); - - var result = healthCheck.get(); - Assertions.assertFalse(result.succeeded()); - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultIt.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultIt.java deleted file mode 100644 index fd56385d1..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultIt.java +++ /dev/null @@ -1,122 +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.hashicorpvault; - -import org.eclipse.edc.spi.security.Vault; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import java.util.UUID; - -class HashicorpVaultIt extends AbstractHashicorpIt { - - @Test - @DisplayName("Resolve a secret that exists") - void testResolveSecret_exists() { - Vault vault = getVault(); - String secretValue = vault.resolveSecret(VAULT_ENTRY_KEY); - Assertions.assertEquals(VAULT_ENTRY_VALUE, secretValue); - } - - @Test - @DisplayName("Resolve a secret from a sub directory") - void testResolveSecret_inSubDirectory() { - Vault vault = getVault(); - String key = "sub/" + VAULT_ENTRY_KEY; - String value = key + "value"; - - vault.storeSecret(key, value); - String secretValue = vault.resolveSecret(key); - Assertions.assertEquals(value, secretValue); - } - - @ParameterizedTest - @ValueSource(strings = {"foo!bar", "foo.bar", "foo[bar]", "sub/foo{bar}"}) - @DisplayName("Resolve a secret with url encoded characters") - void testResolveSecret_withUrlEncodedCharacters(String key) { - Vault vault = getVault(); - String value = key + "value"; - - vault.storeSecret(key, value); - String secretValue = vault.resolveSecret(key); - Assertions.assertEquals(value, secretValue); - } - - @Test - @DisplayName("Resolve a secret that does not exist") - void testResolveSecret_doesNotExist() { - Vault vault = getVault(); - Assertions.assertNull(vault.resolveSecret("wrong_key")); - } - - @Test - @DisplayName("Update a secret that exists") - void testSetSecret_exists() { - String key = UUID.randomUUID().toString(); - String value1 = UUID.randomUUID().toString(); - String value2 = UUID.randomUUID().toString(); - - Vault vault = getVault(); - vault.storeSecret(key, value1); - vault.storeSecret(key, value2); - String secretValue = vault.resolveSecret(key); - Assertions.assertEquals(value2, secretValue); - } - - @Test - @DisplayName("Create a secret that does not exist") - void testSetSecret_doesNotExist() { - String key = UUID.randomUUID().toString(); - String value = UUID.randomUUID().toString(); - - Vault vault = getVault(); - vault.storeSecret(key, value); - String secretValue = vault.resolveSecret(key); - Assertions.assertEquals(value, secretValue); - } - - @Test - @DisplayName("Delete a secret that exists") - void testDeleteSecret_exists() { - String key = UUID.randomUUID().toString(); - String value = UUID.randomUUID().toString(); - - Vault vault = getVault(); - vault.storeSecret(key, value); - vault.deleteSecret(key); - - Assertions.assertNull(vault.resolveSecret(key)); - } - - @Test - @DisplayName("Try to delete a secret that does not exist") - void testDeleteSecret_doesNotExist() { - String key = UUID.randomUUID().toString(); - - Vault vault = getVault(); - vault.deleteSecret(key); - - Assertions.assertNull(vault.resolveSecret(key)); - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultTest.java deleted file mode 100644 index 2ca9e4da8..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultTest.java +++ /dev/null @@ -1,145 +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.hashicorpvault; - -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import java.util.UUID; - -class HashicorpVaultTest { - private static final String KEY = "key"; - - // mocks - private HashicorpVaultClient vaultClient; - private HashicorpVault vault; - - @BeforeEach - void setup() { - vaultClient = Mockito.mock(HashicorpVaultClient.class); - final Monitor monitor = Mockito.mock(Monitor.class); - vault = new HashicorpVault(vaultClient); - } - - @Test - void getSecretSuccess() { - // prepare - String value = UUID.randomUUID().toString(); - Result result = Mockito.mock(Result.class); - Mockito.when(vaultClient.getSecretValue(KEY)).thenReturn(result); - Mockito.when(result.getContent()).thenReturn(value); - Mockito.when(result.succeeded()).thenReturn(true); - Mockito.when(result.failed()).thenReturn(false); - - // invoke - String returnValue = vault.resolveSecret(KEY); - - // verify - Mockito.verify(vaultClient, Mockito.times(1)).getSecretValue(KEY); - Assertions.assertEquals(value, returnValue); - } - - @Test - void getSecretFailure() { - // prepare - Result result = Mockito.mock(Result.class); - Mockito.when(vaultClient.getSecretValue(KEY)).thenReturn(result); - Mockito.when(result.succeeded()).thenReturn(false); - Mockito.when(result.failed()).thenReturn(true); - - // invoke - String returnValue = vault.resolveSecret(KEY); - - // verify - Mockito.verify(vaultClient, Mockito.times(1)).getSecretValue(KEY); - Assertions.assertNull(returnValue); - } - - @Test - void setSecretSuccess() { - // prepare - String value = UUID.randomUUID().toString(); - Result result = Mockito.mock(Result.class); - Mockito.when(vaultClient.setSecret(KEY, value)).thenReturn(result); - Mockito.when(result.succeeded()).thenReturn(true); - Mockito.when(result.failed()).thenReturn(false); - - // invoke - Result returnValue = vault.storeSecret(KEY, value); - - // verify - Mockito.verify(vaultClient, Mockito.times(1)).setSecret(KEY, value); - Assertions.assertTrue(returnValue.succeeded()); - } - - @Test - void setSecretFailure() { - // prepare - String value = UUID.randomUUID().toString(); - Result result = Mockito.mock(Result.class); - Mockito.when(vaultClient.setSecret(KEY, value)).thenReturn(result); - Mockito.when(result.succeeded()).thenReturn(false); - Mockito.when(result.failed()).thenReturn(true); - - // invoke - Result returnValue = vault.storeSecret(KEY, value); - - // verify - Mockito.verify(vaultClient, Mockito.times(1)).setSecret(KEY, value); - Assertions.assertTrue(returnValue.failed()); - } - - @Test - void destroySecretSuccess() { - // prepare - Result result = Mockito.mock(Result.class); - Mockito.when(vaultClient.destroySecret(KEY)).thenReturn(result); - Mockito.when(result.succeeded()).thenReturn(true); - Mockito.when(result.failed()).thenReturn(false); - - // invoke - Result returnValue = vault.deleteSecret(KEY); - - // verify - Mockito.verify(vaultClient, Mockito.times(1)).destroySecret(KEY); - Assertions.assertTrue(returnValue.succeeded()); - } - - @Test - void destroySecretFailure() { - // prepare - Result result = Mockito.mock(Result.class); - Mockito.when(vaultClient.destroySecret(KEY)).thenReturn(result); - Mockito.when(result.succeeded()).thenReturn(false); - Mockito.when(result.failed()).thenReturn(true); - - // invoke - Result returnValue = vault.deleteSecret(KEY); - - // verify - Mockito.verify(vaultClient, Mockito.times(1)).destroySecret(KEY); - Assertions.assertTrue(returnValue.failed()); - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtilTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtilTest.java deleted file mode 100644 index b79a341b2..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtilTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * - * This program and the accompanying materials are 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: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation - * - */ - -package org.eclipse.tractusx.edc.hashicorpvault; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.stream.Stream; - -class PathUtilTest { - - private static Stream provideStringsForTrimsPathsCorrect() { - return Stream.of( - Arguments.of("v1/secret/data", "v1/secret/data"), - Arguments.of("/v1/secret/data", "v1/secret/data"), - Arguments.of("/v1/secret/data/", "v1/secret/data"), - Arguments.of("v1/secret/data/", "v1/secret/data")); - } - - @ParameterizedTest - @MethodSource("provideStringsForTrimsPathsCorrect") - void trimsPathsCorrect(String path, String expected) { - final String result = PathUtil.trimLeadingOrEndingSlash(path); - - Assertions.assertEquals(expected, result); - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/X509CertificateTestUtil.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/X509CertificateTestUtil.java deleted file mode 100644 index 45cd161f4..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/X509CertificateTestUtil.java +++ /dev/null @@ -1,137 +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.hashicorpvault; - -import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; -import org.bouncycastle.asn1.x509.BasicConstraints; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.cert.X509ExtensionUtils; -import org.bouncycastle.cert.X509v3CertificateBuilder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.DigestCalculator; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.operator.bc.BcDigestCalculatorProvider; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; -import org.testcontainers.shaded.org.bouncycastle.openssl.jcajce.JcaPEMWriter; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.Provider; -import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.time.Duration; -import java.time.Instant; -import java.util.Date; -import java.util.Optional; - -public class X509CertificateTestUtil { - private static final String SIGNATURE_ALGORITHM = "SHA256WithRSAEncryption"; - private static final Provider PROVIDER = new BouncyCastleProvider(); - private static final JcaX509CertificateConverter JCA_X509_CERTIFICATE_CONVERTER = - new JcaX509CertificateConverter().setProvider(PROVIDER); - - public static X509Certificate generateCertificate(int validity, String cn) - throws CertificateException, OperatorCreationException, IOException, - NoSuchAlgorithmException { - - KeyPair keyPair = generateKeyPair(); - - Instant now = Instant.now(); - ContentSigner contentSigner = - new JcaContentSignerBuilder(SIGNATURE_ALGORITHM).build(keyPair.getPrivate()); - X500Name issuer = - new X500Name( - String.format( - "CN=%s", - Optional.ofNullable(cn) - .map(String::trim) - .filter(s -> !s.isEmpty()) - .orElse("rootCA"))); - BigInteger serial = BigInteger.valueOf(now.toEpochMilli()); - Date notBefore = Date.from(now); - Date notAfter = Date.from(now.plus(Duration.ofDays(validity))); - PublicKey publicKey = keyPair.getPublic(); - X509v3CertificateBuilder certificateBuilder = - new JcaX509v3CertificateBuilder(issuer, serial, notBefore, notAfter, issuer, publicKey); - certificateBuilder = - certificateBuilder.addExtension( - Extension.subjectKeyIdentifier, false, createSubjectKeyId(publicKey)); - certificateBuilder = - certificateBuilder.addExtension( - Extension.authorityKeyIdentifier, false, createAuthorityKeyId(publicKey)); - certificateBuilder = - certificateBuilder.addExtension( - Extension.basicConstraints, true, new BasicConstraints(true)); - return JCA_X509_CERTIFICATE_CONVERTER.getCertificate(certificateBuilder.build(contentSigner)); - } - - private static KeyPair generateKeyPair() throws NoSuchAlgorithmException { - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", PROVIDER); - keyPairGenerator.initialize(1024, new SecureRandom()); - - return keyPairGenerator.generateKeyPair(); - } - - private static SubjectKeyIdentifier createSubjectKeyId(PublicKey publicKey) - throws OperatorCreationException { - SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); - DigestCalculator digCalc = - new BcDigestCalculatorProvider().get(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1)); - return new X509ExtensionUtils(digCalc).createSubjectKeyIdentifier(publicKeyInfo); - } - - private static AuthorityKeyIdentifier createAuthorityKeyId(PublicKey publicKey) - throws OperatorCreationException { - SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); - DigestCalculator digCalc = - new BcDigestCalculatorProvider().get(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1)); - return new X509ExtensionUtils(digCalc).createAuthorityKeyIdentifier(publicKeyInfo); - } - - static String convertToPem(X509Certificate certificate) { - try (var stream = new ByteArrayOutputStream()) { - try (OutputStreamWriter writer = new OutputStreamWriter(stream)) { - JcaPEMWriter pemWriter = new JcaPEMWriter(writer); - pemWriter.writeObject(certificate); - pemWriter.flush(); - } - return stream.toString(StandardCharsets.UTF_8); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/edc-extensions/hashicorp-vault/src/test/resources/logback.xml b/edc-extensions/hashicorp-vault/src/test/resources/logback.xml deleted file mode 100644 index fcc51d48d..000000000 --- a/edc-extensions/hashicorp-vault/src/test/resources/logback.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} [%thread] sdfsfs %-5level %logger{36} - %msg%n - - - - - - diff --git a/edc-extensions/postgresql-migration/build.gradle.kts b/edc-extensions/postgresql-migration/build.gradle.kts index 3ba3449de..de91ef6d5 100644 --- a/edc-extensions/postgresql-migration/build.gradle.kts +++ b/edc-extensions/postgresql-migration/build.gradle.kts @@ -30,5 +30,5 @@ dependencies { implementation(libs.edc.sql.core) runtimeOnly(libs.postgres) - implementation("org.flywaydb:flyway-core:9.20.0") + implementation("org.flywaydb:flyway-core:9.21.1") } diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/BusinessGroupPostgresMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/BusinessGroupPostgresMigrationExtension.java new file mode 100644 index 000000000..91125a930 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/BusinessGroupPostgresMigrationExtension.java @@ -0,0 +1,32 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.postgresql.migration; + +import org.eclipse.edc.connector.store.sql.assetindex.ConfigurationKeys; + +public class BusinessGroupPostgresMigrationExtension extends AbstractPostgresqlMigrationExtension { + private static final String NAME = "businessgroup"; + + + @Override + protected String getDataSourceNameConfigurationKey() { + return ConfigurationKeys.DATASOURCE_SETTING_NAME; + } + + @Override + protected String getSubsystemName() { + return NAME; + } +} diff --git a/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index a641ec766..9acab3b4d 100644 --- a/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -23,3 +23,4 @@ org.eclipse.tractusx.edc.postgresql.migration.ContractNegotiationPostgresqlMigra org.eclipse.tractusx.edc.postgresql.migration.PolicyPostgresqlMigrationExtension org.eclipse.tractusx.edc.postgresql.migration.TransferProcessPostgresqlMigrationExtension org.eclipse.tractusx.edc.postgresql.migration.EdrPostgresqlMigrationExtension +org.eclipse.tractusx.edc.postgresql.migration.BusinessGroupPostgresMigrationExtension diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/businessgroup/V0_0_1__Init_BusinessGroup_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/businessgroup/V0_0_1__Init_BusinessGroup_Schema.sql new file mode 100644 index 000000000..aa7b7a08f --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/businessgroup/V0_0_1__Init_BusinessGroup_Schema.sql @@ -0,0 +1,21 @@ +-- +-- 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 edc_business_partner_group +( + bpn VARCHAR NOT NULL + CONSTRAINT edc_business_partner_group_pk + PRIMARY KEY, + groups JSON DEFAULT '[]'::JSON NOT NULL +); 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/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_7__Alter_ContractNegotiation_AddPendingField.sql new file mode 100644 index 000000000..d5a7d0d7a --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_7__Alter_ContractNegotiation_AddPendingField.sql @@ -0,0 +1,16 @@ +-- +-- 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 +-- + +-- alter type of column "type" +ALTER TABLE edc_contract_negotiation + ADD COLUMN pending BOOLEAN DEFAULT FALSE; \ No newline at end of file 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/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_2__Add_ProviderId_Column.sql new file mode 100644 index 000000000..ee20b3990 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_2__Add_ProviderId_Column.sql @@ -0,0 +1,19 @@ +-- +-- 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 provider_id VARCHAR; + 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 new file mode 100644 index 000000000..a3442e00b --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_3__Add_StatefulEntity_Columns.sql @@ -0,0 +1,24 @@ +-- +-- 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/transferprocess/V0_0_11__Alter_TransferProcess_AddPendingField.sql b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_11__Alter_TransferProcess_AddPendingField.sql new file mode 100644 index 000000000..41048659b --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_11__Alter_TransferProcess_AddPendingField.sql @@ -0,0 +1,16 @@ +-- +-- 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 +-- + +-- add column +ALTER TABLE edc_transfer_process + ADD COLUMN pending BOOLEAN DEFAULT FALSE; 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 d06d5ca54..6ea8140fe 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 @@ -36,15 +36,6 @@ public boolean canHandle(DataFlowRequest request) { } } - @Override - public @NotNull Result validate(DataFlowRequest request) { - if (!canHandle(request)) { - return Result.failure(String.format("Invalid DataFlowRequest: %s", request.getId())); - } - - return VALID; - } - @Override public DataSink createSink(DataFlowRequest request) { if (!canHandle(request)) { @@ -65,4 +56,13 @@ public DataSink createSink(DataFlowRequest request) { return new SftpDataSink(sftpClientWrapper); } + + @Override + public @NotNull Result validateRequest(DataFlowRequest request) { + if (!canHandle(request)) { + return Result.failure(String.format("Invalid DataFlowRequest: %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 eca6e5302..c6b23045a 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 @@ -33,15 +33,6 @@ public boolean canHandle(DataFlowRequest request) { } } - @Override - public @NotNull Result validate(DataFlowRequest request) { - if (!canHandle(request)) { - return Result.failure(String.format("Invalid DataFlowRequest: %s", request.getId())); - } - - return VALID; - } - @Override public DataSource createSource(DataFlowRequest request) { if (!canHandle(request)) { @@ -59,4 +50,12 @@ public DataSource createSource(DataFlowRequest request) { SftpClientWrapper sftpClientWrapper = new SftpClientWrapperImpl(sftpClientConfig); return new SftpDataSource(sftpClientWrapper); } + + @Override + public @NotNull Result validateRequest(DataFlowRequest request) { + if (!canHandle(request)) { + return Result.failure(String.format("Invalid DataFlowRequest: %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 e254353a5..c4bcc948b 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 @@ -28,7 +28,6 @@ import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; import org.mockito.Mockito; import java.util.List; @@ -38,32 +37,32 @@ class SftpDataSinkFactoryTest { @Test void validate_valid() { - SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); - SftpUser sftpUser = SftpUser.Builder.newInstance().name("name").build(); - SftpLocation sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); + var dataSinkFactory = new SftpDataSinkFactory(); + var sftpUser = SftpUser.Builder.newInstance().name("name").build(); + var sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); - SftpDataAddress sftpDataAddress = + var sftpDataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + var request = Mockito.mock(DataFlowRequest.class); Mockito.when(request.getDestinationDataAddress()).thenReturn(sftpDataAddress); - Assertions.assertTrue(dataSinkFactory.validate(request).succeeded()); + Assertions.assertTrue(dataSinkFactory.validateRequest(request).succeeded()); } @Test void validate_invalidDataAddressType() { - SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); - DataAddress dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + var dataSinkFactory = new SftpDataSinkFactory(); + var dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); + var request = Mockito.mock(DataFlowRequest.class); Mockito.when(request.getDestinationDataAddress()).thenReturn(dataAddress); - Assertions.assertTrue(dataSinkFactory.validate(request).failed()); + Assertions.assertTrue(dataSinkFactory.validateRequest(request).failed()); } @Test void validate_invalidDataAddressParameters() { - SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); - final Map properties = + var dataSinkFactory = new SftpDataSinkFactory(); + Map properties = Map.of( "type", "sftp", "locationHost", "localhost", @@ -72,33 +71,33 @@ void validate_invalidDataAddressParameters() { "userName", "name", "userPassword", "password"); - final DataAddress dataAddress = + final var dataAddress = DataAddress.Builder.newInstance().properties(properties).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + var request = Mockito.mock(DataFlowRequest.class); Mockito.when(request.getDestinationDataAddress()).thenReturn(dataAddress); - Assertions.assertTrue(dataSinkFactory.validate(request).failed()); + Assertions.assertTrue(dataSinkFactory.validateRequest(request).failed()); } @Test void createSink_successful() { - SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); - SftpUser sftpUser = SftpUser.Builder.newInstance().name("name").build(); - SftpLocation sftpLocation = + var dataSinkFactory = new SftpDataSinkFactory(); + var sftpUser = SftpUser.Builder.newInstance().name("name").build(); + var sftpLocation = SftpLocation.Builder.newInstance().host("127.0.0.1").port(22).path("path").build(); - SftpClientConfig sftpClientConfig = + var sftpClientConfig = SftpClientConfig.Builder.newInstance() .writeOpenModes(List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Append)) .sftpUser(sftpUser) .sftpLocation(sftpLocation) .build(); - SftpDataAddress sftpDataAddress = + var sftpDataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + var request = Mockito.mock(DataFlowRequest.class); Mockito.when(request.getDestinationDataAddress()).thenReturn(sftpDataAddress); - try (MockedStatic staticWrapper = + try (var staticWrapper = Mockito.mockStatic(SftpClientWrapperImpl.class)) { staticWrapper .when(() -> SftpClientWrapperImpl.getSftpClient(sftpClientConfig)) @@ -112,9 +111,9 @@ void createSink_successful() { @Test void createSink_invalidDataAddressType() { - SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); - DataAddress dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + var dataSinkFactory = new SftpDataSinkFactory(); + var dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); + var request = Mockito.mock(DataFlowRequest.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 297059ddc..c4c1b183d 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 @@ -28,41 +28,45 @@ import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; -import org.mockito.Mockito; import java.util.Map; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + class SftpDataSourceFactoryTest { @Test void validate_valid() { - SftpDataSourceFactory dataSourceFactory = new SftpDataSourceFactory(); - SftpUser sftpUser = SftpUser.Builder.newInstance().name("name").build(); - SftpLocation sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); + var dataSourceFactory = new SftpDataSourceFactory(); + var sftpUser = SftpUser.Builder.newInstance().name("name").build(); + var sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); - SftpDataAddress sftpDataAddress = + var sftpDataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getSourceDataAddress()).thenReturn(sftpDataAddress); + var request = mock(DataFlowRequest.class); + when(request.getSourceDataAddress()).thenReturn(sftpDataAddress); - Assertions.assertTrue(dataSourceFactory.validate(request).succeeded()); + Assertions.assertTrue(dataSourceFactory.validateRequest(request).succeeded()); } @Test void validate_invalidDataAddressType() { - SftpDataSourceFactory dataSourceFactory = new SftpDataSourceFactory(); - DataAddress dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getSourceDataAddress()).thenReturn(dataAddress); + var dataSourceFactory = new SftpDataSourceFactory(); + var dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); + var request = mock(DataFlowRequest.class); + when(request.getSourceDataAddress()).thenReturn(dataAddress); - Assertions.assertTrue(dataSourceFactory.validate(request).failed()); + Assertions.assertTrue(dataSourceFactory.validateRequest(request).failed()); } @Test void validate_invalidDataAddressParameters() { - SftpDataSourceFactory dataSourceFactory = new SftpDataSourceFactory(); - final Map properties = + var dataSourceFactory = new SftpDataSourceFactory(); + Map properties = Map.of( "type", "sftp", "locationHost", "localhost", @@ -71,45 +75,45 @@ void validate_invalidDataAddressParameters() { "userName", "name", "userPassword", "password"); - DataAddress dataAddress = DataAddress.Builder.newInstance().properties(properties).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getSourceDataAddress()).thenReturn(dataAddress); + var dataAddress = DataAddress.Builder.newInstance().properties(properties).build(); + var request = mock(DataFlowRequest.class); + when(request.getSourceDataAddress()).thenReturn(dataAddress); - Assertions.assertTrue(dataSourceFactory.validate(request).failed()); + Assertions.assertTrue(dataSourceFactory.validateRequest(request).failed()); } @Test void createSink_successful() { - SftpDataSourceFactory dataSourceFactory = new SftpDataSourceFactory(); - SftpUser sftpUser = SftpUser.Builder.newInstance().name("name").build(); - SftpLocation sftpLocation = + var dataSourceFactory = new SftpDataSourceFactory(); + var sftpUser = SftpUser.Builder.newInstance().name("name").build(); + var sftpLocation = SftpLocation.Builder.newInstance().host("127.0.0.1").port(22).path("path").build(); - SftpClientConfig sftpClientConfig = + var sftpClientConfig = SftpClientConfig.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - SftpDataAddress sftpDataAddress = + var sftpDataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getSourceDataAddress()).thenReturn(sftpDataAddress); + var request = mock(DataFlowRequest.class); + when(request.getSourceDataAddress()).thenReturn(sftpDataAddress); - try (MockedStatic staticWrapper = - Mockito.mockStatic(SftpClientWrapperImpl.class)) { + try (var staticWrapper = + mockStatic(SftpClientWrapperImpl.class)) { staticWrapper .when(() -> SftpClientWrapperImpl.getSftpClient(sftpClientConfig)) - .thenReturn(Mockito.mock(SftpClient.class)); + .thenReturn(mock(SftpClient.class)); Assertions.assertNotNull(dataSourceFactory.createSource(request)); staticWrapper.verify( - () -> SftpClientWrapperImpl.getSftpClient(Mockito.any()), Mockito.times(1)); + () -> SftpClientWrapperImpl.getSftpClient(any()), times(1)); } } @Test void createSink_invalidDataAddressType() { - SftpDataSourceFactory dataSourceFactory = new SftpDataSourceFactory(); - DataAddress dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getSourceDataAddress()).thenReturn(dataAddress); + var dataSourceFactory = new SftpDataSourceFactory(); + var dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); + var request = mock(DataFlowRequest.class); + when(request.getSourceDataAddress()).thenReturn(dataAddress); Assertions.assertNull(dataSourceFactory.createSource(request)); } diff --git a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpDataAddress.java b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpDataAddress.java index f3cf35b18..5fa6da433 100644 --- a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpDataAddress.java +++ b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpDataAddress.java @@ -57,25 +57,25 @@ public static SftpDataAddress fromDataAddress(DataAddress dataAddress) throws Ed } catch (NullPointerException e) { throw new EdcSftpException(e.getMessage(), e); } catch (NumberFormatException e) { - throw new EdcSftpException(format("Port for SftpLocation %s/%s not a number", dataAddress.getProperty(LOCATION_HOST), dataAddress.getProperty(LOCATION_PATH)), e); + throw new EdcSftpException(format("Port for SftpLocation %s/%s not a number", dataAddress.getStringProperty(LOCATION_HOST), dataAddress.getStringProperty(LOCATION_PATH)), e); } } private static SftpLocation createSftpLocation(DataAddress dataAddress) { return SftpLocation.Builder.newInstance() - .host(dataAddress.getProperty(LOCATION_HOST)) - .port(Integer.parseInt(dataAddress.getProperty(LOCATION_PORT, "22"))) - .path(dataAddress.getProperty(LOCATION_PATH)) + .host(dataAddress.getStringProperty(LOCATION_HOST)) + .port(Integer.parseInt(dataAddress.getStringProperty(LOCATION_PORT, "22"))) + .path(dataAddress.getStringProperty(LOCATION_PATH)) .build(); } private static SftpUser createSftpUser(DataAddress dataAddress) { return SftpUser.Builder.newInstance() - .name(dataAddress.getProperty(USER_NAME)) - .password(dataAddress.getProperty(USER_PASSWORD)) + .name(dataAddress.getStringProperty(USER_NAME)) + .password(dataAddress.getStringProperty(USER_PASSWORD)) .keyPair(SftpUserKeyPairGenerator.getKeyPairFromPrivateKey( - dataAddress.getProperty(USER_PRIVATE_KEY), - dataAddress.getProperty(USER_NAME))) + dataAddress.getStringProperty(USER_PRIVATE_KEY), + dataAddress.getStringProperty(USER_NAME))) .build(); } diff --git a/edc-extensions/transferprocess-sftp-common/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpDataAddressTest.java b/edc-extensions/transferprocess-sftp-common/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpDataAddressTest.java index 07f60e406..d6c129c32 100644 --- a/edc-extensions/transferprocess-sftp-common/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpDataAddressTest.java +++ b/edc-extensions/transferprocess-sftp-common/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpDataAddressTest.java @@ -32,7 +32,7 @@ class SftpDataAddressTest { @Test void fromDataAddress_password() { - var properties = Map.of( + Map properties = Map.of( "type", "sftp", "locationHost", "localhost", "locationPort", "22", @@ -59,7 +59,7 @@ void fromDataAddress_keyPair() throws NoSuchAlgorithmException { var keyPair = keyPairGenerator.generateKeyPair(); var privateKeyBytes = keyPair.getPrivate().getEncoded(); - var properties = + Map properties = Map.of( "type", "sftp", "locationHost", "localhost", @@ -84,7 +84,7 @@ void fromDataAddress_keyPair() throws NoSuchAlgorithmException { @Test void fromDataAddress_noAuth() { - var properties = + Map properties = Map.of( "type", "sftp", "locationHost", "localhost", @@ -106,7 +106,7 @@ void fromDataAddress_noAuth() { @Test void fromDataAddress_invalidKeyPairBrokenBase64() { - var properties = + Map properties = Map.of( "type", "sftp", "locationHost", "localhost", @@ -127,7 +127,7 @@ void fromDataAddress_invalidKeyPairBrokenBase64() { @Test void fromDataAddress_invalidKeyPairButCorrectBase64() { - var properties = + Map properties = Map.of( "type", "sftp", "locationHost", "localhost", @@ -148,7 +148,7 @@ void fromDataAddress_invalidKeyPairButCorrectBase64() { @Test void fromDataAddress_portNaN() { - var properties = + Map properties = Map.of( "type", "sftp", "locationHost", "localhost", @@ -168,7 +168,7 @@ void fromDataAddress_portNaN() { @Test void fromDataAddress_missingParameter() { - var properties = + Map properties = Map.of( "type", "sftp", "locationPort", "22", @@ -185,7 +185,7 @@ void fromDataAddress_missingParameter() { @Test void fromDataAddress_notSftp() { - var properties = + Map properties = Map.of( "type", "somethingOtherThanSftp", "locationHost", "localhost", 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 f80687cda..90120c220 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 @@ -57,18 +57,6 @@ 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: - server: - postStart: - - sh - - -c - - |- - { - sleep 5 - - /bin/vault kv put secret/client-secret content=4bDC8/uXB6o517zqqCdrPA== - - /bin/vault kv put secret/aes-keys content=YWVzX2VuY2tleV90ZXN0Cg== - } backendService: httpProxyTokenReceiverUrl: "http://backend:8080" tests: diff --git a/edc-tests/e2e-tests/build.gradle.kts b/edc-tests/e2e-tests/build.gradle.kts index 5a58005d4..af8d43d19 100644 --- a/edc-tests/e2e-tests/build.gradle.kts +++ b/edc-tests/e2e-tests/build.gradle.kts @@ -17,8 +17,8 @@ plugins { } dependencies { - testImplementation(project(":spi:edr-cache-spi")) - testImplementation(project(":edc-extensions:control-plane-adapter-api")) + testImplementation(project(":spi:edr-spi")) + testImplementation(project(":edc-extensions:edr:edr-api")) testImplementation(libs.okhttp.mockwebserver) testImplementation(libs.restAssured) testImplementation(libs.nimbus.jwt) @@ -38,13 +38,17 @@ dependencies { testImplementation(libs.edc.ext.jsonld) testImplementation(libs.edc.dsp) testImplementation(testFixtures(libs.edc.sql.core)) - + testImplementation(libs.awaitility) 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:bpn-validation:bpn-validation-spi")) testImplementation(libs.edc.auth.oauth2.client) + testImplementation(libs.testcontainers.junit) + testImplementation(libs.testcontainers.postgres) + } // do not publish 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 index 74a24e9b2..97e7f5293 100644 --- 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 @@ -32,11 +32,9 @@ public class AssetHelperFunctions { public static JsonObject createAsset(String id, JsonObject assetProperties, JsonObject dataAddress) { return Json.createObjectBuilder() .add(CONTEXT, createContextBuilder()) - .add(TYPE, EDC_NAMESPACE + "AssetEntryDto") - .add(EDC_NAMESPACE + "asset", Json.createObjectBuilder() - .add(ID, id) - .add(EDC_NAMESPACE + "properties", assetProperties) - .build()) + .add(TYPE, "Asset") + .add(ID, id) + .add(EDC_NAMESPACE + "properties", assetProperties) .add(EDC_NAMESPACE + "dataAddress", dataAddress) .build(); diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/CatalogHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/CatalogHelperFunctions.java index 3803ea9e3..a037b9f07 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/CatalogHelperFunctions.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/CatalogHelperFunctions.java @@ -20,7 +20,7 @@ import jakarta.json.JsonValue; import org.eclipse.edc.connector.contract.spi.ContractId; -import static org.eclipse.edc.catalog.spi.CatalogRequest.EDC_CATALOG_REQUEST_QUERY_SPEC; +import static org.eclipse.edc.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; @@ -35,14 +35,14 @@ public static JsonObject createCatalogRequest(JsonObject query, String dspEndpoi jsonBuilder.add(EDC_NAMESPACE + "protocol", "dataspace-protocol-http"); if (query != null) { - jsonBuilder.add(EDC_CATALOG_REQUEST_QUERY_SPEC, query); + jsonBuilder.add(CATALOG_REQUEST_QUERY_SPEC, query); } return jsonBuilder.build(); } public static ContractId getDatasetContractId(JsonObject dataset) { var id = dataset.getJsonArray(ODRL_POLICY_ATTRIBUTE).get(0).asJsonObject().getString(ID); - return ContractId.parse(id); + return ContractId.parseId(id).orElseThrow(f -> new RuntimeException(f.getFailureDetail())); } public static String getDatasetAssetId(JsonObject dataset) { diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java index bc4bb22a7..ffab67582 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java @@ -22,7 +22,7 @@ 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.cp.adapter.dto.NegotiateEdrRequestDto; +import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto; import java.util.Set; diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java index ee7bb0689..d1196453b 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java @@ -15,14 +15,22 @@ package org.eclipse.tractusx.edc.helpers; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.json.Json; import jakarta.json.JsonArrayBuilder; import jakarta.json.JsonObject; import jakarta.json.JsonObjectBuilder; import org.eclipse.edc.connector.policy.spi.PolicyDefinition; +import org.eclipse.edc.jsonld.util.JacksonJsonLd; import org.eclipse.edc.policy.model.AtomicConstraint; +import org.eclipse.edc.policy.model.Operator; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; import java.util.Map; +import java.util.stream.Collectors; import java.util.stream.Stream; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT; @@ -34,18 +42,49 @@ public class PolicyHelperFunctions { + public static final String TX_NAMESPACE = "https://w3id.org/tractusx/v0.0.1/ns/"; private static final String BUSINESS_PARTNER_EVALUATION_KEY = "BusinessPartnerNumber"; + private static final String BUSINESS_PARTNER_CONSTRAINT_KEY = TX_NAMESPACE + "BusinessPartnerGroup"; + + private static final ObjectMapper MAPPER = JacksonJsonLd.createObjectMapper(); /** * 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]}. + * + * @deprecated This method creates a policy that is compliant with the old/legacy BPN validation. Please use {@link PolicyHelperFunctions#businessPartnerGroupPolicy(String, Operator, String...)} instead. */ + @Deprecated public static JsonObject businessPartnerNumberPolicy(String id, String... bpns) { return policyDefinitionBuilder(bnpPolicy(bpns)) .add(ID, id) .build(); } + public static JsonObject businessPartnerGroupPolicy(String id, Operator operator, String... allowedGroups) { + return policyDefinitionBuilder(bpnGroupPolicy(operator.getOdrlRepresentation(), allowedGroups)) + .add(ID, id) + .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, "http://www.w3.org/ns/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]}. @@ -56,6 +95,30 @@ public static JsonObject frameworkPolicy(String id, Map permissi .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 frameworkTemplatePolicy(String id, String frameworkKind) { + var template = fetchFrameworkTemplate().replace("${POLICY_ID}", id).replace("${FRAMEWORK_CREDENTIAL}", frameworkKind); + try { + return MAPPER.readValue(template, JsonObject.class); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + 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"); @@ -125,11 +188,16 @@ private static JsonObject frameworkPermission(Map permissions) { } private static JsonObject atomicConstraint(String leftOperand, String operator, Object rightOperand) { - return Json.createObjectBuilder() + var builder = Json.createObjectBuilder() .add(TYPE, ODRL_CONSTRAINT_TYPE) .add("leftOperand", leftOperand) - .add("operator", operator) - .add("rightOperand", rightOperand.toString()) - .build(); + .add("operator", operator); + + if (rightOperand instanceof Collection) { + builder.add("rightOperand", ((Collection) rightOperand).stream().map(Object::toString).collect(Collectors.joining(","))); + } else { + builder.add("rightOperand", rightOperand.toString()); + } + return builder.build(); } } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/QueryHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/QueryHelperFunctions.java index 74bce1f90..70a837385 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/QueryHelperFunctions.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/QueryHelperFunctions.java @@ -17,10 +17,10 @@ import jakarta.json.Json; import jakarta.json.JsonObject; -import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_LIMIT; -import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_OFFSET; -import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_TYPE; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; +import static org.eclipse.edc.spi.query.QuerySpec.EDC_QUERY_SPEC_LIMIT; +import static org.eclipse.edc.spi.query.QuerySpec.EDC_QUERY_SPEC_OFFSET; +import static org.eclipse.edc.spi.query.QuerySpec.EDC_QUERY_SPEC_TYPE; public class QueryHelperFunctions { diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/TxPostgresqlLocalInstance.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/TxPostgresqlLocalInstance.java new file mode 100644 index 000000000..eaac4e0e2 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/TxPostgresqlLocalInstance.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 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 + * + */ + +package org.eclipse.tractusx.edc.helpers; + +import org.postgresql.ds.PGSimpleDataSource; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import javax.sql.DataSource; + +import static java.lang.String.format; + +@Deprecated(forRemoval = true) +public final class TxPostgresqlLocalInstance { + private final String password; + private final String jdbcUrlPrefix; + private final String username; + private final String databaseName; + + public TxPostgresqlLocalInstance(String user, String password, String jdbcUrlPrefix, String db) { + username = user; + this.password = password; + this.jdbcUrlPrefix = jdbcUrlPrefix; + databaseName = db; + } + + public void createDatabase() { + createDatabase(databaseName); + } + + public void createDatabase(String name) { + try (var connection = DriverManager.getConnection(jdbcUrlPrefix + username, username, password)) { + connection.createStatement().execute(format("create database %s;", name)); + } catch (SQLException e) { + e.printStackTrace(); + // database could already exist + } + } + + public Connection getTestConnection(String hostName, int port, String dbName) { + try { + return createTestDataSource(hostName, port, dbName).getConnection(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public Connection getConnection() { + try { + return DriverManager.getConnection(jdbcUrlPrefix, username, password); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public String getJdbcUrlPrefix() { + return jdbcUrlPrefix; + } + + private DataSource createTestDataSource(String hostName, int port, String dbName) { + var dataSource = new PGSimpleDataSource(); + dataSource.setServerNames(new String[]{ hostName }); + dataSource.setPortNumbers(new int[]{ port }); + dataSource.setUser(username); + dataSource.setPassword(password); + dataSource.setDatabaseName(dbName); + return dataSource; + } +} 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 6910eeda6..83c10940c 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,9 +19,11 @@ import org.eclipse.edc.spi.asset.AssetIndex; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.edr.spi.store.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.validation.businesspartner.spi.BusinessPartnerStore; -import java.util.stream.Collectors; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; /** * Helper class to delete all objects from a runtime's data stores. @@ -39,6 +41,13 @@ public void clearPersistence() { clearPolicies(); clearContractDefinitions(); clearEdrCache(); + clearBusinessPartnerStore(); + } + + public void clearBusinessPartnerStore() { + var bps = context.getService(BusinessPartnerStore.class); + bps.delete(SOKRATES_BPN); + bps.delete(PLATO_BPN); } public void clearContractDefinitions() { @@ -49,7 +58,7 @@ public void clearContractDefinitions() { public void clearPolicies() { var ps = context.getService(PolicyDefinitionStore.class); // must .collect() here, otherwise we'll get a ConcurrentModificationException - ps.findAll(QuerySpec.max()).collect(Collectors.toList()).forEach(p -> ps.delete(p.getId())); + ps.findAll(QuerySpec.max()).toList().forEach(p -> ps.delete(p.getId())); } public void clearAssetIndex() { @@ -59,6 +68,12 @@ public void clearAssetIndex() { public void clearEdrCache() { var edrCache = context.getService(EndpointDataReferenceCache.class); - edrCache.queryForEntries(QuerySpec.max()).forEach(entry -> edrCache.deleteByTransferProcessId(entry.getTransferProcessId())); + edrCache.queryForEntries(QuerySpec.max()).forEach(entry -> { + try { + edrCache.deleteByTransferProcessId(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/Participant.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java index eb8ba1906..36b722640 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java @@ -16,6 +16,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.restassured.response.Response; +import io.restassured.response.ValidatableResponse; import io.restassured.specification.RequestSpecification; import jakarta.json.Json; import jakarta.json.JsonArray; @@ -32,6 +33,7 @@ import java.net.URI; import java.time.Duration; +import java.util.Arrays; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; @@ -49,6 +51,7 @@ import static org.eclipse.tractusx.edc.helpers.CatalogHelperFunctions.getDatasetFirstPolicy; import static org.eclipse.tractusx.edc.helpers.ContractNegotiationHelperFunctions.createNegotiationRequest; import static org.eclipse.tractusx.edc.helpers.EdrNegotiationHelperFunctions.createEdrNegotiationRequest; +import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.TX_NAMESPACE; import static org.eclipse.tractusx.edc.helpers.TransferProcessHelperFunctions.createTransferRequest; import static org.mockito.Mockito.mock; @@ -106,7 +109,7 @@ public void createAsset(String id, JsonObject assetProperties, JsonObject dataAd baseRequest() .body(asset) .when() - .post("/v2/assets") + .post("/v3/assets") .then() .statusCode(200) .contentType(JSON); @@ -139,6 +142,20 @@ public void createPolicy(JsonObject policyDefinition) { .contentType(JSON); } + public void storeBusinessPartner(String bpn, String... groups) { + var body = Json.createObjectBuilder() + .add(ID, bpn) + .add(TX_NAMESPACE + "groups", Json.createArrayBuilder(Arrays.asList(groups))) + .build(); + baseRequest() + .contentType(JSON) + .body(body) + .when() + .post("/business-partner-groups") + .then() + .statusCode(204); + } + public String negotiateContract(Participant other, String assetId) { var dataset = getDatasetForAsset(other, assetId); assertThat(dataset).withFailMessage("Catalog received from " + other.runtimeName + " was empty!").isNotEmpty(); @@ -198,16 +215,20 @@ public String getContractNegotiationError(String negotiationId) { } public JsonObject getEdr(String transferProcessId) { - return baseRequest() - .when() - .get("/edrs/{id}", transferProcessId) - .then() + return getEdrRequest(transferProcessId) .statusCode(200) .extract() .body() .as(JsonObject.class); } + public ValidatableResponse getEdrRequest(String transferProcessId) { + return baseRequest() + .when() + .get("/edrs/{id}", transferProcessId) + .then(); + } + public JsonArray getEdrEntriesByAssetId(String assetId) { return baseRequest() .when() @@ -326,8 +347,21 @@ public String pullProxyDataByAssetId(Participant provider, String assetId) { return getProxyData(body); } + + public String pullProviderDataPlaneDataByAssetId(Participant provider, String assetId) { + var body = Map.of("assetId", assetId); + return getProxyData(body); + } + + public String pullProviderDataPlaneDataByAssetIdAndCustomProperties(Participant provider, String assetId, String path, String params) { + var body = Map.of("assetId", assetId, "pathSegments", path, "queryParams", params); + return getProxyData(body); + } + public Response pullProxyDataResponseByAssetId(Participant provider, String assetId) { - var body = Map.of("assetId", assetId, "endpointUrl", format("%s/aas/test", provider.gatewayEndpoint)); + var body = Map.of("assetId", assetId, + "endpointUrl", format("%s/aas/test", provider.gatewayEndpoint), + "providerId", provider.bpn); return proxyRequest(body); } @@ -338,6 +372,12 @@ public String pullProxyDataByTransferProcessId(Participant provider, String tran } + public String pullProviderDataPlaneDataByTransferProcessId(Participant provider, String transferProcessId) { + var body = Map.of("transferProcessId", transferProcessId); + return getProxyData(body); + + } + public JsonObject getDatasetForAsset(Participant provider, String assetId) { var datasets = getCatalogDatasets(provider); return datasets.stream() @@ -367,6 +407,7 @@ private String getProxyData(Map body) { private Response proxyRequest(Map body) { return given() .baseUri(proxyUrl) + .header("x-api-key", apiKey) .contentType("application/json") .body(body) .post(PROXY_SUBPATH); @@ -378,4 +419,6 @@ private RequestSpecification baseRequest() { .header("x-api-key", apiKey) .contentType(JSON); } + + } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/PgParticipantRuntime.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/PgParticipantRuntime.java index f66b01bf3..ecdfa0e6d 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/PgParticipantRuntime.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/PgParticipantRuntime.java @@ -24,29 +24,106 @@ import org.eclipse.edc.spi.system.injection.InjectionContainer; import org.eclipse.edc.sql.testfixtures.PostgresqlLocalInstance; import org.eclipse.tractusx.edc.token.MockDapsService; +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 static java.lang.String.format; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.DB_SCHEMA_NAME; import static org.mockito.Mockito.mock; public class PgParticipantRuntime extends ParticipantRuntime { + private static final String POSTGRES_IMAGE_NAME = "postgres:14.2"; + private static final String USER = "postgres"; + private static final String PASSWORD = "password"; private final String dbName; + public PostgreSQLContainer postgreSqlContainer; public PgParticipantRuntime(String moduleName, String runtimeName, String bpn, Map properties) { super(moduleName, runtimeName, bpn, properties); this.dbName = runtimeName.toLowerCase(); this.registerServiceMock(IdentityService.class, new MockDapsService(bpn)); this.registerServiceMock(Vault.class, new InMemoryVaultOverride(mock(Monitor.class))); + + postgreSqlContainer = new PostgreSQLContainer<>(POSTGRES_IMAGE_NAME) + .withLabel("runtime", dbName) + .withExposedPorts(5432) + .withUsername(USER) + .withPassword(PASSWORD) + .withDatabaseName(dbName); + } + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + postgreSqlContainer.start(); + var config = postgresqlConfiguration(dbName); + config.forEach(System::setProperty); + super.beforeAll(context); + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + super.afterAll(context); + postgreSqlContainer.stop(); + postgreSqlContainer.close(); } @Override protected void bootExtensions(ServiceExtensionContext context, List> serviceExtensions) { - PostgresqlLocalInstance.createDatabase(dbName); + PostgresqlLocalInstance helper = new PostgresqlLocalInstance(postgreSqlContainer.getUsername(), postgreSqlContainer.getPassword(), baseJdbcUrl(), postgreSqlContainer.getDatabaseName()); + helper.createDatabase(dbName); super.bootExtensions(context, serviceExtensions); } + public Map postgresqlConfiguration(String name) { + var jdbcUrl = jdbcUrl(name); + return new HashMap<>() { + { + put("edc.datasource.asset.name", "asset"); + put("edc.datasource.asset.url", jdbcUrl); + put("edc.datasource.asset.user", USER); + put("edc.datasource.asset.password", PASSWORD); + put("edc.datasource.contractdefinition.name", "contractdefinition"); + put("edc.datasource.contractdefinition.url", jdbcUrl); + put("edc.datasource.contractdefinition.user", USER); + put("edc.datasource.contractdefinition.password", PASSWORD); + put("edc.datasource.contractnegotiation.name", "contractnegotiation"); + put("edc.datasource.contractnegotiation.url", jdbcUrl); + put("edc.datasource.contractnegotiation.user", USER); + put("edc.datasource.contractnegotiation.password", PASSWORD); + put("edc.datasource.policy.name", "policy"); + put("edc.datasource.policy.url", jdbcUrl); + put("edc.datasource.policy.user", USER); + put("edc.datasource.policy.password", PASSWORD); + put("edc.datasource.transferprocess.name", "transferprocess"); + put("edc.datasource.transferprocess.url", jdbcUrl); + put("edc.datasource.transferprocess.user", USER); + put("edc.datasource.transferprocess.password", PASSWORD); + put("edc.datasource.edr.name", "edr"); + put("edc.datasource.edr.url", jdbcUrl); + put("edc.datasource.edr.user", USER); + put("edc.datasource.edr.password", PASSWORD); + put("edc.datasource.bpn.name", "bpn"); + put("edc.datasource.bpn.url", jdbcUrl); + put("edc.datasource.bpn.user", USER); + put("edc.datasource.bpn.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); + } + }; + } + + public String jdbcUrl(String name) { + return baseJdbcUrl() + name + "?currentSchema=" + DB_SCHEMA_NAME; + } + + public String baseJdbcUrl() { + return format("jdbc:postgresql://%s:%s/", postgreSqlContainer.getHost(), postgreSqlContainer.getFirstMappedPort()); + } private static class InMemoryVaultOverride extends InMemoryVault { diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java index 6823c4653..e186a94a5 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java @@ -14,9 +14,6 @@ package org.eclipse.tractusx.edc.lifecycle; -import org.eclipse.edc.sql.testfixtures.PostgresqlLocalInstance; -import org.jetbrains.annotations.NotNull; - import java.util.HashMap; import java.util.Map; @@ -66,54 +63,6 @@ public class TestRuntimeConfiguration { static final String OAUTH_TOKEN_URL = "http://localhost:" + OAUTH_PORT; - public static Map sokratesPostgresqlConfiguration() { - var baseConfiguration = sokratesConfiguration(); - var postgresConfiguration = postgresqlConfiguration(SOKRATES_NAME.toLowerCase()); - baseConfiguration.putAll(postgresConfiguration); - return baseConfiguration; - } - - public static Map platoPostgresqlConfiguration() { - var baseConfiguration = platoConfiguration(); - var postgresConfiguration = postgresqlConfiguration(PLATO_NAME.toLowerCase()); - baseConfiguration.putAll(postgresConfiguration); - return baseConfiguration; - } - - public static Map postgresqlConfiguration(String name) { - var jdbcUrl = jdbcUrl(name); - return new HashMap<>() { - { - put("edc.datasource.asset.name", "asset"); - put("edc.datasource.asset.url", jdbcUrl); - put("edc.datasource.asset.user", PostgresqlLocalInstance.USER); - put("edc.datasource.asset.password", PostgresqlLocalInstance.PASSWORD); - put("edc.datasource.contractdefinition.name", "contractdefinition"); - put("edc.datasource.contractdefinition.url", jdbcUrl); - put("edc.datasource.contractdefinition.user", PostgresqlLocalInstance.USER); - put("edc.datasource.contractdefinition.password", PostgresqlLocalInstance.PASSWORD); - put("edc.datasource.contractnegotiation.name", "contractnegotiation"); - put("edc.datasource.contractnegotiation.url", jdbcUrl); - put("edc.datasource.contractnegotiation.user", PostgresqlLocalInstance.USER); - put("edc.datasource.contractnegotiation.password", PostgresqlLocalInstance.PASSWORD); - put("edc.datasource.policy.name", "policy"); - put("edc.datasource.policy.url", jdbcUrl); - put("edc.datasource.policy.user", PostgresqlLocalInstance.USER); - put("edc.datasource.policy.password", PostgresqlLocalInstance.PASSWORD); - put("edc.datasource.transferprocess.name", "transferprocess"); - put("edc.datasource.transferprocess.url", jdbcUrl); - put("edc.datasource.transferprocess.user", PostgresqlLocalInstance.USER); - put("edc.datasource.transferprocess.password", PostgresqlLocalInstance.PASSWORD); - put("edc.datasource.edr.name", "edr"); - put("edc.datasource.edr.url", jdbcUrl); - put("edc.datasource.edr.user", PostgresqlLocalInstance.USER); - put("edc.datasource.edr.password", PostgresqlLocalInstance.PASSWORD); - // use non-default schema name to test usage of non-default schema - put("org.eclipse.tractusx.edc.postgresql.migration.schema", DB_SCHEMA_NAME); - } - }; - } - public static Map sokratesSsiConfiguration() { var ssiConfiguration = new HashMap() { { @@ -214,8 +163,4 @@ public static Map platoSsiConfiguration() { return ssiConfiguration; } - @NotNull - public static String jdbcUrl(String name) { - return PostgresqlLocalInstance.JDBC_URL_PREFIX + name + "?currentSchema=" + DB_SCHEMA_NAME; - } } 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 index 741e198ed..491ea292b 100644 --- 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 @@ -15,6 +15,7 @@ package org.eclipse.tractusx.edc.tests.catalog; +import org.eclipse.edc.policy.model.Operator; import org.eclipse.tractusx.edc.lifecycle.Participant; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -23,6 +24,7 @@ import static org.assertj.core.api.Assertions.assertThat; 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.businessPartnerGroupPolicy; import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.businessPartnerNumberPolicy; import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.noConstraintPolicyDefinition; import static org.eclipse.tractusx.edc.helpers.QueryHelperFunctions.createQuery; @@ -39,6 +41,7 @@ public abstract class AbstractCatalogTest { protected static final Participant PLATO = new Participant(PLATO_NAME, PLATO_BPN, platoConfiguration()); @Test + @DisplayName("Plato gets catalog from Sokrates. No constraints.") void requestCatalog_fulfillsPolicy_shouldReturnOffer() { // arrange SOKRATES.createAsset("test-asset"); @@ -61,8 +64,8 @@ void requestCatalog_fulfillsPolicy_shouldReturnOffer() { } @Test - @DisplayName("Verify that Plato receives only the offers he is permitted to") - void requestCatalog_filteredByBpn_shouldReject() { + @DisplayName("Verify that Plato receives only the offers he is permitted to (using the legacy BPN validation)") + void requestCatalog_filteredByBpnLegacy_shouldReject() { var onlyPlatoId = "ap"; var onlyDiogenesId = "db"; @@ -88,12 +91,44 @@ void requestCatalog_filteredByBpn_shouldReject() { 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 philosopherId = "ap"; + var mathId = "db"; + + var mustBeGreekPhilosopher = businessPartnerGroupPolicy(philosopherId, Operator.IS_ANY_OF, "greek_customer", "philosopher"); + var mustBeGreekMathematician = businessPartnerGroupPolicy(mathId, Operator.IS_ALL_OF, "greek_customer", "mathematician"); + var noConstraintPolicyId = "no-constraint"; + + + SOKRATES.storeBusinessPartner(PLATO.getBpn(), "greek_customer", "philosopher"); + SOKRATES.createPolicy(mustBeGreekPhilosopher); + SOKRATES.createPolicy(mustBeGreekMathematician); + SOKRATES.createPolicy(noConstraintPolicyDefinition(noConstraintPolicyId)); + + 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"); SOKRATES.createPolicy(noConstraintPolicyDefinition("policy-1")); - SOKRATES.createPolicy(businessPartnerNumberPolicy("policy-2", PLATO.getBpn())); + SOKRATES.createPolicy(businessPartnerGroupPolicy("policy-2", Operator.IS_ANY_OF, "test-group")); SOKRATES.createContractDefinition("asset-1", "def1", "policy-1", "policy-1"); SOKRATES.createContractDefinition("asset-1", "def2", "policy-2", "policy-1"); @@ -110,9 +145,10 @@ void requestCatalog_multipleOffersForAsset() { @DisplayName("Catalog with 1000 offers") void requestCatalog_of1000Assets_shouldContainAll() { var policyId = "policy-1"; - var policy = businessPartnerNumberPolicy(policyId, PLATO.getBpn()); + var policy = businessPartnerGroupPolicy(policyId, Operator.IS_NONE_OF, "test-group1", "test-group2"); SOKRATES.createPolicy(policy); SOKRATES.createPolicy(noConstraintPolicyDefinition("noconstraint")); + SOKRATES.storeBusinessPartner(PLATO.getBpn(), "test-group-3"); range(0, 1000) .forEach(i -> { 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 index 5af78043d..4ded2fc29 100644 --- 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 @@ -22,8 +22,8 @@ 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.lifecycle.TestRuntimeConfiguration.platoPostgresqlConfiguration; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesPostgresqlConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.platoConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; @PostgresqlDbIntegrationTest public class CatalogPostgresqlTest extends AbstractCatalogTest { @@ -33,13 +33,13 @@ public class CatalogPostgresqlTest extends AbstractCatalogTest { ":edc-tests:runtime:runtime-postgresql", SOKRATES_NAME, SOKRATES_BPN, - sokratesPostgresqlConfiguration() + sokratesConfiguration() ); @RegisterExtension protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( ":edc-tests:runtime:runtime-postgresql", PLATO_NAME, PLATO_BPN, - platoPostgresqlConfiguration() + platoConfiguration() ); } 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 index 97949b703..5cf929984 100644 --- 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 @@ -27,6 +27,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.tractusx.edc.helpers.CatalogHelperFunctions.getDatasetAssetId; import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.frameworkPolicy; +import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.frameworkTemplatePolicy; import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.noConstraintPolicyDefinition; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_DSP_CALLBACK; @@ -85,6 +86,38 @@ void requestCatalog_fulfillsPolicy_shouldReturnOffer() { SOKRATES.createContractDefinition("test-asset-1", "test-def-2", "test-ap2", "test-cp1"); + // 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 = noConstraintPolicyDefinition("test-cp1"); + var dismantlerAccessPolicy = frameworkTemplatePolicy("test-ap2", "Dismantler"); + + SOKRATES.createPolicy(bpnAccessPolicy); + SOKRATES.createPolicy(contractPolicy); + SOKRATES.createPolicy(dismantlerAccessPolicy); + + SOKRATES.createContractDefinition("test-asset", "test-def", "test-ap1", "test-cp1"); + SOKRATES.createContractDefinition("test-asset-1", "test-def-2", "test-ap2", "test-cp1"); + + // act var catalog = SOKRATES.getCatalogDatasets(SOKRATES); 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 new file mode 100644 index 000000000..e09f77d27 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractDeleteEdrTest.java @@ -0,0 +1,111 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.tests.edr; + +import jakarta.json.Json; +import okhttp3.mockwebserver.MockWebServer; +import org.assertj.core.api.Condition; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.tractusx.edc.lifecycle.Participant; +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.time.Duration; +import java.util.ArrayList; +import java.util.UUID; + +import static java.time.Duration.ofSeconds; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.EXPIRED; +import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.businessPartnerGroupPolicy; +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.lifecycle.TestRuntimeConfiguration.platoConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; + +public abstract class AbstractDeleteEdrTest { + + protected static final Participant SOKRATES = new Participant(SOKRATES_NAME, SOKRATES_BPN, sokratesConfiguration()); + protected static final Participant PLATO = new Participant(PLATO_NAME, PLATO_BPN, platoConfiguration()); + private static final Duration ASYNC_TIMEOUT = ofSeconds(45); + MockWebServer server; + + @BeforeEach + void setup() { + server = new MockWebServer(); + } + + @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"; + PLATO.createAsset(assetId, Json.createObjectBuilder().build(), Json.createObjectBuilder() + .add(EDC_NAMESPACE + "type", "HttpData") + .add(EDC_NAMESPACE + "contentType", "application/json") + .add(EDC_NAMESPACE + "baseUrl", "http://test:8080") + .add(EDC_NAMESPACE + "authKey", authCodeHeaderName) + .add(EDC_NAMESPACE + "authCode", authCode) + .build()); + + PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-1", Operator.NEQ, "forbidden-policy")); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-2", Operator.EQ, "test-group1", "test-group2")); + PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); + + var callbacks = Json.createArrayBuilder() + .build(); + + SOKRATES.negotiateEdr(PLATO, assetId, callbacks); + + var expired = new ArrayList(); + + await().atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var edrCaches = SOKRATES.getEdrEntriesByAssetId(assetId); + var localExpired = edrCaches.stream() + .filter(json -> json.asJsonObject().getJsonString("tx:edrState").getString().equals(EXPIRED.name())) + .map(json -> json.asJsonObject().getJsonString("edc:transferProcessId").getString()) + .toList(); + assertThat(localExpired).hasSizeGreaterThan(0); + expired.add(localExpired.get(0)); + }); + + await().atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> expired.forEach((id) -> SOKRATES.getEdrRequest(id).statusCode(404))); + + } + + @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/AbstractNegotiateEdrTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java index 0c4db22ea..9e6d87a40 100644 --- 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 @@ -14,7 +14,6 @@ package org.eclipse.tractusx.edc.tests.edr; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.json.Json; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -28,7 +27,7 @@ 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.tractusx.edc.helpers.ReceivedEvent; +import org.eclipse.edc.policy.model.Operator; import org.eclipse.tractusx.edc.lifecycle.Participant; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -38,7 +37,6 @@ import java.io.IOException; import java.util.List; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; @@ -46,13 +44,14 @@ 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.businessPartnerNumberPolicy; +import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.businessPartnerGroupPolicy; 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.lifecycle.TestRuntimeConfiguration.platoConfiguration; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; +import static org.eclipse.tractusx.edc.tests.edr.TestFunctions.waitForEvent; public abstract class AbstractNegotiateEdrTest { @@ -61,9 +60,6 @@ public abstract class AbstractNegotiateEdrTest { MockWebServer server; - ObjectMapper mapper = new ObjectMapper(); - - @BeforeEach void setup() { server = new MockWebServer(); @@ -99,8 +95,9 @@ void negotiateEdr_shouldInvokeCallbacks() throws IOException { .add(EDC_NAMESPACE + "authCode", authCode) .build()); - PLATO.createPolicy(businessPartnerNumberPolicy("policy-1", SOKRATES.getBpn())); - PLATO.createPolicy(businessPartnerNumberPolicy("policy-2", SOKRATES.getBpn())); + PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-1", Operator.IS_NONE_OF, "forbidden-policy")); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-2", Operator.IS_ALL_OF, "test-group1", "test-group2")); PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); @@ -113,7 +110,7 @@ void negotiateEdr_shouldInvokeCallbacks() throws IOException { SOKRATES.negotiateEdr(PLATO, assetId, callbacks); var events = expectedEvents.stream() - .map(this::waitForEvent) + .map(receivedEvent -> waitForEvent(server, receivedEvent)) .collect(Collectors.toList()); assertThat(expectedEvents).usingRecursiveFieldByFieldElementComparator().containsAll(events); @@ -134,20 +131,6 @@ void negotiateEdr_shouldInvokeCallbacks() throws IOException { } - - private ReceivedEvent waitForEvent(ReceivedEvent event) { - try { - var request = server.takeRequest(20, TimeUnit.SECONDS); - if (request != null) { - return mapper.readValue(request.getBody().inputStream(), ReceivedEvent.class); - } else { - throw new RuntimeException("Timeout exceeded waiting for events"); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - @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 new file mode 100644 index 000000000..d0c9d1b9c --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractRenewalEdrTest.java @@ -0,0 +1,136 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.tests.edr; + +import jakarta.json.Json; +import jakarta.json.JsonArrayBuilder; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.assertj.core.api.Condition; +import org.eclipse.edc.connector.transfer.spi.event.TransferProcessCompleted; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.tractusx.edc.lifecycle.Participant; +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.time.Duration; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import static java.time.Duration.ofSeconds; +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.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +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.businessPartnerGroupPolicy; +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.lifecycle.TestRuntimeConfiguration.platoConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; +import static org.eclipse.tractusx.edc.tests.edr.TestFunctions.waitForEvent; + +public abstract class AbstractRenewalEdrTest { + + protected static final Participant SOKRATES = new Participant(SOKRATES_NAME, SOKRATES_BPN, sokratesConfiguration()); + protected static final Participant PLATO = new Participant(PLATO_NAME, PLATO_BPN, platoConfiguration()); + private static final Duration ASYNC_TIMEOUT = ofSeconds(45); + 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(TransferProcessCompleted.class), + createEvent(TransferProcessCompleted.class)); + + var assetId = UUID.randomUUID().toString(); + var url = server.url("/mock/api"); + server.start(); + + var authCodeHeaderName = "test-authkey"; + var authCode = "test-authcode"; + PLATO.createAsset(assetId, Json.createObjectBuilder().build(), Json.createObjectBuilder() + .add(EDC_NAMESPACE + "type", "HttpData") + .add(EDC_NAMESPACE + "contentType", "application/json") + .add(EDC_NAMESPACE + "baseUrl", url.toString()) + .add(EDC_NAMESPACE + "authKey", authCodeHeaderName) + .add(EDC_NAMESPACE + "authCode", authCode) + .build()); + + PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-1", Operator.IS_NONE_OF, "forbidden-policy")); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-2", Operator.IS_ANY_OF, "test-group1", "test-group2")); + PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); + + var callbacks = Json.createArrayBuilder() + .add(createCallback(url.toString(), true, Set.of("transfer.process.completed"))) + .build(); + + expectedEvents.forEach(event -> server.enqueue(new MockResponse())); + + SOKRATES.negotiateEdr(PLATO, assetId, callbacks); + + var events = expectedEvents.stream() + .map(receivedEvent -> waitForEvent(server, receivedEvent)) + .collect(Collectors.toList()); + + assertThat(expectedEvents).usingRecursiveFieldByFieldElementComparator().containsAll(events); + + JsonArrayBuilder edrCaches = Json.createArrayBuilder(); + + await().atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var localEdrCaches = SOKRATES.getEdrEntriesByAssetId(assetId); + assertThat(localEdrCaches).hasSizeGreaterThan(1); + localEdrCaches.forEach(edrCaches::add); + }); + + + assertThat(edrCaches.build()) + .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")); + } + + + @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 new file mode 100644 index 000000000..2ca557532 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/DeleteEdrInMemoryTest.java @@ -0,0 +1,48 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.tests.edr; + + +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; +import org.junit.jupiter.api.extension.RegisterExtension; + +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.lifecycle.TestRuntimeConfiguration.platoConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; +import static org.eclipse.tractusx.edc.tests.edr.TestFunctions.renewalConfiguration; + +@EndToEndTest +public class DeleteEdrInMemoryTest extends AbstractDeleteEdrTest { + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + SOKRATES_NAME, + SOKRATES_BPN, + renewalConfiguration(sokratesConfiguration(), "5") + ); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + PLATO_NAME, + PLATO_BPN, + renewalConfiguration(platoConfiguration()) + ); +} 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 new file mode 100644 index 000000000..c1b3f96b9 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/DeleteEdrPostgresqlTest.java @@ -0,0 +1,47 @@ +/* + * 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 + * + */ + +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; + +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.lifecycle.TestRuntimeConfiguration.platoConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; +import static org.eclipse.tractusx.edc.tests.edr.TestFunctions.renewalConfiguration; + +@PostgresqlDbIntegrationTest +public class DeleteEdrPostgresqlTest extends AbstractDeleteEdrTest { + + @RegisterExtension + protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( + ":edc-tests:runtime:runtime-postgresql", + SOKRATES_NAME, + SOKRATES_BPN, + renewalConfiguration(sokratesConfiguration(), "5") + ); + @RegisterExtension + protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( + ":edc-tests:runtime:runtime-postgresql", + PLATO_NAME, + PLATO_BPN, + renewalConfiguration(platoConfiguration()) + ); + +} 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 index ec2ccf7bf..37f1e72f7 100644 --- 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 @@ -22,8 +22,8 @@ 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.lifecycle.TestRuntimeConfiguration.platoPostgresqlConfiguration; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesPostgresqlConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.platoConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; @PostgresqlDbIntegrationTest public class NegotiateEdrPostgresqlTest extends AbstractNegotiateEdrTest { @@ -33,14 +33,14 @@ public class NegotiateEdrPostgresqlTest extends AbstractNegotiateEdrTest { ":edc-tests:runtime:runtime-postgresql", SOKRATES_NAME, SOKRATES_BPN, - sokratesPostgresqlConfiguration() + sokratesConfiguration() ); @RegisterExtension protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( ":edc-tests:runtime:runtime-postgresql", PLATO_NAME, PLATO_BPN, - platoPostgresqlConfiguration() + platoConfiguration() ); } 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 new file mode 100644 index 000000000..da6f80ccb --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/RenewalEdrInMemoryTest.java @@ -0,0 +1,48 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.tests.edr; + + +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.tractusx.edc.lifecycle.ParticipantRuntime; +import org.junit.jupiter.api.extension.RegisterExtension; + +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.lifecycle.TestRuntimeConfiguration.platoConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; +import static org.eclipse.tractusx.edc.tests.edr.TestFunctions.renewalConfiguration; + +@EndToEndTest +public class RenewalEdrInMemoryTest extends AbstractRenewalEdrTest { + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + SOKRATES_NAME, + SOKRATES_BPN, + renewalConfiguration(sokratesConfiguration()) + ); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + PLATO_NAME, + PLATO_BPN, + renewalConfiguration(platoConfiguration()) + ); +} 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 new file mode 100644 index 000000000..93cb8117e --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/RenewalEdrPostgresqlTest.java @@ -0,0 +1,47 @@ +/* + * 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 + * + */ + +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; + +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.lifecycle.TestRuntimeConfiguration.platoConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; +import static org.eclipse.tractusx.edc.tests.edr.TestFunctions.renewalConfiguration; + +@PostgresqlDbIntegrationTest +public class RenewalEdrPostgresqlTest extends AbstractRenewalEdrTest { + + @RegisterExtension + protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( + ":edc-tests:runtime:runtime-postgresql", + SOKRATES_NAME, + SOKRATES_BPN, + renewalConfiguration(sokratesConfiguration()) + ); + @RegisterExtension + protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( + ":edc-tests:runtime:runtime-postgresql", + PLATO_NAME, + PLATO_BPN, + renewalConfiguration(platoConfiguration()) + ); + +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/TestFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/TestFunctions.java new file mode 100644 index 000000000..e123c61f1 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/TestFunctions.java @@ -0,0 +1,58 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.tests.edr; + +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.mockwebserver.MockWebServer; +import org.eclipse.tractusx.edc.helpers.ReceivedEvent; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class TestFunctions { + + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + public static Map renewalConfiguration(Map config) { + return renewalConfiguration(config, "10"); + } + + public static Map renewalConfiguration(Map config, String retention) { + var ssiConfiguration = 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"); + } + }; + ssiConfiguration.putAll(config); + return ssiConfiguration; + } + + public static ReceivedEvent waitForEvent(MockWebServer server, ReceivedEvent event) { + try { + var request = server.takeRequest(20, TimeUnit.SECONDS); + if (request != null) { + return MAPPER.readValue(request.getBody().inputStream(), ReceivedEvent.class); + } 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/negotiation/AbstractContractNegotiateTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/negotiation/AbstractContractNegotiateTest.java index 3651ac642..4053a77b7 100644 --- 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 @@ -16,6 +16,7 @@ import jakarta.json.Json; import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates; +import org.eclipse.edc.policy.model.Operator; import org.eclipse.tractusx.edc.lifecycle.Participant; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -27,7 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.businessPartnerNumberPolicy; +import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.businessPartnerGroupPolicy; 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; @@ -58,7 +59,8 @@ void contractNegotiation_shouldFail_whenPolicyEvaluationFails() { .add(EDC_NAMESPACE + "authCode", authCode) .build()); - PLATO.createPolicy(businessPartnerNumberPolicy("policy-1", SOKRATES.getBpn())); + PLATO.storeBusinessPartner(SOKRATES.getBpn(), "allowed-group"); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-1", Operator.NEQ, "forbidden-group")); PLATO.createPolicy(frameworkPolicy("policy-2", Map.of("Dismantler", "active"))); PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); 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 index f38ee49f0..37f3df3fe 100644 --- 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 @@ -17,11 +17,15 @@ 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.TransferProcessCompleted; +import org.eclipse.edc.policy.model.Operator; import org.eclipse.edc.spi.event.EventEnvelope; import org.eclipse.tractusx.edc.lifecycle.Participant; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -29,12 +33,13 @@ import java.io.IOException; import java.util.Set; +import java.util.UUID; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; import static org.eclipse.tractusx.edc.helpers.EdrNegotiationHelperFunctions.createCallback; -import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.businessPartnerNumberPolicy; +import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.businessPartnerGroupPolicy; 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.PLATO_PROXIED_AAS_BACKEND_PORT; @@ -48,16 +53,23 @@ public abstract class AbstractDataPlaneProxyTest { protected static final Participant SOKRATES = new Participant(SOKRATES_NAME, SOKRATES_BPN, sokratesConfiguration()); protected static final Participant PLATO = new Participant(PLATO_NAME, PLATO_BPN, platoConfiguration()); + 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; + @Test @DisplayName("Verify E2E flow with Data Plane proxies and EDR") - void httpPullDataTransfer_withEdrAndProxy() throws IOException { + void httpPullDataTransfer_withEdrAndProxy() { var eventsUrl = server.url(PROXIED_PATH); - var assetId = "api-asset-1"; + var assetId = UUID.randomUUID().toString(); var authCodeHeaderName = "test-authkey"; var authCode = "test-authcode"; PLATO.createAsset(assetId, Json.createObjectBuilder().build(), Json.createObjectBuilder() @@ -68,8 +80,9 @@ void httpPullDataTransfer_withEdrAndProxy() throws IOException { .add(EDC_NAMESPACE + "authCode", authCode) .build()); - PLATO.createPolicy(businessPartnerNumberPolicy("policy-1", SOKRATES.getBpn())); - PLATO.createPolicy(businessPartnerNumberPolicy("policy-2", SOKRATES.getBpn())); + PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-1", Operator.IS_ANY_OF, "test-group1")); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-2", Operator.IS_ALL_OF, "test-group1", "test-group2")); PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); var callbacks = Json.createArrayBuilder() @@ -100,7 +113,7 @@ void httpPullDataTransfer_withoutEdr() throws IOException { var eventsUrl = server.url(PROXIED_PATH); - var assetId = "api-asset-1"; + var assetId = UUID.randomUUID().toString(); var authCodeHeaderName = "test-authkey"; var authCode = "test-authcode"; PLATO.createAsset(assetId, Json.createObjectBuilder().build(), Json.createObjectBuilder() @@ -111,8 +124,9 @@ void httpPullDataTransfer_withoutEdr() throws IOException { .add(EDC_NAMESPACE + "authCode", authCode) .build()); - PLATO.createPolicy(businessPartnerNumberPolicy("policy-1", SOKRATES.getBpn())); - PLATO.createPolicy(businessPartnerNumberPolicy("policy-2", SOKRATES.getBpn())); + PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-1", Operator.NEQ, "forbidden-policy")); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-2", Operator.EQ, "test-group1", "test-group2")); PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); @@ -128,7 +142,7 @@ void httpPullDataTransfer_shouldFailForAsset_withTwoEdrAndProxy() throws IOExcep var eventsUrl = server.url(PROXIED_PATH); - var assetId = "api-asset-1"; + var assetId = UUID.randomUUID().toString(); var authCodeHeaderName = "test-authkey"; var authCode = "test-authcode"; PLATO.createAsset(assetId, Json.createObjectBuilder().build(), Json.createObjectBuilder() @@ -139,8 +153,9 @@ void httpPullDataTransfer_shouldFailForAsset_withTwoEdrAndProxy() throws IOExcep .add(EDC_NAMESPACE + "authCode", authCode) .build()); - PLATO.createPolicy(businessPartnerNumberPolicy("policy-1", SOKRATES.getBpn())); - PLATO.createPolicy(businessPartnerNumberPolicy("policy-2", SOKRATES.getBpn())); + PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-1", Operator.IS_NONE_OF, "forbidden-policy")); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-2", Operator.IS_ALL_OF, "test-group1", "test-group2")); PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); var callbacks = Json.createArrayBuilder() @@ -172,6 +187,103 @@ void httpPullDataTransfer_shouldFailForAsset_withTwoEdrAndProxy() throws IOExcep assertThat(data).isEqualTo(body); } + @Test + @DisplayName("Verify E2E flow with Data Plane provider and EDR") + void httpPullDataTransfer_withEdrAndProviderDataPlaneProxy() throws IOException { + + var eventsUrl = server.url(PROXIED_PATH); + + var assetId = UUID.randomUUID().toString(); + var authCodeHeaderName = "test-authkey"; + var authCode = "test-authcode"; + PLATO.createAsset(assetId, Json.createObjectBuilder().build(), Json.createObjectBuilder() + .add(EDC_NAMESPACE + "type", "HttpData") + .add(EDC_NAMESPACE + "contentType", "application/json") + .add(EDC_NAMESPACE + "baseUrl", eventsUrl.toString()) + .add(EDC_NAMESPACE + "authKey", authCodeHeaderName) + .add(EDC_NAMESPACE + "authCode", authCode) + .build()); + + PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-1", Operator.IS_ANY_OF, "test-group1")); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-2", Operator.IS_ALL_OF, "test-group1", "test-group2")); + PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); + + var callbacks = Json.createArrayBuilder() + .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.completed"))) + .build(); + + // response to callback + server.enqueue(new MockResponse()); + + SOKRATES.negotiateEdr(PLATO, assetId, callbacks); + + var transferEvent = waitForTransferCompletion(); + + var body = "{\"response\": \"ok\"}"; + + server.enqueue(new MockResponse().setBody(body)); + var data = SOKRATES.pullProviderDataPlaneDataByAssetId(PLATO, assetId); + assertThat(data).isEqualTo(body); + + server.enqueue(new MockResponse().setBody(body)); + data = SOKRATES.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 eventsUrl = server.url(PROXIED_PATH); + + 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 { + return switch (recordedRequest.getPath()) { + case PROXIED_PATH -> new MockResponse(); + case CUSTOM_FULL_PATH -> new MockResponse().setBody(body); + default -> new MockResponse().setResponseCode(404); + }; + } + }); + + var assetId = UUID.randomUUID().toString(); + var authCodeHeaderName = "test-authkey"; + var authCode = "test-authcode"; + PLATO.createAsset(assetId, Json.createObjectBuilder().build(), Json.createObjectBuilder() + .add(EDC_NAMESPACE + "type", "HttpData") + .add(EDC_NAMESPACE + "contentType", "application/json") + .add(EDC_NAMESPACE + "baseUrl", customUrl.toString()) + .add(EDC_NAMESPACE + "authKey", authCodeHeaderName) + .add(EDC_NAMESPACE + "authCode", authCode) + .add(EDC_NAMESPACE + "proxyPath", "true") + .add(EDC_NAMESPACE + "proxyQueryParams", "true") + .build()); + + PLATO.storeBusinessPartner(SOKRATES.getBpn(), "test-group1", "test-group2"); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-1", Operator.NEQ, "forbidden-policy")); + PLATO.createPolicy(businessPartnerGroupPolicy("policy-2", Operator.EQ, "test-group1", "test-group2")); + PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); + + var callbacks = Json.createArrayBuilder() + .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.completed"))) + .build(); + + SOKRATES.negotiateEdr(PLATO, assetId, callbacks); + + waitForTransferCompletion(); + + var data = SOKRATES.pullProviderDataPlaneDataByAssetIdAndCustomProperties(PLATO, assetId, CUSTOM_SUB_PATH, CUSTOM_QUERY_PARAMS); + assertThat(data).isEqualTo(body); + + } + @BeforeEach void setup() throws IOException { server = new MockWebServer(); @@ -185,7 +297,7 @@ void teardown() throws IOException { private EventEnvelope waitForTransferCompletion() { try { - var request = server.takeRequest(20, TimeUnit.SECONDS); + var request = server.takeRequest(60, TimeUnit.SECONDS); if (request != null) { return mapper.readValue(request.getBody().inputStream(), new TypeReference<>() { }); 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 index f2da5eba8..d6ce2d6bc 100644 --- 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 @@ -22,24 +22,24 @@ 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.lifecycle.TestRuntimeConfiguration.platoPostgresqlConfiguration; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesPostgresqlConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.platoConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; @PostgresqlDbIntegrationTest public class DataPlaneProxyPostgresqlTest extends AbstractDataPlaneProxyTest { - + @RegisterExtension protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( ":edc-tests:runtime:runtime-postgresql", SOKRATES_NAME, SOKRATES_BPN, - sokratesPostgresqlConfiguration() + sokratesConfiguration() ); @RegisterExtension protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( ":edc-tests:runtime:runtime-postgresql", PLATO_NAME, PLATO_BPN, - platoPostgresqlConfiguration() + platoConfiguration() ); } 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 index 887c7a307..61892beff 100644 --- 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 @@ -22,8 +22,8 @@ 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.lifecycle.TestRuntimeConfiguration.platoPostgresqlConfiguration; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesPostgresqlConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.platoConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; @PostgresqlDbIntegrationTest public class HttpConsumerPullWithProxyPostgresqlTest extends AbstractHttpConsumerPullWithProxyTest { @@ -34,14 +34,14 @@ public class HttpConsumerPullWithProxyPostgresqlTest extends AbstractHttpConsume ":edc-tests:runtime:runtime-postgresql", SOKRATES_NAME, SOKRATES_BPN, - sokratesPostgresqlConfiguration() + sokratesConfiguration() ); @RegisterExtension protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( ":edc-tests:runtime:runtime-postgresql", PLATO_NAME, PLATO_BPN, - platoPostgresqlConfiguration() + platoConfiguration() ); } diff --git a/edc-tests/e2e-tests/src/test/resources/framework-policy.json b/edc-tests/e2e-tests/src/test/resources/framework-policy.json new file mode 100644 index 000000000..321aecbe5 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/resources/framework-policy.json @@ -0,0 +1,24 @@ +{ + "@context": [ + "https://w3id.org/edc/v0.0.1", + "https://w3id.org/tractusx/edc/v0.0.1", + "http://www.w3.org/ns/odrl.jsonld" + ], + "@type": "PolicyDefinitionRequest", + "@id": "${POLICY_ID}", + "policy": { + "@type": "Set", + "permission": [ + { + "action": "use", + "constraint": [ + { + "leftOperand": "${FRAMEWORK_CREDENTIAL}", + "operator": "eq", + "rightOperand": "active" + } + ] + } + ] + } +} diff --git a/edc-tests/edc-dataplane-proxy-e2e/build.gradle.kts b/edc-tests/edc-dataplane-proxy-e2e/build.gradle.kts index 0eeb54af0..eeb8473f3 100644 --- a/edc-tests/edc-dataplane-proxy-e2e/build.gradle.kts +++ b/edc-tests/edc-dataplane-proxy-e2e/build.gradle.kts @@ -24,7 +24,8 @@ dependencies { // test runtime config testImplementation(libs.edc.config.filesystem) testImplementation(libs.edc.dpf.http) - testImplementation(project(":spi:edr-cache-spi")) + testImplementation(libs.edc.auth.tokenbased) + 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")) diff --git a/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/DpfProxyEndToEndTest.java b/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/DpfProxyEndToEndTest.java index 2a5780162..588f6d698 100644 --- a/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/DpfProxyEndToEndTest.java +++ b/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/DpfProxyEndToEndTest.java @@ -21,7 +21,7 @@ import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.spi.types.domain.HttpDataAddress; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCache; +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; @@ -74,6 +74,7 @@ public class DpfProxyEndToEndTest { 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( @@ -81,9 +82,9 @@ public class DpfProxyEndToEndTest { "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, @@ -184,6 +185,7 @@ private RequestSpecification createSpecification(String body) { return given() .baseUri("http://localhost:" + CONSUMER_PROXY_PORT) .contentType("application/json") + .header("x-api-key", API_KEY) .body(body); } diff --git a/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/EdrCacheSetup.java b/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/EdrCacheSetup.java index bcaeaf68a..59a1a80b8 100644 --- a/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/EdrCacheSetup.java +++ b/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/EdrCacheSetup.java @@ -16,7 +16,7 @@ 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.EndpointDataReferenceEntry; +import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; import java.util.ArrayList; import java.util.List; diff --git a/edc-tests/miw-tests/build.gradle.kts b/edc-tests/miw-tests/build.gradle.kts index 40e999168..c27404b3a 100644 --- a/edc-tests/miw-tests/build.gradle.kts +++ b/edc-tests/miw-tests/build.gradle.kts @@ -17,8 +17,8 @@ plugins { } dependencies { - testImplementation(project(":spi:edr-cache-spi")) - testImplementation(project(":edc-extensions:control-plane-adapter-api")) + testImplementation(project(":spi:edr-spi")) + testImplementation(project(":edc-extensions:edr:edr-api")) testImplementation(libs.okhttp.mockwebserver) testImplementation(libs.restAssured) testImplementation(libs.nimbus.jwt) diff --git a/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/TestServiceExtension.java b/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/TestServiceExtension.java new file mode 100644 index 000000000..b498f185b --- /dev/null +++ b/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/TestServiceExtension.java @@ -0,0 +1,36 @@ +/* + * 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 + * + */ + +package org.eclipse.tractusx.edc.lifecycle; + +import org.eclipse.edc.connector.transfer.spi.status.StatusCheckerRegistry; +import org.eclipse.edc.connector.transfer.spi.types.TransferProcess; +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; + +@Extension(value = "Extension used to inject dummy services into E2E runtimes") +public class TestServiceExtension implements ServiceExtension { + + @Inject + private StatusCheckerRegistry registry; + + @Override + public void initialize(ServiceExtensionContext context) { + // takes care that ongoing HTTP transfers are actually completed, otherwise they would + // always stay in the "STARTED" state + registry.register("HttpProxy", (tp, r) -> tp.getType() == TransferProcess.Type.CONSUMER); + } +} 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 022080a86..c3abfd832 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 @@ -14,4 +14,5 @@ org.eclipse.tractusx.edc.lifecycle.ConsumerServicesExtension org.eclipse.tractusx.edc.lifecycle.VaultSeedExtension +org.eclipse.tractusx.edc.lifecycle.TestServiceExtension diff --git a/edc-tests/runtime/runtime-postgresql/build.gradle.kts b/edc-tests/runtime/runtime-postgresql/build.gradle.kts index 85a03d538..fd07930c8 100644 --- a/edc-tests/runtime/runtime-postgresql/build.gradle.kts +++ b/edc-tests/runtime/runtime-postgresql/build.gradle.kts @@ -28,8 +28,7 @@ dependencies { exclude(module = "ssi-miw-credential-client") exclude(module = "ssi-identity-extractor") exclude(module = "cx-policy") - exclude(module = "data-encryption") - exclude(module = "hashicorp-vault") + exclude(group = "org.eclipse.edc", "vault-hashicorp") } implementation(project(":edc-tests:runtime:extensions")) diff --git a/gradle.properties b/gradle.properties index 5925f9b86..51f52611d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ group=org.eclipse.tractusx.edc -version=0.5.0 +version=0.5.1 # configure the build: -annotationProcessorVersion=0.1.3 -edcGradlePluginsVersion=0.1.3 -metaModelVersion=0.1.3 +annotationProcessorVersion=0.2.1 +edcGradlePluginsVersion=0.2.1 +metaModelVersion=0.2.1 txScmConnection=scm:git:git@github.com:eclipse-tractusx/tractusx-edc.git txWebsiteUrl=https://github.com/eclipse-tractusx/tractusx-edc.git txScmUrl=https://github.com/eclipse-tractusx/tractusx-edc.git diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4390f08a4..d66c670b3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,27 +2,27 @@ format.version = "1.1" [versions] -edc = "0.1.3" +edc = "0.2.1" postgres = "42.6.0" awaitility = "4.2.0" nimbus = "9.31" -azure-identity = "1.9.1" +azure-identity = "1.10.0" slf4j = "2.0.7" okhttp = "4.11.0" mockwebserver = "5.0.0-alpha.11" -bouncyCastle-jdk18on = "1.75" +bouncyCastle-jdk18on = "1.76" mockito = "5.2.0" restAssured = "5.3.1" apache-sshd = "2.10.0" testcontainers = "1.18.3" -aws = "2.20.98" +aws = "2.20.130" rsApi = "3.1.0" -jupiter = "5.9.3" +jupiter = "5.10.0" assertj = "3.24.2" titanium = "1.3.2" jackson = "2.15.2" jakarta-json = "2.0.1" -tink = "1.9.0" +tink = "1.10.0" iron-vc = "0.8.1" [libraries] @@ -48,12 +48,16 @@ edc-boot = { module = "org.eclipse.edc:boot", version.ref = "edc" } edc-config-filesystem = { module = "org.eclipse.edc:configuration-filesystem", version.ref = "edc" } edc-jsonld = { module = "org.eclipse.edc:json-ld", version.ref = "edc" } edc-vault-filesystem = { module = "org.eclipse.edc:vault-filesystem", version.ref = "edc" } +edc-vault-hashicorp = { module = "org.eclipse.edc:vault-hashicorp", version.ref = "edc" } edc-core-controlplane = { module = "org.eclipse.edc:control-plane-core", version.ref = "edc" } edc-core-connector = { module = "org.eclipse.edc:connector-core", version.ref = "edc" } edc-core-jetty = { module = "org.eclipse.edc:jetty-core", version.ref = "edc" } 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-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-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,6 +78,7 @@ 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" } # implementations edc-sql-assetindex = { module = "org.eclipse.edc:asset-index-sql", version.ref = "edc" } @@ -133,6 +138,7 @@ restAssured = { module = "io.rest-assured:rest-assured", version.ref = "restAssu 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" } testcontainers-junit = { module = "org.testcontainers:junit-jupiter", version.ref = "testcontainers" } +testcontainers-postgres = { module = "org.testcontainers:postgresql", version.ref = "testcontainers" } aws-s3 = { module = "software.amazon.awssdk:s3", version.ref = "aws" } jakarta-rsApi = { module = "jakarta.ws.rs:jakarta.ws.rs-api", version.ref = "rsApi" } jakartaJson = { module = "org.glassfish:jakarta.json", version.ref = "jakarta-json" } diff --git a/resources/openapi/yaml/control-plane-adapter-api.yaml b/resources/openapi/yaml/control-plane-adapter-api.yaml deleted file mode 100644 index 75eec75a5..000000000 --- a/resources/openapi/yaml/control-plane-adapter-api.yaml +++ /dev/null @@ -1,415 +0,0 @@ -openapi: 3.0.1 -paths: - /edrs: - get: - description: Returns all EndpointDataReference entry according to a query - operationId: queryEdrs - parameters: - - in: query - name: assetId - schema: - type: string - example: null - - in: query - name: agreementId - schema: - type: string - example: null - responses: - "200": - content: - application/json: - schema: - type: array - example: null - items: - $ref: '#/components/schemas/EndpointDataReferenceEntry' - "400": - content: - application/json: - schema: - type: array - example: null - items: - $ref: '#/components/schemas/ApiErrorDetail' - description: Request was malformed - tags: - - Control Plane Adapter EDR Api - post: - 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. - operationId: initiateEdrNegotiation - requestBody: - content: - application/json: - schema: - type: object - additionalProperties: - $ref: '#/components/schemas/NegotiateEdrRequestDto' - example: null - properties: - empty: - type: boolean - example: null - valueType: - type: string - enum: - - ARRAY - - OBJECT - - STRING - - NUMBER - - "TRUE" - - "FALSE" - - "NULL" - example: null - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/IdResponseDto' - description: The negotiation was successfully initiated. - "400": - content: - application/json: - schema: - type: array - example: null - items: - $ref: '#/components/schemas/ApiErrorDetail' - description: Request body was malformed - tags: - - Control Plane Adapter EDR Api - /edrs/{id}: - get: - description: Gets an EDR with the given transfer process ID) - operationId: getEdr - parameters: - - in: path - name: id - required: true - schema: - type: string - example: null - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/DataAddressDto' - description: The EDR cached - "400": - content: - application/json: - schema: - type: array - example: null - items: - $ref: '#/components/schemas/ApiErrorDetail' - description: "Request was malformed, e.g. id was null" - "404": - content: - application/json: - schema: - type: array - example: null - items: - $ref: '#/components/schemas/ApiErrorDetail' - description: An EDR with the given ID does not exist - tags: - - Control Plane Adapter EDR Api -components: - schemas: - Action: - type: object - example: null - properties: - constraint: - $ref: '#/components/schemas/Constraint' - includedIn: - type: string - example: null - type: - type: string - example: null - ApiErrorDetail: - type: object - example: null - properties: - invalidValue: - type: object - example: null - message: - type: string - example: null - path: - type: string - example: null - type: - type: string - example: null - CallbackAddress: - type: object - example: null - properties: - authCodeId: - type: string - example: null - authKey: - type: string - example: null - events: - type: array - example: null - items: - type: string - example: null - uniqueItems: true - transactional: - type: boolean - example: null - uri: - type: string - example: null - Constraint: - type: object - discriminator: - propertyName: edctype - example: null - properties: - edctype: - type: string - example: null - required: - - edctype - ContractOfferDescription: - type: object - example: null - properties: - assetId: - type: string - example: null - offerId: - type: string - example: null - policy: - $ref: '#/components/schemas/Policy' - DataAddressDto: - type: object - example: null - properties: - '@context': - type: object - example: null - '@type': - type: string - example: null - properties: - type: object - additionalProperties: - type: string - example: null - example: null - Duty: - type: object - example: null - properties: - action: - $ref: '#/components/schemas/Action' - assignee: - type: string - example: null - assigner: - type: string - example: null - consequence: - $ref: '#/components/schemas/Duty' - constraints: - type: array - example: null - items: - $ref: '#/components/schemas/Constraint' - parentPermission: - $ref: '#/components/schemas/Permission' - target: - type: string - example: null - EndpointDataReferenceEntry: - type: object - example: null - properties: - agreementId: - type: string - example: null - assetId: - type: string - example: null - transferProcessId: - type: string - example: null - IdResponseDto: - type: object - example: null - properties: - '@context': - type: object - example: null - '@id': - type: string - example: null - '@type': - type: string - example: null - createdAt: - type: integer - format: int64 - example: null - JsonObject: - type: object - additionalProperties: - $ref: '#/components/schemas/JsonValue' - example: null - properties: - empty: - type: boolean - example: null - valueType: - type: string - enum: - - ARRAY - - OBJECT - - STRING - - NUMBER - - "TRUE" - - "FALSE" - - "NULL" - example: null - JsonValue: - type: object - example: null - properties: - valueType: - type: string - enum: - - ARRAY - - OBJECT - - STRING - - NUMBER - - "TRUE" - - "FALSE" - - "NULL" - example: null - NegotiateEdrRequestDto: - type: object - example: null - properties: - callbackAddresses: - type: array - example: null - items: - $ref: '#/components/schemas/CallbackAddress' - connectorAddress: - type: string - example: null - connectorId: - type: string - example: null - offer: - $ref: '#/components/schemas/ContractOfferDescription' - protocol: - type: string - example: null - providerId: - type: string - example: null - Permission: - type: object - example: null - properties: - action: - $ref: '#/components/schemas/Action' - assignee: - type: string - example: null - assigner: - type: string - example: null - constraints: - type: array - example: null - items: - $ref: '#/components/schemas/Constraint' - duties: - type: array - example: null - items: - $ref: '#/components/schemas/Duty' - target: - type: string - example: null - Policy: - type: object - example: null - properties: - '@type': - type: string - enum: - - SET - - OFFER - - CONTRACT - example: null - assignee: - type: string - example: null - assigner: - type: string - example: null - extensibleProperties: - type: object - additionalProperties: - type: object - example: null - example: null - inheritsFrom: - type: string - example: null - obligations: - type: array - example: null - items: - $ref: '#/components/schemas/Duty' - permissions: - type: array - example: null - items: - $ref: '#/components/schemas/Permission' - prohibitions: - type: array - example: null - items: - $ref: '#/components/schemas/Prohibition' - target: - type: string - example: null - Prohibition: - type: object - example: null - properties: - action: - $ref: '#/components/schemas/Action' - assignee: - type: string - example: null - assigner: - type: string - example: null - constraints: - type: array - example: null - items: - $ref: '#/components/schemas/Constraint' - target: - type: string - example: null diff --git a/resources/openapi/yaml/control-plane-adapter.yaml b/resources/openapi/yaml/control-plane-adapter.yaml deleted file mode 100644 index c54839524..000000000 --- a/resources/openapi/yaml/control-plane-adapter.yaml +++ /dev/null @@ -1,40 +0,0 @@ -openapi: 3.0.1 -paths: - /adapter/asset/sync/{assetId}: - get: - operationId: getAssetSynchronous - parameters: - - in: path - name: assetId - required: true - schema: - type: string - example: null - - in: query - name: providerUrl - schema: - type: string - example: null - - in: query - name: contractAgreementId - schema: - type: string - example: null - - in: query - name: contractAgreementReuse - schema: - type: boolean - default: true - example: null - - in: query - name: timeout - schema: - type: string - example: null - responses: - default: - content: - application/json: {} - description: default response - tags: - - Control Plane Adapter diff --git a/resources/openapi/yaml/edc-dataplane-proxy-consumer-api.yaml b/resources/openapi/yaml/edc-dataplane-proxy-consumer-api.yaml deleted file mode 100644 index 861d83730..000000000 --- a/resources/openapi/yaml/edc-dataplane-proxy-consumer-api.yaml +++ /dev/null @@ -1,34 +0,0 @@ -openapi: 3.0.1 -paths: - /aas/request: - post: - operationId: requestAsset - requestBody: - content: - '*/*': - schema: - $ref: '#/components/schemas/AssetRequest' - responses: - default: - content: - application/json: - schema: - $ref: '#/components/schemas/AssetRequest' - description: Requests asset data - tags: - - Data Plane Proxy API -components: - schemas: - AssetRequest: - type: object - example: null - properties: - assetId: - type: string - example: null - endpointUrl: - type: string - example: null - transferProcessId: - type: string - example: null diff --git a/resources/openapi/yaml/edc-dataplane-proxy-provider-api.yaml b/resources/openapi/yaml/edc-dataplane-proxy-provider-api.yaml deleted file mode 100644 index 1a55c51b7..000000000 --- a/resources/openapi/yaml/edc-dataplane-proxy-provider-api.yaml +++ /dev/null @@ -1,12 +0,0 @@ -openapi: 3.0.1 -paths: - /gateway/{paths}: - get: - operationId: requestAsset - responses: - default: - content: - application/json: {} - description: Gets asset data - tags: - - Data Plane Proxy API diff --git a/resources/openapi/yaml/observability-api-customization.yaml b/resources/openapi/yaml/observability-api-customization.yaml deleted file mode 100644 index 063a3122a..000000000 --- a/resources/openapi/yaml/observability-api-customization.yaml +++ /dev/null @@ -1,105 +0,0 @@ -openapi: 3.0.1 -paths: - /check/health: - get: - description: Performs a health check to determine whether the runtime is working - properly. - operationId: checkHealth - responses: - "200": - content: - application/json: - schema: - type: array - example: null - items: - $ref: '#/components/schemas/HealthStatus' - tags: - - Application Observability - /check/liveness: - get: - description: Performs a liveness probe to determine whether the runtime is working - properly. - operationId: getLiveness - responses: - "200": - content: - application/json: - schema: - type: array - example: null - items: - $ref: '#/components/schemas/HealthStatus' - tags: - - Application Observability - /check/readiness: - get: - description: Performs a readiness probe to determine whether the runtime is - able to accept requests. - operationId: getReadiness - responses: - "200": - content: - application/json: - schema: - type: array - example: null - items: - $ref: '#/components/schemas/HealthStatus' - tags: - - Application Observability - /check/startup: - get: - description: Performs a startup probe to determine whether the runtime has completed - startup. - operationId: getStartup - responses: - "200": - content: - application/json: - schema: - type: array - example: null - items: - $ref: '#/components/schemas/HealthStatus' - tags: - - Application Observability -components: - schemas: - Failure: - type: object - example: null - properties: - failureDetail: - type: string - example: null - messages: - type: array - example: null - items: - type: string - example: null - HealthCheckResult: - type: object - example: null - properties: - component: - type: string - example: null - failure: - $ref: '#/components/schemas/Failure' - isHealthy: - type: boolean - example: null - HealthStatus: - type: object - example: null - properties: - componentResults: - type: array - example: null - items: - $ref: '#/components/schemas/HealthCheckResult' - isSystemHealthy: - type: boolean - example: null diff --git a/samples/multi-tenancy/README.md b/samples/multi-tenancy/README.md new file mode 100644 index 000000000..3cc7e0dd2 --- /dev/null +++ b/samples/multi-tenancy/README.md @@ -0,0 +1,124 @@ +# Multi Tenancy + +This sample show how to create a custom runtime to run multiple EDC tenants in a single java process. + +## How it works + +In a Java Runtime, multiple "sub-runtimes" with dedicated classloader can be launched in parallel, giving object-level +separation (an object instantiated by a sub-runtime cannot be accessed by another sub-runtime). + +## How to use + +The module provides an extension of the `BaseRuntime` class called `MultiTenantRuntime`. +This class can be set in the `build.gradle.kts` as the main class: + +```kotlin +application { + mainClass.set("org.eclipse.tractusx.edc.samples.multitenancy.MultiTenantRuntime") +} +``` + +This runtime looks for a properties file which path can be specified with the `edc.tenants.path` property. + +In this file the tenants are defined through settings, e.g.: + +```properties +edc.tenants.tenant1.edc.fs.config=/config/path +edc.tenants.tenant2.edc.fs.config=/config/path +edc.tenants.tenant3.edc.fs.config=/config/path +``` + +Using this file the EDC will run with 3 tenants: `tenant1`, `tenant2` and `tenant3`, every one with their respective +configuration file. +Everything that stays after the tenant name in the setting key will be loaded in the tenant runtime, so *theoretically* +(but not recommended) you could define all the tenants configuration in the tenants properties file: + +```properties +edc.tenants.tenant1.web.http.port=18181 +edc.tenants.tenant1.any.other.setting=value +edc.tenants.tenant2.web.http.port=28181 +edc.tenants.tenant3.web.http.port=38181 +``` + +## Sample + +Build: + +```shell +./gradlew :samples:multi-tenancy:build +``` + +Run: + +```shell +java -jar -Dedc.tenants.path=samples/multi-tenancy/tenants.properties samples/multi-tenancy/build/libs/multitenant.jar +``` + +Create a PolicyDefinition on `first` tenant: + +```shell +curl -X POST http://localhost:18183/management/v2/policydefinitions \ + --header 'Content-Type: application/json' \ + --data '{ + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "policy": { + "@context": "http://www.w3.org/ns/odrl.jsonld", + "@type": "set", + "permission": [], + "prohibition": [], + "obligation": [] + } + } + ' +``` + +Get `first` tenant policy definitions: + +```shell +curl -X POST http://localhost:18183/management/v2/policydefinitions/request +``` + +Will get a list containing the PolicyDefinition we created: + +```json +[ + { + "@id": "f48f2e27-c385-4846-b8b8-112c08bfa424", + "@type": "edc:PolicyDefinition", + "edc:createdAt": 1691147860257, + "edc:policy": { + "@id": "898fa3d6-b488-4f5f-9a41-4fb4b9229813", + "@type": "odrl:Set", + "odrl:permission": [], + "odrl:prohibition": [], + "odrl:obligation": [] + }, + "@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/" + } + } +] +``` + +`second` and `third` tenants will have no policy definitions: + +```shell +curl -X POST http://localhost:28183/management/v2/policydefinitions/request +``` + +and + +```shell +curl -X POST http://localhost:38183/management/v2/policydefinitions/request +``` + +will return + +```json +[] +``` diff --git a/samples/multi-tenancy/build.gradle.kts b/samples/multi-tenancy/build.gradle.kts new file mode 100644 index 000000000..2303cf86d --- /dev/null +++ b/samples/multi-tenancy/build.gradle.kts @@ -0,0 +1,46 @@ +/* + * 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 + * + */ + +plugins { + `java-library` + id("application") + id("com.github.johnrengelman.shadow") version "8.1.1" +} + + +dependencies { + implementation(libs.edc.boot) + 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") + } + implementation(libs.edc.iam.mock) + implementation(libs.edc.core.controlplane) +} + +application { + mainClass.set("org.eclipse.tractusx.edc.samples.multitenancy.MultiTenantRuntime") +} + +tasks.withType { + mergeServiceFiles() + archiveFileName.set("multitenant.jar") +} + +// do not publish +edcBuild { + publish.set(false) +} diff --git a/samples/multi-tenancy/config/first.properties b/samples/multi-tenancy/config/first.properties new file mode 100644 index 000000000..4b7a2f36d --- /dev/null +++ b/samples/multi-tenancy/config/first.properties @@ -0,0 +1,9 @@ +web.http.port=18181 +web.http.path=/ +web.http.protocol.port=18182 +web.http.protocol.path=/protocol +web.http.management.port=18183 +web.http.management.path=/management +web.http.control.port=18184 +web.http.control.path=/control +edc.dsp.callback.address=http://localhost:18182 diff --git a/samples/multi-tenancy/config/second.properties b/samples/multi-tenancy/config/second.properties new file mode 100644 index 000000000..7abd9f8b7 --- /dev/null +++ b/samples/multi-tenancy/config/second.properties @@ -0,0 +1,9 @@ +web.http.port=28181 +web.http.path=/ +web.http.protocol.port=28182 +web.http.protocol.path=/protocol +web.http.management.port=28183 +web.http.management.path=/management +web.http.control.port=28184 +web.http.control.path=/control +edc.dsp.callback.address=http://localhost:28182 diff --git a/samples/multi-tenancy/config/third.properties b/samples/multi-tenancy/config/third.properties new file mode 100644 index 000000000..1fdd8593b --- /dev/null +++ b/samples/multi-tenancy/config/third.properties @@ -0,0 +1,9 @@ +web.http.port=38181 +web.http.path=/ +web.http.protocol.port=38182 +web.http.protocol.path=/protocol +web.http.management.port=38183 +web.http.management.path=/management +web.http.control.port=38184 +web.http.control.path=/control +edc.dsp.callback.address=http://localhost:38182 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 new file mode 100644 index 000000000..b9ddbec27 --- /dev/null +++ b/samples/multi-tenancy/src/main/java/org/eclipse/tractusx/edc/samples/multitenancy/MultiTenantRuntime.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022 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 implementation + * + */ + +package org.eclipse.tractusx.edc.samples.multitenancy; + +import org.eclipse.edc.boot.system.runtime.BaseRuntime; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.spi.system.configuration.ConfigFactory; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Properties; + +import static java.lang.ClassLoader.getSystemClassLoader; + +public class MultiTenantRuntime extends BaseRuntime { + + private final @NotNull Monitor monitor = createMonitor(); + + public static void main(String[] args) { + var runtime = new MultiTenantRuntime(); + runtime.boot(); + } + + protected void boot() { + loadTenantsConfig().getConfig("edc.tenants").partition().forEach(this::bootTenant); + } + + private void bootTenant(Config tenantConfig) { + var baseProperties = System.getProperties(); + tenantConfig.getRelativeEntries().forEach(System::setProperty); + var classPathEntries = Arrays.stream(System.getProperty("java.class.path").split(File.pathSeparator)) + .map(this::toUrl) + .toArray(URL[]::new); + + Thread runtimeThread = null; + try (var classLoader = URLClassLoader.newInstance(classPathEntries, getSystemClassLoader())) { + runtimeThread = new Thread(() -> { + try { + Thread.currentThread().setContextClassLoader(classLoader); + super.boot(); + } catch (Exception e) { + throw new EdcException(e); + } + }); + + monitor.info("Starting tenant " + tenantConfig.currentNode()); + runtimeThread.start(); + + runtimeThread.join(20_000); + + } catch (InterruptedException e) { + runtimeThread.interrupt(); + throw new EdcException(e); + } catch (IOException e) { + throw new EdcException(e); + } finally { + System.setProperties(baseProperties); + } + } + + @NotNull + private Config loadTenantsConfig() { + var tenantsPath = System.getProperty("edc.tenants.path"); + if (tenantsPath == null) { + throw new EdcException("No edc.tenants.path mandatory property provided"); + } + try (var is = Files.newInputStream(Path.of(tenantsPath))) { + var properties = new Properties(); + properties.load(is); + return ConfigFactory.fromProperties(properties); + } catch (IOException e) { + throw new EdcException(e); + } + } + + private URL toUrl(String entry) { + try { + return new File(entry).toURI().toURL(); + } catch (MalformedURLException e) { + throw new EdcException(e); + } + } +} diff --git a/samples/multi-tenancy/src/test/java/org/eclipse/tractusx/edc/samples/multitenancy/MultiTenantRuntimeTest.java b/samples/multi-tenancy/src/test/java/org/eclipse/tractusx/edc/samples/multitenancy/MultiTenantRuntimeTest.java new file mode 100644 index 000000000..5cd49157e --- /dev/null +++ b/samples/multi-tenancy/src/test/java/org/eclipse/tractusx/edc/samples/multitenancy/MultiTenantRuntimeTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022 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 implementation + * + */ + +package org.eclipse.tractusx.edc.samples.multitenancy; + +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatcher; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +class MultiTenantRuntimeTest { + + private final Monitor monitor = mock(); + private final MultiTenantRuntime runtime = + new MultiTenantRuntime() { + @Override + protected @NotNull Monitor createMonitor() { + return monitor; + } + }; + + @Test + void throwsExceptionIfNoTenantsPropertyProvided() { + assertThrows(EdcException.class, runtime::boot); + verify(monitor, never()).info(argThat(connectorIsReady())); + } + + @Test + void throwsExceptionIfTenantsFileDoesNotExist() { + System.setProperty("edc.tenants.path", "unexistentfile"); + + assertThrows(EdcException.class, runtime::boot); + verify(monitor, never()).info(argThat(connectorIsReady())); + } + + @Test + void threadForEveryTenant() { + System.setProperty("edc.tenants.path", "./src/test/resources/tenants.properties"); + + runtime.boot(); + + verify(monitor, times(2)).info(argThat(connectorIsReady())); + } + + @NotNull + private ArgumentMatcher connectorIsReady() { + return message -> message.endsWith(" ready"); + } + +} diff --git a/samples/multi-tenancy/src/test/resources/tenants.properties b/samples/multi-tenancy/src/test/resources/tenants.properties new file mode 100644 index 000000000..0362cfa59 --- /dev/null +++ b/samples/multi-tenancy/src/test/resources/tenants.properties @@ -0,0 +1,10 @@ +edc.tenants.one.edc.any=any +edc.tenants.one.web.http.port=18181 +edc.tenants.one.web.http.path=/api +edc.tenants.one.web.http.protocol.port=18282 +edc.tenants.one.web.http.protocol.path=/protocol +edc.tenants.two.edc.any=any +edc.tenants.two.web.http.port=28181 +edc.tenants.two.web.http.path=/api +edc.tenants.two.web.http.protocol.port=28282 +edc.tenants.two.web.http.protocol.path=/protocol diff --git a/samples/multi-tenancy/tenants.properties b/samples/multi-tenancy/tenants.properties new file mode 100644 index 000000000..1f1b15a5e --- /dev/null +++ b/samples/multi-tenancy/tenants.properties @@ -0,0 +1,3 @@ +edc.tenants.first.edc.fs.config=samples/multi-tenancy/config/first.properties +edc.tenants.second.edc.fs.config=samples/multi-tenancy/config/second.properties +edc.tenants.third.edc.fs.config=samples/multi-tenancy/config/third.properties diff --git a/settings.gradle.kts b/settings.gradle.kts index 2c58f3df7..86486df88 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,30 +20,34 @@ rootProject.name = "tractusx-edc" // spi modules -include(":spi:control-plane-adapter-spi") -include(":spi:edr-cache-spi") +include(":spi:callback-spi") +include(":spi:edr-spi") include(":spi:core-spi") include(":spi:ssi-spi") // core modules include(":core:edr-cache-core") +include(":core:edr-core") include(":core:json-ld-core") -include(":edc-extensions:business-partner-validation") +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:cx-oauth2") include(":edc-extensions:data-encryption") include(":edc-extensions:dataplane-selector-configuration") -include(":edc-extensions:hashicorp-vault") include(":edc-extensions:postgresql-migration") 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:control-plane-adapter-api") -include(":edc-extensions:control-plane-adapter-callback") -include(":edc-extensions:edr-cache-sql") +include(":edc-extensions:edr:edr-api") +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") @@ -65,7 +69,6 @@ include(":edc-controlplane:edc-runtime-memory") include(":edc-controlplane:edc-controlplane-memory-hashicorp-vault") include(":edc-controlplane:edc-controlplane-postgresql-azure-vault") include(":edc-controlplane:edc-controlplane-postgresql-hashicorp-vault") -include(":edc-controlplane:edc-controlplane-postgresql-hashicorp-vault-legacy") // modules for dataplane artifacts include(":edc-dataplane") @@ -78,6 +81,8 @@ include(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-core") include(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-api") include(":edc-tests:edc-dataplane-proxy-e2e") +include(":samples:multi-tenancy") + // this is needed to have access to snapshot builds of plugins pluginManagement { diff --git a/spi/control-plane-adapter-spi/build.gradle.kts b/spi/callback-spi/build.gradle.kts similarity index 93% rename from spi/control-plane-adapter-spi/build.gradle.kts rename to spi/callback-spi/build.gradle.kts index 80ee806b2..f13496ffb 100644 --- a/spi/control-plane-adapter-spi/build.gradle.kts +++ b/spi/callback-spi/build.gradle.kts @@ -20,7 +20,7 @@ plugins { dependencies { implementation(project(":spi:core-spi")) - implementation(project(":spi:edr-cache-spi")) + implementation(project(":spi:edr-spi")) implementation(libs.edc.spi.core) implementation(libs.edc.spi.contract) implementation(libs.edc.spi.aggregateservices) diff --git a/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/callback/InProcessCallback.java b/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallback.java similarity index 93% rename from spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/callback/InProcessCallback.java rename to spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallback.java index 12a6901fa..e7648161b 100644 --- a/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/callback/InProcessCallback.java +++ b/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallback.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.spi.cp.adapter.callback; +package org.eclipse.tractusx.edc.spi.callback; import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; import org.eclipse.edc.spi.event.Event; diff --git a/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/callback/InProcessCallbackRegistry.java b/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallbackRegistry.java similarity index 95% rename from spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/callback/InProcessCallbackRegistry.java rename to spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallbackRegistry.java index 2968530ae..32868b011 100644 --- a/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/callback/InProcessCallbackRegistry.java +++ b/spi/callback-spi/src/main/java/org/eclipse/tractusx/edc/spi/callback/InProcessCallbackRegistry.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.spi.cp.adapter.callback; +package org.eclipse.tractusx.edc.spi.callback; import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; 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 5d05db6ee..369c40fc4 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 @@ -18,6 +18,9 @@ public final class CoreConstants { public static final String TX_PREFIX = "tx"; public static final String TX_NAMESPACE = "https://w3id.org/tractusx/v0.0.1/ns/"; + public static final String TX_CONTEXT = "https://w3id.org/tractusx/edc/v0.0.1"; + public static final String EDC_CONTEXT = "https://w3id.org/edc/v0.0.1"; + private CoreConstants() { } diff --git a/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntry.java b/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntry.java deleted file mode 100644 index 6351bca79..000000000 --- a/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntry.java +++ /dev/null @@ -1,111 +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 - * - */ - -package org.eclipse.tractusx.edc.edr.spi; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; - -import java.util.Objects; - -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; - -/** - * An entry in the cache for an {@link EndpointDataReference}. - */ -@JsonDeserialize(builder = EndpointDataReferenceEntry.Builder.class) -public class EndpointDataReferenceEntry { - - public static final String SIMPLE_TYPE = "EndpointDataReferenceEntry"; - - public static final String EDR_ENTRY_TYPE = TX_NAMESPACE + SIMPLE_TYPE; - public static final String EDR_ENTRY_ASSET_ID = EDC_NAMESPACE + "assetId"; - public static final String EDR_ENTRY_AGREEMENT_ID = EDC_NAMESPACE + "agreementId"; - public static final String EDR_ENTRY_TRANSFER_PROCESS_ID = EDC_NAMESPACE + "transferProcessId"; - - private String assetId; - private String agreementId; - private String transferProcessId; - - private EndpointDataReferenceEntry() { - } - - public String getAssetId() { - return assetId; - } - - public String getAgreementId() { - return agreementId; - } - - public String getTransferProcessId() { - return 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); - } - - @Override - public int hashCode() { - return Objects.hash(assetId, agreementId, transferProcessId); - } - - @JsonPOJOBuilder(withPrefix = "") - public static class Builder { - private final EndpointDataReferenceEntry entry; - - private Builder() { - entry = new EndpointDataReferenceEntry(); - } - - @JsonCreator - public static Builder newInstance() { - return new Builder(); - } - - public Builder assetId(String assetId) { - entry.assetId = assetId; - return this; - } - - public Builder agreementId(String agreementId) { - entry.agreementId = agreementId; - return this; - } - - public Builder transferProcessId(String transferProcessId) { - entry.transferProcessId = transferProcessId; - return this; - } - - public EndpointDataReferenceEntry build() { - requireNonNull(entry.assetId, "assetId"); - requireNonNull(entry.agreementId, "agreementId"); - requireNonNull(entry.transferProcessId, "transferProcessId"); - return entry; - } - } - -} diff --git a/spi/edr-cache-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCacheBaseTest.java b/spi/edr-cache-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCacheBaseTest.java deleted file mode 100644 index dddea7775..000000000 --- a/spi/edr-cache-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCacheBaseTest.java +++ /dev/null @@ -1,130 +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 - * - */ - -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.junit.jupiter.api.Test; - -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static java.util.UUID.randomUUID; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.tractusx.edc.edr.spi.TestFunctions.edr; -import static org.eclipse.tractusx.edc.edr.spi.TestFunctions.edrEntry; - -public abstract class EndpointDataReferenceCacheBaseTest { - - protected abstract EndpointDataReferenceCache getStore(); - - @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); - assertThat(edrs.size()).isEqualTo(1); - assertThat(edrs.get((0)).getId()).isEqualTo(edrId); - - } - - - @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 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())).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); - } - -} diff --git a/spi/edr-cache-spi/build.gradle.kts b/spi/edr-spi/build.gradle.kts similarity index 83% rename from spi/edr-cache-spi/build.gradle.kts rename to spi/edr-spi/build.gradle.kts index a08b24ee3..e170eafc3 100644 --- a/spi/edr-cache-spi/build.gradle.kts +++ b/spi/edr-spi/build.gradle.kts @@ -20,10 +20,13 @@ plugins { dependencies { implementation(project(":spi:core-spi")) implementation(libs.edc.spi.core) + implementation(libs.edc.spi.contract) + implementation(libs.edc.spi.aggregateservices) testFixturesImplementation(libs.edc.junit) testFixturesImplementation(libs.junit.jupiter.api) testFixturesImplementation(libs.assertj) - + testFixturesImplementation(libs.awaitility) + } 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 new file mode 100644 index 000000000..42e68139b --- /dev/null +++ b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EdrManager.java @@ -0,0 +1,37 @@ +/* + * 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 + * + */ + +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/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/service/AdapterTransferProcessService.java b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/service/EdrService.java similarity index 73% rename from spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/service/AdapterTransferProcessService.java rename to spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/service/EdrService.java index 3ff0b50e9..54608b005 100644 --- a/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/service/AdapterTransferProcessService.java +++ b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/service/EdrService.java @@ -12,20 +12,23 @@ * */ -package org.eclipse.tractusx.edc.spi.cp.adapter.service; +package org.eclipse.tractusx.edc.edr.spi.service; import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; +import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; import org.eclipse.edc.service.spi.result.ServiceResult; +import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; -import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; +import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry; +import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest; import java.util.List; /** * Service for opening a transfer process. */ -public interface AdapterTransferProcessService { +@ExtensionPoint +public interface EdrService { /** * Open a transfer process by firing a contract negotiation. Implementors should fire a contract negotiation @@ -44,6 +47,8 @@ public interface AdapterTransferProcessService { */ ServiceResult findByTransferProcessId(String transferProcessId); - ServiceResult> findByAssetAndAgreement(String assetId, String agreementId); + ServiceResult> findBy(QuerySpec querySpec); + + ServiceResult deleteByTransferProcessId(String transferProcessId); } diff --git a/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCache.java b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/store/EndpointDataReferenceCache.java similarity index 58% rename from spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCache.java rename to spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/store/EndpointDataReferenceCache.java index bf0f3dc37..9dd5030a0 100644 --- a/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCache.java +++ b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/store/EndpointDataReferenceCache.java @@ -12,21 +12,26 @@ * */ -package org.eclipse.tractusx.edc.edr.spi; +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 { +public interface EndpointDataReferenceCache extends StateEntityStore { /** * Resolves an {@link EndpointDataReference} for the transfer process, returning null if one does not exist. @@ -34,11 +39,27 @@ public interface EndpointDataReferenceCache { @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); + 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}. @@ -51,9 +72,16 @@ public interface EndpointDataReferenceCache { */ 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 new file mode 100644 index 000000000..3e0b0a15f --- /dev/null +++ b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/EndpointDataReferenceEntry.java @@ -0,0 +1,215 @@ +/* + * 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 + * + */ + +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 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 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) + .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 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 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 new file mode 100644 index 000000000..5abf02697 --- /dev/null +++ b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/EndpointDataReferenceEntryStates.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 - 2022 Microsoft Corporation + * + * This program and the accompanying materials are 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: + * Microsoft Corporation - initial API and implementation + * Fraunhofer Institute for Software and Systems Engineering - minor modifications + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - improvements + * + */ + +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/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/model/NegotiateEdrRequest.java b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/NegotiateEdrRequest.java similarity index 98% rename from spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/model/NegotiateEdrRequest.java rename to spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/NegotiateEdrRequest.java index 846c16653..c6cab456c 100644 --- a/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/model/NegotiateEdrRequest.java +++ b/spi/edr-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/types/NegotiateEdrRequest.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.tractusx.edc.spi.cp.adapter.model; +package org.eclipse.tractusx.edc.edr.spi.types; import org.eclipse.edc.connector.contract.spi.types.offer.ContractOffer; import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; diff --git a/spi/edr-cache-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 similarity index 95% rename from spi/edr-cache-spi/src/test/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntryTest.java rename to spi/edr-spi/src/test/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntryTest.java index 75266e1bc..7095d3713 100644 --- a/spi/edr-cache-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 @@ -16,6 +16,7 @@ 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; 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 new file mode 100644 index 000000000..72d52ac44 --- /dev/null +++ b/spi/edr-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCacheTestBase.java @@ -0,0 +1,391 @@ +/* + * 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 + * + */ + +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().findByCorrelationIdAndLease(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_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-cache-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 similarity index 66% rename from spi/edr-cache-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/TestFunctions.java rename to spi/edr-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/TestFunctions.java index bd9a0b221..69f07ecca 100644 --- a/spi/edr-cache-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 @@ -15,6 +15,12 @@ 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 { @@ -28,10 +34,16 @@ public static EndpointDataReference edr(String id) { } 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, EndpointDataReferenceEntryStates state) { return EndpointDataReferenceEntry.Builder.newInstance() .assetId(assetId) .agreementId(agreementId) .transferProcessId(transferProcessId) + .providerId(UUID.randomUUID().toString()) + .state(state.code()) .build(); } }