diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml index a252bf108..22b20f283 100644 --- a/.github/actions/publish-docker-image/action.yml +++ b/.github/actions/publish-docker-image/action.yml @@ -41,6 +41,10 @@ inputs: docker_tag: required: false description: 'additional docker tags' + do_push: + required: false + default: 'false' + description: 'whether or not to actually push the image' runs: using: "composite" steps: @@ -93,7 +97,7 @@ runs: file: ${{ inputs.rootDir }}/src/main/docker/Dockerfile build-args: | JAR=${{ inputs.rootDir }}/build/libs/${{ inputs.imagename }}.jar - push: ${{ github.event_name != 'pull_request' }} + push: ${{ inputs.do_push == 'true' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/actions/run-deployment-test/action.yml b/.github/actions/run-deployment-test/action.yml index fafff52b2..1a84914d7 100644 --- a/.github/actions/run-deployment-test/action.yml +++ b/.github/actions/run-deployment-test/action.yml @@ -39,6 +39,10 @@ inputs: required: true description: "The directory that contains the docker file, e.g. edc-controlplane/edc-runtime-memory" + values_file: + required: true + description: "A yaml file that contains the values for the test installation. will be modified!" + runs: using: "composite" steps: @@ -73,12 +77,7 @@ runs: shell: bash run: |- sh -c "edc-tests/deployment/src/main/resources/prepare-test.sh \ - edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml" - - name: Install Infrastructure - shell: bash - run: |- - helm install infra edc-tests/deployment/src/main/resources/helm/test-infrastructure \ - --wait-for-jobs --timeout=30s --dependency-update + ${{ inputs.values_file }}" - name: Install Runtime shell: bash diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 8235015ed..fc27437c6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -93,6 +93,7 @@ jobs: imagename: ${{ matrix.variant.img }} docker_user: ${{ secrets.DOCKER_HUB_USER }} docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} + do_push: ${{ github.event_name != 'pull_request' }} publish-to-sonatype: name: "Publish artefacts to OSSRH Snapshots / MavenCentral" diff --git a/.github/workflows/deployment-test.yaml b/.github/workflows/deployment-test.yaml index 9474abcbe..7a7e63c8a 100644 --- a/.github/workflows/deployment-test.yaml +++ b/.github/workflows/deployment-test.yaml @@ -79,11 +79,12 @@ jobs: with: imagename: edc-runtime-memory rootDir: edc-controlplane/edc-runtime-memory + values_file: charts/tractusx-connector-memory/example.yaml helm_command: |- helm install tx-inmem charts/tractusx-connector-memory \ -f charts/tractusx-connector-memory/example.yaml \ --set vault.secrets="daps-crt:$(cat daps.cert);daps-key:$(cat daps.key)" \ - --wait-for-jobs --timeout=120s + --wait-for-jobs --timeout=120s --dependency-update # wait for the pod to become ready kubectl rollout status deployment tx-inmem @@ -102,6 +103,7 @@ jobs: with: imagename: "edc-controlplane-postgresql-hashicorp-vault edc-dataplane-hashicorp-vault" rootDir: "." + values_file: edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml helm_command: |- helm install tx-prod charts/tractusx-connector \ -f edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml \ @@ -131,6 +133,7 @@ jobs: with: imagename: "edc-controlplane-postgresql-azure-vault edc-dataplane-azure-vault" rootDir: "." + values_file: edc-tests/deployment/src/main/resources/helm/tractusx-connector-azure-vault-test.yaml helm_command: |- az keyvault secret set --vault-name ${{ secrets.AZURE_VAULT_NAME }} --name daps-crt --value "$(cat daps.cert)" > /dev/null az keyvault secret set --vault-name ${{ secrets.AZURE_VAULT_NAME }} --name daps-key --value "$(cat daps.key)" > /dev/null diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 1c080db15..ef04b6746 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -63,7 +63,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: Bump version in /charts - uses: mikefarah/yq@v4.33.3 + uses: mikefarah/yq@v4.34.1 with: cmd: |- find charts -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index da5787431..ecf936722 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -58,9 +58,10 @@ jobs: - uses: ./.github/actions/publish-docker-image name: Publish ${{ matrix.variant.img }} with: - docker_tag: ${{ needs.release-version.outputs.RELEASE_VERSION }} + docker_tag: ${{ inputs.docker_tag }} rootDir: ${{ matrix.variant.dir }}/${{ matrix.variant.img }} imagename: ${{ matrix.variant.img }} namespace: ${{ inputs.namespace }} docker_user: ${{ secrets.DOCKER_HUB_USER }} docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} + do_push: 'true' diff --git a/.github/workflows/publish-maven.yaml b/.github/workflows/publish-maven.yaml new file mode 100644 index 000000000..5624843f9 --- /dev/null +++ b/.github/workflows/publish-maven.yaml @@ -0,0 +1,68 @@ +# +# 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 +# + +--- +name: "Manually publish Maven Artefacts to OSSRH" + +on: + workflow_dispatch: + inputs: + version: + required: false + description: 'a semver string denoting the version. Append -SNAPSHOT for snapshots. If ommitted, the version is taken from gradle.properties' + +concurrency: + # cancel only running jobs on pull requests + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + maven-release: + name: 'Publish all artefacts to Sonatype/MavenCentral' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + # Set-Up + - uses: actions/checkout@v3.5.2 + - uses: ./.github/actions/setup-java + + # Import GPG Key + - uses: ./.github/actions/import-gpg-key + name: "Import GPG Key" + with: + gpg-private-key: ${{ secrets.ORG_GPG_PRIVATE_KEY }} + + # publish releases + - name: Publish version + env: + OSSRH_PASSWORD: ${{ secrets.ORG_OSSRH_PASSWORD }} + OSSRH_USER: ${{ secrets.ORG_OSSRH_USERNAME }} + run: |- + if [ -z ${{ inputs.version }} ]; + then + VERSION=$(./gradlew properties -q | grep "version:" | awk '{print $2}') + echo "Publishing using version from gradle.properties: $VERSION" + ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository --no-parallel -Pversion=$VERSION -Psigning.gnupg.executable=gpg -Psigning.gnupg.passphrase="${{ secrets.ORG_GPG_PASSPHRASE }}" + else + echo "Publishing using version from parameter: ${{ inputs.version }}" + ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository --no-parallel -Pversion=${{ inputs.version }} -Psigning.gnupg.executable=gpg -Psigning.gnupg.passphrase="${{ secrets.ORG_GPG_PASSPHRASE }}" + fi diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 9de73849f..089ec3ed8 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -111,14 +111,18 @@ jobs: steps: - uses: actions/checkout@v3.5.2 + - name: Export RELEASE_VERSION env + run: | + echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - uses: ./.github/actions/publish-docker-image name: Publish ${{ matrix.variant.img }} with: - docker_tag: ${{ needs.release-version.outputs.RELEASE_VERSION }} + docker_tag: ${{ env.RELEASE_VERSION }} rootDir: ${{ matrix.variant.dir }}/${{ matrix.variant.img }} imagename: ${{ matrix.variant.img }} docker_user: ${{ secrets.DOCKER_HUB_USER }} docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} + do_push: 'true' # Release: Helm Charts helm-release: diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index 310c78527..c2e276362 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -70,7 +70,6 @@ jobs: - name: Run Checkstyle run: | ./gradlew checkstyleMain checkstyleTest - echo "Running Checkstyle is currently a placeholder" markdown-lint: runs-on: ubuntu-latest @@ -126,4 +125,23 @@ jobs: - uses: ./.github/actions/setup-java - name: Run E2E tests - run: ./gradlew :edc-tests:runtime:build test -DincludeTags="EndToEndTest" + run: ./gradlew test -DincludeTags="EndToEndTest" + + postgres-tests: + runs-on: ubuntu-latest + needs: [ verify-formatting ] + + services: + postgres: + image: postgres:14.2 + ports: + - 5432:5432 + env: + POSTGRES_PASSWORD: password + + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/setup-java + + - name: Run Postgresql E2E tests + run: ./gradlew test -DincludeTags="PostgresqlIntegrationTest" diff --git a/CHANGELOG.md b/CHANGELOG.md index f3360a429..c57281d2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,20 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.4.0] - 2023-05-18 +## [0.4.1] - 2023-05-31 + +## [0.4.1] - 2023-05-31 + +### Added + +- SQL implementation for the EDR Cache +- E2E test variant using PostgreSQL +- Documentation + +### Changed + +- Moved to Java 17 +- Switched to Eclipse Dataspace Components `0.1.0` + +### Removed + +- Lombok ## [0.4.0] - 2023-05-18 -## Added +### Added - Support for the new Dataspace Protocol - GitHub Workflow to check for missing license headers -## Changed +### Changed - Switched to Eclipse Dataspace Components `0.0.1-milestone-9` -## Removed +### Removed - Business tests. All tests are covered by other means. - Control-Plane-Adapter. Replaced by a DSP-compatible implementation @@ -306,9 +323,9 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ## [0.0.1] - 2022-05-13 -[Unreleased]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.4.0...HEAD +[Unreleased]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.4.1...HEAD -[0.4.0]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.3...0.4.0 +[0.4.1]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.3...0.4.1 [0.3.3]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.2...0.3.3 diff --git a/build.gradle.kts b/build.gradle.kts index 953e3b4ab..50213a16b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,22 +19,21 @@ import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin +import java.time.Duration plugins { `java-library` `maven-publish` `jacoco-report-aggregation` - id("io.freefair.lombok") version "8.0.1" - id("com.diffplug.spotless") version "6.18.0" + id("com.diffplug.spotless") version "6.19.0" id("com.github.johnrengelman.shadow") version "8.1.1" id("com.bmuschko.docker-remote-api") version "9.3.1" + id("io.github.gradle-nexus.publish-plugin") version "1.3.0" } -val javaVersion: String by project val txScmConnection: String by project val txWebsiteUrl: String by project val txScmUrl: String by project -val groupId: String by project val annotationProcessorVersion: String by project val metaModelVersion: String by project @@ -57,13 +56,11 @@ project.subprojects.forEach { allprojects { apply(plugin = "org.eclipse.edc.edc-build") - apply(plugin = "io.freefair.lombok") repositories { mavenCentral() } dependencies { - implementation("org.projectlombok:lombok:1.18.26") 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 @@ -73,7 +70,7 @@ allprojects { implementation("org.yaml:snakeyaml:2.0") { because("version 1.33 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-1471.") } - implementation("net.minidev:json-smart:2.4.10") { + implementation("net.minidev:json-smart:2.4.11") { because("version 2.4.8 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-1370.") } } @@ -91,10 +88,9 @@ allprojects { metaModel.set(metaModelVersion) } - val gid = groupId pom { // this is actually important, so we can publish under the correct GID - groupId = gid + groupId = project.group.toString() projectName.set(project.name) description.set("edc :: ${project.name}") projectUrl.set(txWebsiteUrl) @@ -104,12 +100,12 @@ allprojects { swagger { title.set((project.findProperty("apiTitle") ?: "Tractus-X REST API") as String) description = - (project.findProperty("apiDescription") ?: "Tractus-X REST APIs - merged by OpenApiMerger") as String + (project.findProperty("apiDescription") + ?: "Tractus-X REST APIs - merged by OpenApiMerger") as String outputFilename.set(project.name) outputDirectory.set(file("${rootProject.projectDir.path}/resources/openapi/yaml")) resourcePackages = setOf("org.eclipse.tractusx.edc") } - javaLanguageVersion.set(JavaLanguageVersion.of(javaVersion)) } configure { @@ -149,7 +145,7 @@ allprojects { subprojects { afterEvaluate { if (project.plugins.hasPlugin("com.github.johnrengelman.shadow") && - file("${project.projectDir}/src/main/docker/Dockerfile").exists() + file("${project.projectDir}/src/main/docker/Dockerfile").exists() ) { //actually apply the plugin to the (sub-)project @@ -173,3 +169,10 @@ subprojects { } } } + +nexusPublishing { + transitionCheckOptions { + maxRetries.set(120) + delayBetween.set(Duration.ofSeconds(10)) + } +} \ No newline at end of file diff --git a/charts/tractusx-connector-azure-vault/Chart.yaml b/charts/tractusx-connector-azure-vault/Chart.yaml index 3169ba8e3..7b2802934 100644 --- a/charts/tractusx-connector-azure-vault/Chart.yaml +++ b/charts/tractusx-connector-azure-vault/Chart.yaml @@ -40,12 +40,25 @@ 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.4.0 +version: 0.4.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.4.0" +appVersion: "0.4.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 +dependencies: + # IDS Dynamic Attribute Provisioning Service (IAM) + - name: daps + version: 0.0.1 + repository: "file://./subcharts/omejdn" + alias: daps + condition: install.daps + # PostgreSQL + - name: postgresql + alias: postgresql + version: 12.1.6 + repository: https://charts.bitnami.com/bitnami + condition: install.postgresql diff --git a/charts/tractusx-connector-azure-vault/README.md b/charts/tractusx-connector-azure-vault/README.md index ec46cbe07..d13d18514 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.4.0](https://img.shields.io/badge/Version-0.4.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.0](https://img.shields.io/badge/AppVersion-0.4.0-informational?style=flat-square) +![Version: 0.4.1](https://img.shields.io/badge/Version-0.4.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.1](https://img.shields.io/badge/AppVersion-0.4.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. @@ -38,7 +38,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.4.0 \ +helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0.4.1 \ -f /tractusx-connector-azure-vault-test.yaml \ --set vault.azure.name=$AZURE_VAULT_NAME \ --set vault.azure.client=$AZURE_CLIENT_ID \ @@ -52,6 +52,13 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri * +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| file://./subcharts/omejdn | daps(daps) | 0.0.1 | +| https://charts.bitnami.com/bitnami | postgresql(postgresql) | 12.1.6 | + ## Values | Key | Type | Default | Description | @@ -67,7 +74,7 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri | 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},"observability":{"insecure":true,"path":"/observability","port":8085},"protocol":{"path":"/api/v1/ids","port":8084}}` | endpoints of the control plane | +| controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"","path":"/management","port":8081},"metrics":{"path":"/metrics","port":9090},"observability":{"insecure":true,"path":"/observability","port":8085},"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 | @@ -85,8 +92,8 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri | controlplane.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | | controlplane.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | | controlplane.endpoints.observability.port | int | `8085` | port for incoming API calls | -| controlplane.endpoints.protocol | object | `{"path":"/api/v1/ids","port":8084}` | ids api, used for inter connector communication and must be internet facing | -| controlplane.endpoints.protocol.path | string | `"/api/v1/ids"` | path 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 | `[]` | | @@ -100,7 +107,7 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri | 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 | `["ids"]` | EDC endpoints exposed by this ingress resource | +| 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 | @@ -160,6 +167,11 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri | 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.fullnameOverride | string | `"daps"` | | | daps.paths.jwks | string | `"/jwks.json"` | | | daps.paths.token | string | `"/token"` | | | daps.url | string | `""` | | @@ -184,6 +196,8 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri | dataplane.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | | dataplane.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | | dataplane.endpoints.observability.port | int | `8085` | port for incoming API calls | +| 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 | `{}` | | @@ -241,12 +255,20 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri | 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` | | | nameOverride | string | `""` | | +| participant.id | string | `""` | | +| postgresql.auth.database | string | `"edc"` | | +| postgresql.auth.password | string | `"password"` | | +| postgresql.auth.username | string | `"user"` | | | postgresql.enabled | bool | `false` | | +| postgresql.fullnameOverride | string | `"postgresql"` | | | postgresql.jdbcUrl | string | `""` | | -| postgresql.password | string | `""` | | -| postgresql.username | string | `""` | | +| postgresql.primary.persistence | string | `nil` | | +| 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) | diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/.helmignore b/charts/tractusx-connector-azure-vault/subcharts/omejdn/.helmignore similarity index 100% rename from edc-tests/deployment/src/main/resources/helm/omejdn/.helmignore rename to charts/tractusx-connector-azure-vault/subcharts/omejdn/.helmignore diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml b/charts/tractusx-connector-azure-vault/subcharts/omejdn/Chart.yaml similarity index 78% rename from edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml rename to charts/tractusx-connector-azure-vault/subcharts/omejdn/Chart.yaml index 0ac95d502..3e10aa1fc 100644 --- a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml +++ b/charts/tractusx-connector-azure-vault/subcharts/omejdn/Chart.yaml @@ -18,9 +18,8 @@ --- apiVersion: v2 -name: all-in-one +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 @@ -30,34 +29,12 @@ description: A Helm chart for Kubernetes # 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.1.0 - +version: 0.4.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: "1.16.0" - -dependencies: - # IDS Dynamic Attribute Provisioning Service (IAM) - - name: ids-daps - version: 0.0.1 - repository: "file://../omejdn" - alias: idsdaps - condition: install.daps - - # HashiCorp Vault - - name: vault - alias: vault - version: 0.20.0 - repository: https://helm.releases.hashicorp.com - - # PostgreSQL - - name: postgresql - alias: postgresql - version: 12.1.6 - repository: https://charts.bitnami.com/bitnami \ No newline at end of file +appVersion: "0.4.1" diff --git a/charts/tractusx-connector-azure-vault/subcharts/omejdn/README.md b/charts/tractusx-connector-azure-vault/subcharts/omejdn/README.md new file mode 100644 index 000000000..d23a9f9fa --- /dev/null +++ b/charts/tractusx-connector-azure-vault/subcharts/omejdn/README.md @@ -0,0 +1,39 @@ +# daps + +![Version: 0.4.1](https://img.shields.io/badge/Version-0.4.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.1](https://img.shields.io/badge/AppVersion-0.4.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/edc-tests/deployment/src/main/resources/helm/omejdn/templates/_helpers.tpl b/charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/_helpers.tpl similarity index 100% rename from edc-tests/deployment/src/main/resources/helm/omejdn/templates/_helpers.tpl rename to charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/_helpers.tpl diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/configmap.yaml b/charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/configmap.yaml similarity index 97% rename from edc-tests/deployment/src/main/resources/helm/omejdn/templates/configmap.yaml rename to charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/configmap.yaml index 97efcc12b..5ad21648d 100644 --- a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/configmap.yaml +++ b/charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/configmap.yaml @@ -31,7 +31,7 @@ data: omejdn.yml: |- --- - host: http://ids-daps:4567/ + host: http://daps:4567/ path_prefix: '' bind_to: 0.0.0.0 allow_origin: "*" @@ -41,7 +41,7 @@ data: - yaml user_backend_default: yaml accept_audience: idsc:IDS_CONNECTORS_ALL - issuer: http://ids-daps:4567/ + issuer: http://daps:4567/ environment: development default_audience: - idsc:IDS_CONNECTORS_ALL diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/deployment.yaml b/charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/deployment.yaml similarity index 100% rename from edc-tests/deployment/src/main/resources/helm/omejdn/templates/deployment.yaml rename to charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/deployment.yaml diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/hpa.yaml b/charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/hpa.yaml similarity index 100% rename from edc-tests/deployment/src/main/resources/helm/omejdn/templates/hpa.yaml rename to charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/hpa.yaml diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/imagepullsecret.yaml b/charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/imagepullsecret.yaml similarity index 100% rename from edc-tests/deployment/src/main/resources/helm/omejdn/templates/imagepullsecret.yaml rename to charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/imagepullsecret.yaml diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/service.yaml b/charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/service.yaml similarity index 100% rename from edc-tests/deployment/src/main/resources/helm/omejdn/templates/service.yaml rename to charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/service.yaml diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/serviceaccount.yaml b/charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/serviceaccount.yaml similarity index 100% rename from edc-tests/deployment/src/main/resources/helm/omejdn/templates/serviceaccount.yaml rename to charts/tractusx-connector-azure-vault/subcharts/omejdn/templates/serviceaccount.yaml diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/values.yaml b/charts/tractusx-connector-azure-vault/subcharts/omejdn/values.yaml similarity index 100% rename from edc-tests/deployment/src/main/resources/helm/omejdn/values.yaml rename to charts/tractusx-connector-azure-vault/subcharts/omejdn/values.yaml diff --git a/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml b/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml index b5bd4968e..3a8ed5fd7 100644 --- a/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml @@ -109,6 +109,12 @@ spec: {{- end }} {{- end }} + ######################## + ## ID CONFIGURATION ## + ######################## + - name: EDC_PARTICIPANT_ID + value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }} + ######################## ## DAPS CONFIGURATION ## ######################## @@ -154,31 +160,15 @@ spec: value: {{ .Values.controlplane.endpoints.observability.insecure | quote }} ######### - ## IDS ## + ## DSP ## ######### - - name: "IDS_WEBHOOK_ADDRESS" - value: {{ include "txdc.controlplane.url.protocol" . | quote }} - - name: "EDC_IDS_ENDPOINT" + + - name: "EDC_DSP_CALLBACK_ADDRESS" value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" .) .Values.controlplane.endpoints.protocol.path | quote }} - - name: "EDC_IDS_ID" - value: {{ printf "urn:connector:%s" (lower .Values.controlplane.internationalDataSpaces.id) | quote }} - - name: "EDC_IDS_DESCRIPTION" - value: {{ .Values.controlplane.internationalDataSpaces.description | quote }} - - name: "EDC_IDS_TITLE" - value: {{ .Values.controlplane.internationalDataSpaces.title | quote }} - - name: "EDC_IDS_MAINTAINER" - value: {{ .Values.controlplane.internationalDataSpaces.maintainer | quote }} - - name: "EDC_IDS_CURATOR" - value: {{ .Values.controlplane.internationalDataSpaces.curator | quote }} - - name: "EDC_IDS_CATALOG_ID" - value: {{ printf "urn:catalog:%s" (lower .Values.controlplane.internationalDataSpaces.catalogId) | quote }} - name: "EDC_OAUTH_PROVIDER_AUDIENCE" value: "idsc:IDS_CONNECTORS_ALL" - name: "EDC_OAUTH_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path "/data" | quote }} - # this is the old setting name for 'EDC_OAUTH_ENDPOINT_AUDIENCE' and is mandatory for Produce EDC v0.1.2 and older - - name: "EDC_IDS_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path "/data" | quote }} + value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path | quote }} ################ ## POSTGRESQL ## @@ -234,6 +224,16 @@ spec: - name: "EDC_DATASOURCE_TRANSFERPROCESS_URL" value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | 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.username | required ".Values.postgresql.username is required" | quote }} + - name: "EDC_DATASOURCE_EDR_PASSWORD" + value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + - name: "EDC_DATASOURCE_EDR_URL" + value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} + ################ ## DATA PLANE ## ################ diff --git a/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml b/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml index a745e6778..468c1c2d9 100644 --- a/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector-azure-vault/templates/deployment-dataplane.yaml @@ -147,6 +147,20 @@ spec: value: {{ .Values.dataplane.aws.accessKeyId | quote }} {{- end }} + ############### + ## EDR CACHE ## + ############### + + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/edr-cache-sql + - name: "EDC_DATASOURCE_EDR_NAME" + value: "edr" + - name: "EDC_DATASOURCE_EDR_USER" + value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + - name: "EDC_DATASOURCE_EDR_PASSWORD" + value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + - name: "EDC_DATASOURCE_EDR_URL" + value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} + ########### ## VAULT ## ########### diff --git a/charts/tractusx-connector-azure-vault/templates/service-dataplane.yaml b/charts/tractusx-connector-azure-vault/templates/service-dataplane.yaml index 65df635da..179963fa8 100644 --- a/charts/tractusx-connector-azure-vault/templates/service-dataplane.yaml +++ b/charts/tractusx-connector-azure-vault/templates/service-dataplane.yaml @@ -48,5 +48,9 @@ spec: 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-azure-vault/values.yaml b/charts/tractusx-connector-azure-vault/values.yaml index a2a80bcb3..3a124ad81 100644 --- a/charts/tractusx-connector-azure-vault/values.yaml +++ b/charts/tractusx-connector-azure-vault/values.yaml @@ -26,6 +26,9 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. +install: + daps: true + postgresql: true fullnameOverride: "" nameOverride: "" @@ -34,6 +37,9 @@ 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 @@ -107,7 +113,7 @@ controlplane: # -- port for incoming api calls port: 8084 # -- path for incoming api calls - path: /api/v1/ids + path: /api/v1/dsp # -- metrics api, used for application metrics, must not be internet facing metrics: # -- port for incoming api calls @@ -195,7 +201,7 @@ controlplane: annotations: {} # -- EDC endpoints exposed by this ingress resource endpoints: - - ids + - 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 @@ -239,8 +245,7 @@ controlplane: # -- [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: - {} + 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 @@ -340,6 +345,9 @@ dataplane: control: port: 8083 path: /api/dataplane/control + proxy: + port: 8186 + path: /proxy observability: # -- port for incoming API calls port: 8085 @@ -440,8 +448,7 @@ dataplane: # -- [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: - {} + 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 @@ -488,11 +495,18 @@ dataplane: public: "" postgresql: - enabled: false jdbcUrl: "" - username: "" - password: "" - + fullnameOverride: "postgresql" + primary: + persistence: + enabled: false + readReplicas: + persistence: + enabled: false + auth: + database: "edc" + username: "user" + password: "password" vault: azure: name: "" @@ -509,15 +523,21 @@ vault: dapsPublicKey: daps-public-key daps: + fullnameOverride: "daps" url: "" 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: "" - serviceAccount: # Specifies whether a service account should be created create: true @@ -528,3 +548,6 @@ serviceAccount: name: "" # -- Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) imagePullSecrets: [] +idsdaps: + connectors: + - certificate: |- diff --git a/charts/tractusx-connector-memory/Chart.yaml b/charts/tractusx-connector-memory/Chart.yaml index 8986d3072..5612b3523 100644 --- a/charts/tractusx-connector-memory/Chart.yaml +++ b/charts/tractusx-connector-memory/Chart.yaml @@ -34,12 +34,19 @@ 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.4.0 +version: 0.4.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.4.0" +appVersion: "0.4.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 +dependencies: + # IDS Dynamic Attribute Provisioning Service (IAM) + - name: daps + version: 0.0.1 + repository: "file://./subcharts/omejdn" + alias: daps + condition: install.daps diff --git a/charts/tractusx-connector-memory/README.md b/charts/tractusx-connector-memory/README.md index 2a7a25af5..532c7539a 100644 --- a/charts/tractusx-connector-memory/README.md +++ b/charts/tractusx-connector-memory/README.md @@ -1,6 +1,6 @@ # tractusx-connector-memory -![Version: 0.4.0](https://img.shields.io/badge/Version-0.4.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.0](https://img.shields.io/badge/AppVersion-0.4.0-informational?style=flat-square) +![Version: 0.4.1](https://img.shields.io/badge/Version-0.4.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.1](https://img.shields.io/badge/AppVersion-0.4.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! @@ -32,7 +32,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.4.0 \ +helm install my-release tractusx-edc/tractusx-connector-memory --version 0.4.1 \ -f /example.yaml \ --set vault.secrets="daps-cert:$DAPS_CERT;daps-key:$DAPS_KEY" \ ``` @@ -43,6 +43,12 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri * +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| file://./subcharts/omejdn | daps(daps) | 0.0.1 | + ## Values | Key | Type | Default | Description | @@ -50,12 +56,20 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri | backendService.httpProxyTokenReceiverUrl | string | `""` | | | 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.fullnameOverride | string | `"daps"` | | | daps.paths.jwks | string | `"/jwks.json"` | | | daps.paths.token | string | `"/token"` | | | daps.url | string | `""` | | | 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` | | | nameOverride | string | `""` | | +| participant.id | string | `""` | | | 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 | @@ -66,23 +80,23 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri | runtime.debug.enabled | bool | `false` | | | runtime.debug.port | int | `1044` | | | runtime.debug.suspendOnStart | bool | `false` | | -| runtime.endpoints | object | `{"control":{"path":"/control","port":8083},"data":{"authKey":"","path":"/data","port":8081},"default":{"path":"/api","port":8080},"observability":{"insecure":true,"path":"/observability","port":8085},"protocol":{"path":"/api/v1/ids","port":8084},"public":{"path":"/api/public","port":8086},"validation":{"path":"/validation","port":8082}}` | endpoints of the control plane | +| runtime.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"","path":"/management","port":8081},"observability":{"insecure":true,"path":"/observability","port":8085},"protocol":{"path":"/api/v1/dsp","port":8084},"proxy":{"path":"/proxy","port":8186},"public":{"path":"/api/public","port":8086},"validation":{"path":"/validation","port":8082}}` | endpoints of the control plane | | runtime.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | | runtime.endpoints.control.path | string | `"/control"` | path for incoming api calls | | runtime.endpoints.control.port | int | `8083` | port for incoming api calls | -| runtime.endpoints.data | object | `{"authKey":"","path":"/data","port":8081}` | data management api, used by internal users, can be added to an ingress and must not be internet facing | -| runtime.endpoints.data.authKey | string | `""` | authentication key, must be attached to each 'X-Api-Key' request header | -| runtime.endpoints.data.path | string | `"/data"` | path for incoming api calls | -| runtime.endpoints.data.port | int | `8081` | port for incoming api calls | | runtime.endpoints.default | object | `{"path":"/api","port":8080}` | default api for health checks, should not be added to any ingress | | runtime.endpoints.default.path | string | `"/api"` | path for incoming api calls | | runtime.endpoints.default.port | int | `8080` | port for incoming api calls | +| runtime.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 | +| 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.observability | object | `{"insecure":true,"path":"/observability","port":8085}` | observability api with unsecured access, must not be internet facing | | runtime.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | | runtime.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | | runtime.endpoints.observability.port | int | `8085` | port for incoming API calls | -| runtime.endpoints.protocol | object | `{"path":"/api/v1/ids","port":8084}` | ids api, used for inter connector communication and must be internet facing | -| runtime.endpoints.protocol.path | string | `"/api/v1/ids"` | path 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.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 | | runtime.endpoints.validation.path | string | `"/validation"` | path for incoming api calls | @@ -109,7 +123,7 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri | runtime.ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | | runtime.ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | | runtime.ingresses[1].enabled | bool | `false` | | -| runtime.ingresses[1].endpoints | list | `["data","control"]` | EDC endpoints exposed by this ingress resource | +| runtime.ingresses[1].endpoints | list | `["management","control"]` | EDC endpoints exposed by this ingress resource | | runtime.ingresses[1].hostname | string | `"edc-control.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | | runtime.ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | | runtime.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | @@ -168,6 +182,7 @@ Note that `DAPS_CERT` contains the x509 certificate, `DAPS_KEY` contains the pri | vault.secretNames.transferProxyTokenSignerPrivateKey | string | `"transfer-proxy-token-signer-private-key"` | | | vault.secretNames.transferProxyTokenSignerPublicKey | string | `"transfer-proxy-token-signer-public-key"` | | | vault.secrets | string | `""` | | +| vault.server.postStart | string | `""` | | ---------------------------------------------- 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-memory/example.yaml b/charts/tractusx-connector-memory/example.yaml index 2eb672a74..5c2c60ca8 100644 --- a/charts/tractusx-connector-memory/example.yaml +++ b/charts/tractusx-connector-memory/example.yaml @@ -32,11 +32,15 @@ --- fullnameOverride: tx-inmem + +participant: + id: "test-participant" + runtime: service: type: NodePort endpoints: - data: + management: authKey: password image: pullPolicy: Never @@ -50,7 +54,7 @@ vault: secretNames: transferProxyTokenSignerPublicKey: daps-crt transferProxyTokenSignerPrivateKey: daps-key - transferProxyTokenEncryptionAesKey: aes-keysc + transferProxyTokenEncryptionAesKey: aes-keys dapsPrivateKey: daps-key dapsPublicKey: daps-crt @@ -59,7 +63,7 @@ vault: secrets: daps: - url: "http://ids-daps:4567" + url: "http://daps:4567" clientId: "99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23:keyid:99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23" backendService: diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.helmignore b/charts/tractusx-connector-memory/subcharts/omejdn/.helmignore similarity index 98% rename from edc-tests/deployment/src/main/resources/helm/test-infrastructure/.helmignore rename to charts/tractusx-connector-memory/subcharts/omejdn/.helmignore index 8c60d7821..0e8a0eb36 100644 --- a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.helmignore +++ b/charts/tractusx-connector-memory/subcharts/omejdn/.helmignore @@ -21,4 +21,3 @@ .idea/ *.tmproj .vscode/ -docs diff --git a/charts/tractusx-connector-memory/subcharts/omejdn/Chart.yaml b/charts/tractusx-connector-memory/subcharts/omejdn/Chart.yaml new file mode 100644 index 000000000..3e10aa1fc --- /dev/null +++ b/charts/tractusx-connector-memory/subcharts/omejdn/Chart.yaml @@ -0,0 +1,40 @@ +# 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.4.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.4.1" diff --git a/charts/tractusx-connector-memory/subcharts/omejdn/README.md b/charts/tractusx-connector-memory/subcharts/omejdn/README.md new file mode 100644 index 000000000..d23a9f9fa --- /dev/null +++ b/charts/tractusx-connector-memory/subcharts/omejdn/README.md @@ -0,0 +1,39 @@ +# daps + +![Version: 0.4.1](https://img.shields.io/badge/Version-0.4.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.1](https://img.shields.io/badge/AppVersion-0.4.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-memory/subcharts/omejdn/templates/_helpers.tpl b/charts/tractusx-connector-memory/subcharts/omejdn/templates/_helpers.tpl new file mode 100644 index 000000000..95b115eee --- /dev/null +++ b/charts/tractusx-connector-memory/subcharts/omejdn/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +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-memory/subcharts/omejdn/templates/configmap.yaml b/charts/tractusx-connector-memory/subcharts/omejdn/templates/configmap.yaml new file mode 100644 index 000000000..5ad21648d --- /dev/null +++ b/charts/tractusx-connector-memory/subcharts/omejdn/templates/configmap.yaml @@ -0,0 +1,92 @@ +# 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://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://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-memory/subcharts/omejdn/templates/deployment.yaml b/charts/tractusx-connector-memory/subcharts/omejdn/templates/deployment.yaml new file mode 100644 index 000000000..58bfff105 --- /dev/null +++ b/charts/tractusx-connector-memory/subcharts/omejdn/templates/deployment.yaml @@ -0,0 +1,168 @@ +# 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-memory/subcharts/omejdn/templates/hpa.yaml b/charts/tractusx-connector-memory/subcharts/omejdn/templates/hpa.yaml new file mode 100644 index 000000000..f1f072f6c --- /dev/null +++ b/charts/tractusx-connector-memory/subcharts/omejdn/templates/hpa.yaml @@ -0,0 +1,47 @@ +# 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-memory/subcharts/omejdn/templates/imagepullsecret.yaml b/charts/tractusx-connector-memory/subcharts/omejdn/templates/imagepullsecret.yaml new file mode 100644 index 000000000..44f573e0f --- /dev/null +++ b/charts/tractusx-connector-memory/subcharts/omejdn/templates/imagepullsecret.yaml @@ -0,0 +1,31 @@ +# 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-memory/subcharts/omejdn/templates/service.yaml b/charts/tractusx-connector-memory/subcharts/omejdn/templates/service.yaml new file mode 100644 index 000000000..947e69742 --- /dev/null +++ b/charts/tractusx-connector-memory/subcharts/omejdn/templates/service.yaml @@ -0,0 +1,34 @@ +# 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: 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 }} diff --git a/charts/tractusx-connector-memory/subcharts/omejdn/templates/serviceaccount.yaml b/charts/tractusx-connector-memory/subcharts/omejdn/templates/serviceaccount.yaml new file mode 100644 index 000000000..536f31871 --- /dev/null +++ b/charts/tractusx-connector-memory/subcharts/omejdn/templates/serviceaccount.yaml @@ -0,0 +1,31 @@ +# 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.serviceAccount.create -}} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "omejdn.serviceAccountName" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/tractusx-connector-memory/subcharts/omejdn/values.yaml b/charts/tractusx-connector-memory/subcharts/omejdn/values.yaml new file mode 100644 index 000000000..f411b8774 --- /dev/null +++ b/charts/tractusx-connector-memory/subcharts/omejdn/values.yaml @@ -0,0 +1,109 @@ +# 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-memory/templates/_helpers.tpl b/charts/tractusx-connector-memory/templates/_helpers.tpl index b30831421..bbe52eb41 100644 --- a/charts/tractusx-connector-memory/templates/_helpers.tpl +++ b/charts/tractusx-connector-memory/templates/_helpers.tpl @@ -103,13 +103,6 @@ Control IDS URL {{- end }}{{/* end if .Values.runtime.url.protocol */}} {{- end }} -{{/* -Observability URL -*/}} -{{- define "tdxc.runtime.url.readiness" -}} -{{- printf "http://%s-runtime:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.runtime.endpoints.observability.port $.Values.runtime.endpoints.observability.path -}} -{{- end }} - {{/* Validation URL */}} diff --git a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml index 462a592d6..f383b101f 100644 --- a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml @@ -1,24 +1,24 @@ # -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License, Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# + # Copyright (c) 2023 ZF Friedrichshafen AG + # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH + # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License, Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + # License for the specific language governing permissions and limitations + # under the License. + # + # SPDX-License-Identifier: Apache-2.0 + # --- apiVersion: apps/v1 @@ -109,6 +109,12 @@ spec: {{- end }} {{- end }} + ######################## + ## ID CONFIGURATION ## + ######################## + - name: EDC_PARTICIPANT_ID + value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }} + ######################## ## DAPS CONFIGURATION ## ######################## @@ -129,7 +135,7 @@ spec: # API # ####### - name: "EDC_API_AUTH_KEY" - value: {{ .Values.runtime.endpoints.data.authKey | required ".Values.runtime.endpoints.data.authKey is required" | quote }} + value: {{ .Values.runtime.endpoints.management.authKey | required ".Values.runtime.endpoints.management.authKey is required" | quote }} - name: "WEB_HTTP_DEFAULT_PORT" value: {{ .Values.runtime.endpoints.default.port | quote }} - name: "WEB_HTTP_DEFAULT_PATH" @@ -138,16 +144,16 @@ spec: # WEB_HTTP_DATA_PORT is renamed to WEB_HTTP_MANAGEMENT_PORT from version 0.2.1 and newer # we will keep both settings for downward capabilities - name: "WEB_HTTP_DATA_PORT" - value: {{ .Values.runtime.endpoints.data.port | quote }} + value: {{ .Values.runtime.endpoints.management.port | quote }} # WEB_HTTP_DATA_PATH is renamed to WEB_HTTP_MANAGEMENT_PATH from version 0.2.1 and newer # we will keep both settings for downward capabilities - name: "WEB_HTTP_DATA_PATH" - value: {{ .Values.runtime.endpoints.data.path | quote }} + value: {{ .Values.runtime.endpoints.management.path | quote }} {{- else }} - name: "WEB_HTTP_MANAGEMENT_PORT" - value: {{ .Values.runtime.endpoints.data.port | quote }} + value: {{ .Values.runtime.endpoints.management.port | quote }} - name: "WEB_HTTP_MANAGEMENT_PATH" - value: {{ .Values.runtime.endpoints.data.path | quote }} + value: {{ .Values.runtime.endpoints.management.path | quote }} {{- end }} - name: "WEB_HTTP_VALIDATION_PORT" value: {{ .Values.runtime.endpoints.validation.port | quote }} @@ -175,31 +181,15 @@ spec: value: {{ include "txdc.runtime.url.validation" .}} ######### - ## IDS ## + ## DSP ## ######### - - name: "IDS_WEBHOOK_ADDRESS" - value: {{ include "txdc.runtime.url.protocol" . | quote }} - - name: "EDC_IDS_ENDPOINT" + + - name: "EDC_DSP_CALLBACK_ADDRESS" value: {{ printf "%s%s" (include "txdc.runtime.url.protocol" .) .Values.runtime.endpoints.protocol.path | quote }} - - name: "EDC_IDS_ID" - value: {{ printf "urn:connector:%s" (lower .Values.runtime.internationalDataSpaces.id) | quote }} - - name: "EDC_IDS_DESCRIPTION" - value: {{ .Values.runtime.internationalDataSpaces.description | quote }} - - name: "EDC_IDS_TITLE" - value: {{ .Values.runtime.internationalDataSpaces.title | quote }} - - name: "EDC_IDS_MAINTAINER" - value: {{ .Values.runtime.internationalDataSpaces.maintainer | quote }} - - name: "EDC_IDS_CURATOR" - value: {{ .Values.runtime.internationalDataSpaces.curator | quote }} - - name: "EDC_IDS_CATALOG_ID" - value: {{ printf "urn:catalog:%s" (lower .Values.runtime.internationalDataSpaces.catalogId) | quote }} - name: "EDC_OAUTH_PROVIDER_AUDIENCE" value: "idsc:IDS_CONNECTORS_ALL" - name: "EDC_OAUTH_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s%s" (include "txdc.runtime.url.protocol" . ) .Values.runtime.endpoints.protocol.path "/data" | quote }} - # this is the old setting name for 'EDC_OAUTH_ENDPOINT_AUDIENCE' and is mandatory for Produce EDC v0.1.2 and older - - name: "EDC_IDS_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s%s" (include "txdc.runtime.url.protocol" . ) .Values.runtime.endpoints.protocol.path "/data" | quote }} + value: {{ printf "%s%s" (include "txdc.runtime.url.protocol" . ) .Values.runtime.endpoints.protocol.path | quote }} ################ ## DATA PLANE ## @@ -253,7 +243,7 @@ spec: value: "0" - name: "EDC_CP_ADAPTER_REUSE_CONTRACT_AGREEMENT" value: "0" - + ########################### ## BUSINESS PARTNER NUMBER VALIDATION EXTENSION ## ########################### diff --git a/charts/tractusx-connector-memory/templates/service-runtime.yaml b/charts/tractusx-connector-memory/templates/service-runtime.yaml index d227ceb53..9a0100253 100644 --- a/charts/tractusx-connector-memory/templates/service-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/service-runtime.yaml @@ -1,24 +1,24 @@ # -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License, Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# + # Copyright (c) 2023 ZF Friedrichshafen AG + # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH + # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License, Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + # License for the specific language governing permissions and limitations + # under the License. + # + # SPDX-License-Identifier: Apache-2.0 + # --- apiVersion: v1 @@ -39,7 +39,7 @@ spec: targetPort: control protocol: TCP name: control - - port: {{ .Values.runtime.endpoints.data.port }} + - port: {{ .Values.runtime.endpoints.management.port }} targetPort: data protocol: TCP name: data diff --git a/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml b/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml index 497e619a1..ad051af69 100644 --- a/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml +++ b/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml @@ -33,6 +33,6 @@ spec: containers: - name: wget image: curlimages/curl - command: ['curl'] - args: ['{{ include "tdxc.runtime.url.readiness" . }}'] + command: [ 'curl' ] + args: [ '{{- printf "http://%s-runtime:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.runtime.endpoints.observability.port $.Values.runtime.endpoints.observability.path -}}' ] restartPolicy: Never diff --git a/charts/tractusx-connector-memory/values.yaml b/charts/tractusx-connector-memory/values.yaml index 7df670250..4668714cf 100644 --- a/charts/tractusx-connector-memory/values.yaml +++ b/charts/tractusx-connector-memory/values.yaml @@ -23,14 +23,17 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. +install: + daps: 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: "" + runtime: image: repository: "" @@ -85,11 +88,11 @@ runtime: # -- 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 - data: + management: # -- port for incoming api calls port: 8081 # -- path for incoming api calls - path: /data + path: /management # -- authentication key, must be attached to each 'X-Api-Key' request header authKey: "" # -- validation api, only used by the data plane and should not be added to any ingress @@ -109,7 +112,7 @@ runtime: # -- port for incoming api calls port: 8084 # -- path for incoming api calls - path: /api/v1/ids + path: /api/v1/dsp # -- observability api with unsecured access, must not be internet facing observability: # -- port for incoming API calls @@ -121,6 +124,9 @@ runtime: public: port: 8086 path: /api/public + proxy: + port: 8186 + path: /proxy businessPartnerValidation: log: agreementValidation: true @@ -217,7 +223,7 @@ runtime: annotations: {} # -- EDC endpoints exposed by this ingress resource endpoints: - - data + - management - control # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" @@ -238,8 +244,7 @@ runtime: # -- [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: - {} + 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 @@ -270,20 +275,17 @@ runtime: 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: "" public: "" readiness: "" - vault: # secrets can be seeded by supplying them in a comma separated list key1:secret2,key2:secret2 secrets: "" @@ -293,17 +295,24 @@ vault: transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key dapsPrivateKey: daps-private-key dapsPublicKey: daps-public-key - + server: + postStart: |- daps: + fullnameOverride: "daps" url: "" 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: "" - serviceAccount: # Specifies whether a service account should be created create: true @@ -314,3 +323,6 @@ serviceAccount: name: "" # -- Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) imagePullSecrets: [] +idsdaps: + connectors: + - certificate: |- diff --git a/charts/tractusx-connector/Chart.yaml b/charts/tractusx-connector/Chart.yaml index f20757da3..307be2907 100644 --- a/charts/tractusx-connector/Chart.yaml +++ b/charts/tractusx-connector/Chart.yaml @@ -40,12 +40,31 @@ 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.4.0 +version: 0.4.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.4.0" +appVersion: "0.4.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 +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/README.md b/charts/tractusx-connector/README.md index b2c9f8e2d..cfb07b537 100644 --- a/charts/tractusx-connector/README.md +++ b/charts/tractusx-connector/README.md @@ -1,6 +1,6 @@ # tractusx-connector -![Version: 0.4.0](https://img.shields.io/badge/Version-0.4.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.0](https://img.shields.io/badge/AppVersion-0.4.0-informational?style=flat-square) +![Version: 0.4.1](https://img.shields.io/badge/Version-0.4.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.1](https://img.shields.io/badge/AppVersion-0.4.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. @@ -37,7 +37,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.4.0 \ +helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0.4.1 \ -f /tractusx-connector-test.yaml ``` @@ -45,6 +45,14 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. * +## 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 | @@ -60,7 +68,7 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | 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},"observability":{"insecure":true,"path":"/observability","port":8085},"protocol":{"path":"/api/v1/ids","port":8084}}` | endpoints of the control plane | +| controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"","path":"/management","port":8081},"metrics":{"path":"/metrics","port":9090},"observability":{"insecure":true,"path":"/observability","port":8085},"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 | @@ -78,8 +86,8 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | controlplane.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | | controlplane.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | | controlplane.endpoints.observability.port | int | `8085` | port for incoming API calls | -| controlplane.endpoints.protocol | object | `{"path":"/api/v1/ids","port":8084}` | ids api, used for inter connector communication and must be internet facing | -| controlplane.endpoints.protocol.path | string | `"/api/v1/ids"` | path 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 | `[]` | | @@ -153,6 +161,11 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | 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.fullnameOverride | string | `"daps"` | | | daps.paths.jwks | string | `"/jwks.json"` | | | daps.paths.token | string | `"/token"` | | | daps.url | string | `""` | | @@ -177,6 +190,8 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | dataplane.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | | dataplane.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | | dataplane.endpoints.observability.port | int | `8085` | port for incoming API calls | +| 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 | `{}` | | @@ -234,16 +249,25 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | 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 | `""` | | -| postgresql.enabled | bool | `false` | | +| participant.id | string | `""` | | +| postgresql.auth.database | string | `"edc"` | | +| postgresql.auth.password | string | `"password"` | | +| postgresql.auth.username | string | `"user"` | | +| postgresql.fullnameOverride | string | `"postgresql"` | | | postgresql.jdbcUrl | string | `""` | | -| postgresql.password | string | `""` | | -| postgresql.username | string | `""` | | +| 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 | `""` | | +| vault.fullnameOverride | string | `"vault"` | | | vault.hashicorp.healthCheck.enabled | bool | `true` | | | vault.hashicorp.healthCheck.standbyOk | bool | `true` | | | vault.hashicorp.paths.health | string | `"/v1/sys/health"` | | @@ -251,11 +275,15 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | vault.hashicorp.timeout | int | `30` | | | vault.hashicorp.token | string | `""` | | | vault.hashicorp.url | string | `""` | | +| 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/subcharts/omejdn/.helmignore b/charts/tractusx-connector/subcharts/omejdn/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/charts/tractusx-connector/subcharts/omejdn/.helmignore @@ -0,0 +1,23 @@ +# 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/subcharts/omejdn/Chart.yaml b/charts/tractusx-connector/subcharts/omejdn/Chart.yaml new file mode 100644 index 000000000..3e10aa1fc --- /dev/null +++ b/charts/tractusx-connector/subcharts/omejdn/Chart.yaml @@ -0,0 +1,40 @@ +# 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.4.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.4.1" diff --git a/charts/tractusx-connector/subcharts/omejdn/README.md b/charts/tractusx-connector/subcharts/omejdn/README.md new file mode 100644 index 000000000..d23a9f9fa --- /dev/null +++ b/charts/tractusx-connector/subcharts/omejdn/README.md @@ -0,0 +1,39 @@ +# daps + +![Version: 0.4.1](https://img.shields.io/badge/Version-0.4.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.1](https://img.shields.io/badge/AppVersion-0.4.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/subcharts/omejdn/templates/_helpers.tpl b/charts/tractusx-connector/subcharts/omejdn/templates/_helpers.tpl new file mode 100644 index 000000000..95b115eee --- /dev/null +++ b/charts/tractusx-connector/subcharts/omejdn/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +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/subcharts/omejdn/templates/configmap.yaml b/charts/tractusx-connector/subcharts/omejdn/templates/configmap.yaml new file mode 100644 index 000000000..5ad21648d --- /dev/null +++ b/charts/tractusx-connector/subcharts/omejdn/templates/configmap.yaml @@ -0,0 +1,92 @@ +# 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://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://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/subcharts/omejdn/templates/deployment.yaml b/charts/tractusx-connector/subcharts/omejdn/templates/deployment.yaml new file mode 100644 index 000000000..58bfff105 --- /dev/null +++ b/charts/tractusx-connector/subcharts/omejdn/templates/deployment.yaml @@ -0,0 +1,168 @@ +# 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/subcharts/omejdn/templates/hpa.yaml b/charts/tractusx-connector/subcharts/omejdn/templates/hpa.yaml new file mode 100644 index 000000000..f1f072f6c --- /dev/null +++ b/charts/tractusx-connector/subcharts/omejdn/templates/hpa.yaml @@ -0,0 +1,47 @@ +# 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/subcharts/omejdn/templates/imagepullsecret.yaml b/charts/tractusx-connector/subcharts/omejdn/templates/imagepullsecret.yaml new file mode 100644 index 000000000..44f573e0f --- /dev/null +++ b/charts/tractusx-connector/subcharts/omejdn/templates/imagepullsecret.yaml @@ -0,0 +1,31 @@ +# 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/subcharts/omejdn/templates/service.yaml b/charts/tractusx-connector/subcharts/omejdn/templates/service.yaml new file mode 100644 index 000000000..947e69742 --- /dev/null +++ b/charts/tractusx-connector/subcharts/omejdn/templates/service.yaml @@ -0,0 +1,34 @@ +# 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: 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 }} diff --git a/charts/tractusx-connector/subcharts/omejdn/templates/serviceaccount.yaml b/charts/tractusx-connector/subcharts/omejdn/templates/serviceaccount.yaml new file mode 100644 index 000000000..536f31871 --- /dev/null +++ b/charts/tractusx-connector/subcharts/omejdn/templates/serviceaccount.yaml @@ -0,0 +1,31 @@ +# 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.serviceAccount.create -}} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "omejdn.serviceAccountName" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/tractusx-connector/subcharts/omejdn/values.yaml b/charts/tractusx-connector/subcharts/omejdn/values.yaml new file mode 100644 index 000000000..f411b8774 --- /dev/null +++ b/charts/tractusx-connector/subcharts/omejdn/values.yaml @@ -0,0 +1,109 @@ +# 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/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 64e8fa5f8..f4cc125e1 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -109,6 +109,12 @@ spec: {{- end }} {{- end }} + ######################## + ## ID CONFIGURATION ## + ######################## + - name: EDC_PARTICIPANT_ID + value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }} + ######################## ## DAPS CONFIGURATION ## ######################## @@ -154,31 +160,15 @@ spec: value: {{ .Values.controlplane.endpoints.observability.insecure | quote }} ######### - ## IDS ## + ## DSP ## ######### - - name: "IDS_WEBHOOK_ADDRESS" - value: {{ include "txdc.controlplane.url.protocol" . | quote }} - - name: "EDC_IDS_ENDPOINT" + + - name: "EDC_DSP_CALLBACK_ADDRESS" value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" .) .Values.controlplane.endpoints.protocol.path | quote }} - - name: "EDC_IDS_ID" - value: {{ printf "urn:connector:%s" (lower .Values.controlplane.internationalDataSpaces.id) | quote }} - - name: "EDC_IDS_DESCRIPTION" - value: {{ .Values.controlplane.internationalDataSpaces.description | quote }} - - name: "EDC_IDS_TITLE" - value: {{ .Values.controlplane.internationalDataSpaces.title | quote }} - - name: "EDC_IDS_MAINTAINER" - value: {{ .Values.controlplane.internationalDataSpaces.maintainer | quote }} - - name: "EDC_IDS_CURATOR" - value: {{ .Values.controlplane.internationalDataSpaces.curator | quote }} - - name: "EDC_IDS_CATALOG_ID" - value: {{ printf "urn:catalog:%s" (lower .Values.controlplane.internationalDataSpaces.catalogId) | quote }} - name: "EDC_OAUTH_PROVIDER_AUDIENCE" value: "idsc:IDS_CONNECTORS_ALL" - name: "EDC_OAUTH_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path "/data" | quote }} - # this is the old setting name for 'EDC_OAUTH_ENDPOINT_AUDIENCE' and is mandatory for Produce EDC v0.1.2 and older - - name: "EDC_IDS_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path "/data" | quote }} + value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path | quote }} ################ ## POSTGRESQL ## @@ -188,9 +178,9 @@ spec: - name: "EDC_DATASOURCE_ASSET_NAME" value: "asset" - name: "EDC_DATASOURCE_ASSET_USER" - value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - name: "EDC_DATASOURCE_ASSET_PASSWORD" - value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - name: "EDC_DATASOURCE_ASSET_URL" value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} @@ -198,9 +188,9 @@ spec: - name: "EDC_DATASOURCE_CONTRACTDEFINITION_NAME" value: "contractdefinition" - name: "EDC_DATASOURCE_CONTRACTDEFINITION_USER" - value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - name: "EDC_DATASOURCE_CONTRACTDEFINITION_PASSWORD" - value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - name: "EDC_DATASOURCE_CONTRACTDEFINITION_URL" value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} @@ -208,9 +198,9 @@ spec: - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_NAME" value: "contractnegotiation" - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_USER" - value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_PASSWORD" - value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_URL" value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} @@ -218,9 +208,9 @@ spec: - name: "EDC_DATASOURCE_POLICY_NAME" value: "policy" - name: "EDC_DATASOURCE_POLICY_USER" - value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - name: "EDC_DATASOURCE_POLICY_PASSWORD" - value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - name: "EDC_DATASOURCE_POLICY_URL" value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} @@ -228,12 +218,22 @@ spec: - name: "EDC_DATASOURCE_TRANSFERPROCESS_NAME" value: "transferprocess" - name: "EDC_DATASOURCE_TRANSFERPROCESS_USER" - value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - name: "EDC_DATASOURCE_TRANSFERPROCESS_PASSWORD" - value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} - name: "EDC_DATASOURCE_TRANSFERPROCESS_URL" value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | 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.username | required ".Values.postgresql.username is required" | quote }} + - name: "EDC_DATASOURCE_EDR_PASSWORD" + value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + - name: "EDC_DATASOURCE_EDR_URL" + value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} + ################ ## DATA PLANE ## ################ diff --git a/charts/tractusx-connector/templates/deployment-dataplane.yaml b/charts/tractusx-connector/templates/deployment-dataplane.yaml index 5c6b28a1d..5a911dea2 100644 --- a/charts/tractusx-connector/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector/templates/deployment-dataplane.yaml @@ -147,6 +147,20 @@ spec: value: {{ .Values.dataplane.aws.accessKeyId | quote }} {{- end }} + ############### + ## EDR CACHE ## + ############### + + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/edr-cache-sql + - name: "EDC_DATASOURCE_EDR_NAME" + value: "edr" + - name: "EDC_DATASOURCE_EDR_USER" + value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + - name: "EDC_DATASOURCE_EDR_PASSWORD" + value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + - name: "EDC_DATASOURCE_EDR_URL" + value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} + ########### ## VAULT ## ########### diff --git a/charts/tractusx-connector/templates/service-dataplane.yaml b/charts/tractusx-connector/templates/service-dataplane.yaml index 07ee9cba3..14c2181b7 100644 --- a/charts/tractusx-connector/templates/service-dataplane.yaml +++ b/charts/tractusx-connector/templates/service-dataplane.yaml @@ -51,5 +51,10 @@ spec: 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/values.yaml b/charts/tractusx-connector/values.yaml index e2bb1a692..2825a2e7f 100644 --- a/charts/tractusx-connector/values.yaml +++ b/charts/tractusx-connector/values.yaml @@ -26,14 +26,19 @@ # 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 @@ -107,7 +112,7 @@ controlplane: # -- port for incoming api calls port: 8084 # -- path for incoming api calls - path: /api/v1/ids + path: /api/v1/dsp # -- metrics api, used for application metrics, must not be internet facing metrics: # -- port for incoming api calls @@ -239,18 +244,17 @@ controlplane: # -- [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 + 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/) @@ -275,18 +279,15 @@ controlplane: 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 @@ -340,6 +341,9 @@ dataplane: control: port: 8083 path: /api/dataplane/control + proxy: + port: 8186 + path: /proxy observability: # -- port for incoming API calls port: 8085 @@ -440,18 +444,17 @@ dataplane: # -- [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 + 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/) @@ -482,18 +485,32 @@ dataplane: 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: - enabled: false jdbcUrl: "" - username: "" - password: "" - + fullnameOverride: "postgresql" + primary: + persistence: + enabled: false + readReplicas: + persistence: + enabled: false + auth: + database: "edc" + username: "user" + password: "password" vault: + fullnameOverride: "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: "" token: "" @@ -510,17 +527,22 @@ vault: transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key dapsPrivateKey: daps-private-key dapsPublicKey: daps-public-key - daps: + fullnameOverride: "daps" url: "" 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: "" - serviceAccount: # Specifies whether a service account should be created create: true @@ -531,3 +553,6 @@ serviceAccount: name: "" # -- Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) imagePullSecrets: [] +idsdaps: + connectors: + - certificate: |- diff --git a/core/edr-cache-core/build.gradle.kts b/core/edr-cache-core/build.gradle.kts index 0c1e5474d..95b0c0be6 100644 --- a/core/edr-cache-core/build.gradle.kts +++ b/core/edr-cache-core/build.gradle.kts @@ -22,5 +22,8 @@ dependencies { implementation(libs.edc.util) implementation(project(":spi:edr-cache-spi")) + + testImplementation(testFixtures(project(":spi:edr-cache-spi"))) + } 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 new file mode 100644 index 000000000..27346d512 --- /dev/null +++ b/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/EdrCacheEntryPredicateConverter.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.core.defaults; + +import org.eclipse.edc.spi.query.BaseCriterionToPredicateConverter; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; + +public class EdrCacheEntryPredicateConverter extends BaseCriterionToPredicateConverter { + + @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; + } + } + 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 d396b4170..0149902bf 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,6 +14,8 @@ package org.eclipse.tractusx.edc.edr.core.defaults; +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; @@ -22,8 +24,15 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Predicate; +import java.util.stream.Stream; import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; @@ -36,14 +45,17 @@ public class InMemoryEndpointDataReferenceCache implements EndpointDataReferenceCache { private final LockManager lockManager; + private final EdrCacheEntryPredicateConverter predicateConverter = new EdrCacheEntryPredicateConverter(); + private final Map> entriesByAssetId; + private final Map entriesByEdrId; private final Map edrsByTransferProcessId; public InMemoryEndpointDataReferenceCache() { lockManager = new LockManager(new ReentrantReadWriteLock()); entriesByAssetId = new HashMap<>(); - entriesByEdrId = new HashMap<>(); + entriesByEdrId = new ConcurrentHashMap<>(); edrsByTransferProcessId = new HashMap<>(); } @@ -63,9 +75,8 @@ public List referencesForAsset(String assetId) { } @Override - @NotNull - public List entriesForAsset(String assetId) { - return lockManager.readLock(() -> entriesByAssetId.getOrDefault(assetId, emptyList())); + public Stream queryForEntries(QuerySpec spec) { + return filterBy(spec.getFilterExpression()); } @Override @@ -87,13 +98,23 @@ public StoreResult deleteByTransferProcessId(String if (edr == null) { return notFound("EDR entry not found for id: " + id); } - var entry = entriesByEdrId.get(edr.getId()); + var entry = entriesByEdrId.remove(edr.getId()); var entries = entriesByAssetId.get(entry.getAssetId()); entries.remove(entry); if (entries.isEmpty()) { entriesByAssetId.remove(entry.getAssetId()); } + return success(entry); }); } + + private Stream filterBy(List criteria) { + var predicate = criteria.stream() + .map(predicateConverter::convert) + .reduce(x -> true, Predicate::and); + + return entriesByEdrId.values().stream() + .filter(predicate); + } } 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 c2dab4afc..7fb287716 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,50 +14,15 @@ package org.eclipse.tractusx.edc.edr.core.defaults; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; -import org.junit.jupiter.api.Test; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCacheBaseTest; -import static java.util.UUID.randomUUID; -import static org.assertj.core.api.Assertions.assertThat; +class InMemoryEndpointDataReferenceCacheTest extends EndpointDataReferenceCacheBaseTest { + private final InMemoryEndpointDataReferenceCache cache = new InMemoryEndpointDataReferenceCache(); -class InMemoryEndpointDataReferenceCacheTest { - private static final String TRANSFER_PROCESS_ID = "tp1"; - private static final String ASSET_ID = "asset1"; - private static final String EDR_ID = "edr1"; - - private InMemoryEndpointDataReferenceCache cache = new InMemoryEndpointDataReferenceCache(); - - @Test - @SuppressWarnings("DataFlowIssue") - void verify_operations() { - var edr = EndpointDataReference.Builder.newInstance(). - endpoint("http://test.com") - .id(EDR_ID) - .authCode("11111") - .authKey("authentication").build(); - - var entry = EndpointDataReferenceEntry.Builder.newInstance() - .assetId(ASSET_ID) - .agreementId(randomUUID().toString()) - .transferProcessId(TRANSFER_PROCESS_ID) - .build(); - - cache.save(entry, edr); - - assertThat(cache.resolveReference(TRANSFER_PROCESS_ID).getId()).isEqualTo(EDR_ID); - - var edrs = cache.referencesForAsset(ASSET_ID); - assertThat(edrs.size()).isEqualTo(1); - assertThat(edrs.get((0)).getId()).isEqualTo(EDR_ID); - - var entries = cache.entriesForAsset(ASSET_ID); - assertThat(entries.size()).isEqualTo(1); - assertThat(entries.get((0)).getAssetId()).isEqualTo(ASSET_ID); - - assertThat(cache.deleteByTransferProcessId(TRANSFER_PROCESS_ID).succeeded()).isTrue(); - - assertThat(cache.entriesForAsset(ASSET_ID)).isEmpty(); - assertThat(cache.resolveReference(TRANSFER_PROCESS_ID)).isNull(); + @Override + protected EndpointDataReferenceCache getStore() { + return cache; } + } 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 764afb33b..b17c1e524 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 @@ -29,8 +29,8 @@ class PersistentCacheEntryTest { void verify_serializeDeserialize() throws JsonProcessingException { var mapper = new ObjectMapper(); - var edr = EndpointDataReference.Builder.newInstance(). - endpoint("http://test.com") + var edr = EndpointDataReference.Builder.newInstance() + .endpoint("http://test.com") .id(randomUUID().toString()) .authCode("11111") .authKey("authentication").build(); diff --git a/docs/development/decision-records/2023-05-23_activate_checkstyle/README.md b/docs/development/decision-records/2023-05-23_activate_checkstyle/README.md new file mode 100644 index 000000000..a42559758 --- /dev/null +++ b/docs/development/decision-records/2023-05-23_activate_checkstyle/README.md @@ -0,0 +1,17 @@ +# Activate Checkstyle to enforce code style + +## Decision + +From now on, Tractus-X EDC will use activate Checkstyle, i.e. change its reporting level from `WARNING` to `ERROR`. + +## Rationale + +We already have a checkstyle task in our Gradle setup, and our [PR Etiquette document](../../../../pr_etiquette.md) references +the styleguide and mandates its use. + +Our CI pipeline already uses checkstyle, but only outputs warning at the moment. + +## Approach + +- in `resources/tx-checkstyle-config.xml`, Line 22, change `` to ``. +- fix all checkstyle errors diff --git a/docs/development/decision-records/2023-05-23_java_17_baseline/README.md b/docs/development/decision-records/2023-05-23_java_17_baseline/README.md new file mode 100644 index 000000000..fc65db00b --- /dev/null +++ b/docs/development/decision-records/2023-05-23_java_17_baseline/README.md @@ -0,0 +1,15 @@ +# Java 17 baseline + +## Decision + +We will use Java 17 as baseline version. + +## Rationale + +Java 11 active support [will end in September 2023](https://endoflife.date/java), and, following Java "new" release cycle, we should update the baseline +version to the current LTS from time to time. +[EDC](https://github.com/eclipse-edc/Connector/blob/main/docs/developer/decision-records/2023-05-23-java-17-baseline/README.md) upstream is switching to Java 17. + +## Approach + +Remove the custom `javaVersion` and let the `edc-build` plugin set that when upgrading to the newest version of EDC. diff --git a/docs/development/postman/collection.json b/docs/development/postman/collection.json index 26de5c7d2..7a25a8138 100644 --- a/docs/development/postman/collection.json +++ b/docs/development/postman/collection.json @@ -1,48 +1,13 @@ { "info": { - "_postman_id": "fcea09d2-13d9-49ce-8c44-d3cb3078eb82", - "name": "EDC", + "_postman_id": "64e409d9-3fd0-4b85-8de9-df1094a58400", + "name": "tractusx-edc_dsp", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "6134257" + "_exporter_id": "27652630" }, "item": [ { - "name": "Asset", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{PROVIDER_MANAGEMENT_URL}}/assets/{{ASSET_ID}}", - "host": [ - "{{PROVIDER_MANAGEMENT_URL}}" - ], - "path": [ - "assets", - "{{ASSET_ID}}" - ] - } - }, - "response": [] - }, - { - "name": "Assets", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{PROVIDER_MANAGEMENT_URL}}/assets", - "host": [ - "{{PROVIDER_MANAGEMENT_URL}}" - ], - "path": [ - "assets" - ] - } - }, - "response": [] - }, - { - "name": "Asset", + "name": "Create Asset", "event": [ { "listen": "test", @@ -61,7 +26,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"asset\": {\n \"properties\": {\n \"asset:prop:id\": \"{{ASSET_ID}}\",\n \"asset:prop:description\": \"Tractus-X EDC Demo Asset\"\n }\n },\n \"dataAddress\": {\n \"properties\": {\n \"type\": \"HttpData\",\n \"baseUrl\": \"https://jsonplaceholder.typicode.com/todos/1\"\n }\n }\n}", + "raw": "{\n \"@context\": {},\n \"asset\": {\n \"@type\": \"Asset\",\n \"@id\": \"{{ASSET_ID}}\", \n \"properties\": {\n \"description\": \"Product EDC Demo Asset\"\n }\n },\n \"dataAddress\": {\n \"@type\": \"DataAddress\",\n \"type\": \"HttpData\",\n \"baseUrl\": \"https://jsonplaceholder.typicode.com/todos\"\n }\n}", "options": { "raw": { "language": "json" @@ -81,90 +46,67 @@ "response": [] }, { - "name": "Asset", + "name": "Get all Assets", "request": { - "method": "DELETE", + "method": "POST", "header": [], "url": { - "raw": "{{PROVIDER_MANAGEMENT_URL}}/assets/{{ASSET_ID}}", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/assets/request", "host": [ "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ "assets", - "{{ASSET_ID}}" + "request" ] } }, "response": [] }, { - "name": "Policy", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, + "name": "Get Asset by ID", "request": { "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { - "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions/{{POLICY_ID}}", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/assets/{{ASSET_ID}}", "host": [ "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "policydefinitions", - "{{POLICY_ID}}" + "assets", + "{{ASSET_ID}}" ] } }, "response": [] }, { - "name": "Policies", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, + "name": "Delete Asset by ID", "request": { - "method": "GET", + "method": "DELETE", "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { - "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/assets/{{ASSET_ID}}", "host": [ "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "policydefinitions" + "assets", + "{{ASSET_ID}}" ] } }, "response": [] }, { - "name": "Policy (Public)", + "name": "Create Policy", "request": { "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", + "raw": "{\n \"@context\": {\n \"odrl\": \"http://www.w3.org/ns/odrl/2/\"\n },\n \"@type\": \"PolicyDefinitionRequestDto\",\n \"@id\": \"{{POLICY_ID}}\",\n \"policy\": {\n\t\t\"@type\": \"Policy\",\n\t\t\"odrl:permission\" : [{\n\t\t\t\"odrl:action\" : \"USE\",\n\t\t\t\"odrl:constraint\" : {\n\t\t\t\t\"@type\": \"LogicalConstraint\",\n\t\t\t\t\"odrl:or\" : [{\n\t\t\t\t\t\"@type\" : \"Constraint\",\n\t\t\t\t\t\"odrl:leftOperand\" : \"BusinessPartnerNumber\",\n\t\t\t\t\t\"odrl:operator\" : \"EQ\",\n\t\t\t\t\t\"odrl:rightOperand\" : \"{{POLICY_BPN}}\"\n\t\t\t\t}]\n\t\t\t}\n\t\t}]\n }\n}", "options": { "raw": { "language": "json" @@ -184,13 +126,16 @@ "response": [] }, { - "name": "Policy (Properties)", + "name": "Get Policy by ID", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, "request": { - "method": "POST", + "method": "GET", "header": [], "body": { "mode": "raw", - "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ],\n \"extensibleProperties\": {\n \"foo\": \"bar\"\n }\n }\n}", + "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", "options": { "raw": { "language": "json" @@ -198,25 +143,26 @@ } }, "url": { - "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions/{{POLICY_ID}}", "host": [ "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "policydefinitions" + "policydefinitions", + "{{POLICY_ID}}" ] } }, "response": [] }, { - "name": "Policy (BPN)", + "name": "Get all Policies", "request": { "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": [\n {\n \"edctype\": \"AtomicConstraint\",\n \"leftExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"BusinessPartnerNumber\"\n },\n \"rightExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"{{POLICY_BPN}}\"\n },\n \"operator\": \"EQ\"\n }\n ]\n }\n ]\n }\n}", + "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", "options": { "raw": { "language": "json" @@ -224,19 +170,20 @@ } }, "url": { - "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions/request", "host": [ "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "policydefinitions" + "policydefinitions", + "request" ] } }, "response": [] }, { - "name": "Policy", + "name": "Delte Policy by ID", "request": { "method": "DELETE", "header": [], @@ -263,16 +210,13 @@ "response": [] }, { - "name": "Contract Definition", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, + "name": "Create Contract Definitiion", "request": { - "method": "GET", + "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", + "raw": "{\n \"@context\": {},\n \"@id\": \"{{CONTRACT_DEFINITION_ID}}\",\n \"@type\": \"ContractDefinition\",\n \"accessPolicyId\": \"{{ACCESS_POLICY_ID}}\",\n \"contractPolicyId\": \"{{CONTRACT_POLICY_ID}}\",\n \"assetsSelector\" : {\n \"@type\" : \"CriterionDto\",\n \"operandLeft\": \"{{EDC_NAMESPACE}}id\",\n \"operator\": \"=\",\n \"operandRight\": \"{{ASSET_ID}}\"\n }\n}", "options": { "raw": { "language": "json" @@ -280,20 +224,19 @@ } }, "url": { - "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions/{{POLICY_ID}}", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions", "host": [ "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "contractdefinitions", - "{{POLICY_ID}}" + "contractdefinitions" ] } }, "response": [] }, { - "name": "Contract Definitiions", + "name": "Get Contract Definition by ID", "protocolProfileBehavior": { "disableBodyPruning": true }, @@ -310,25 +253,26 @@ } }, "url": { - "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions/{{CONTRACT_DEFINITION_ID}}", "host": [ "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "contractdefinitions" + "contractdefinitions", + "{{CONTRACT_DEFINITION_ID}}" ] } }, "response": [] }, { - "name": "Contract Definitiion", + "name": "Get all Contract Definitiions", "request": { "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "{\n \"id\": \"{{CONTRACT_DEFINITION_ID}}\",\n \"criteria\": [\n {\n \"operandLeft\": \"asset:prop:id\",\n \"operator\": \"=\",\n \"operandRight\": \"{{ASSET_ID}}\"\n }\n ],\n \"accessPolicyId\": \"{{ACCESS_POLICY_ID}}\",\n \"contractPolicyId\": \"{{CONTRACT_POLICY_ID}}\"\n}", + "raw": "", "options": { "raw": { "language": "json" @@ -336,19 +280,20 @@ } }, "url": { - "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions/request", "host": [ "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "contractdefinitions" + "contractdefinitions", + "request" ] } }, "response": [] }, { - "name": "Contract Definition", + "name": "Delte Contract Definition", "request": { "method": "DELETE", "header": [], @@ -362,40 +307,13 @@ } }, "url": { - "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions/{{POLICY_ID}}", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions/{{CONTRACT_DEFINITION_ID}}", "host": [ "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ "contractdefinitions", - "{{POLICY_ID}}" - ] - } - }, - "response": [] - }, - { - "name": "Catalog", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{CONSUMER_MANAGEMENT_URL}}/catalog?providerUrl={{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data&size=50", - "host": [ - "{{CONSUMER_MANAGEMENT_URL}}" - ], - "path": [ - "catalog" - ], - "query": [ - { - "key": "providerUrl", - "value": "{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data" - }, - { - "key": "size", - "value": "50" - } + "{{CONTRACT_DEFINITION_ID}}" ] } }, @@ -408,7 +326,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"providerUrl\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\",\r\n \"querySpec\": {\r\n \"offset\": 0,\r\n \"limit\": 100,\r\n \"sort\": \"ASC\",\r\n \"sortField\": \"\"\r\n }\r\n}", + "raw": "{\r\n \"@context\": {},\r\n \"protocol\": \"dataspace-protocol-http\",\r\n \"providerUrl\": \"{{PROVIDER_PROTOCOL_URL}}\",\r\n \"querySpec\": {\r\n \"offset\": 0,\r\n \"limit\": 100,\r\n \"filter\": \"\",\r\n \"range\": {\r\n \"from\": 0,\r\n \"to\": 100\r\n },\r\n \"sortField\": \"\",\r\n \"criterion\": \"\"\r\n }\r\n}", "options": { "raw": { "language": "json" @@ -429,7 +347,7 @@ "response": [] }, { - "name": "Negotation (Public)", + "name": "Initiate Negotation", "event": [ { "listen": "test", @@ -450,7 +368,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{CONTRACT_DEFINITION_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": []\n }\n ]\n }\n }\n}", + "raw": "{\n\t\"@context\": {\n\t\t\"odrl\": \"http://www.w3.org/ns/odrl/2/\"\n\t},\n\t\"@type\": \"NegotiationInitiateRequestDto\",\n\t\"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}\",\n\t\"protocol\": \"dataspace-protocol-http\",\n\t\"connectorId\": \"{{PROVIDER_ID}}\",\n\t\"providerId\": \"{{PROVIDER_ID}}\",\n\t\"offer\": {\n\t\t\"offerId\": \"1:1:46483724-18f1-4dff-87da-f26725dcc59c\",\n\t\t\"assetId\": \"{{ASSET_ID}}\",\n\t\t\"policy\": {\n\t\t\t\"@type\": \"odrl:Set\",\n\t\t\t\"odrl:permission\": {\n\t\t\t\t\"odrl:target\": \"{{ASSET_ID}}\",\n\t\t\t\t\"odrl:action\": {\n\t\t\t\t\t\"odrl:type\": \"USE\"\n\t\t\t\t},\n\t\t\t\t\"odrl:constraint\": {\n\t\t\t\t\t\"odrl:or\": {\n\t\t\t\t\t\t\"odrl:leftOperand\": \"BusinessPartnerNumber\",\n\t\t\t\t\t\t\"odrl:operator\": \"EQ\",\n\t\t\t\t\t\t\"odrl:rightOperand\": \"{{POLICY_BPN}}\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"odrl:prohibition\": [],\n\t\t\t\"odrl:obligation\": [],\n\t\t\t\"odrl:target\": \"{{ASSET_ID}}\"\n\t\t}\n\t}\n}", "options": { "raw": { "language": "json" @@ -470,17 +388,22 @@ "response": [] }, { - "name": "Negotation (Properties)", + "name": "Get all Negotations", "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + }, { "listen": "test", "script": { "exec": [ - "pm.test(\"Body matches string\", function () {", - " var jsonData = pm.response.json();", - " pm.collectionVariables.set(\"NEGOTIATION_ID\", jsonData.id);", - "", - "});" + "" ], "type": "text/javascript" } @@ -489,38 +412,77 @@ "request": { "method": "POST", "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{POLICY_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": []\n }\n ],\n \"extensibleProperties\": {\n \"foo\": \"bar\"\n }\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } + "url": { + "raw": "{{CONSUMER_MANAGEMENT_URL}}/contractnegotiations/request", + "host": [ + "{{CONSUMER_MANAGEMENT_URL}}" + ], + "path": [ + "contractnegotiations", + "request" + ] + } + }, + "response": [] + }, + { + "name": "Cancel Negotation by ID", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" } }, + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], "url": { - "raw": "{{CONSUMER_MANAGEMENT_URL}}/contractnegotiations", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/contractnegotiations/7cff6ecb-7e5e-40b8-b101-eba3f2045b1f/cancel", "host": [ "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "contractnegotiations" + "contractnegotiations", + "7cff6ecb-7e5e-40b8-b101-eba3f2045b1f", + "cancel" ] } }, "response": [] }, { - "name": "Negotation (BPN)", + "name": "Initiate Transfer", "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.collectionVariables.set(\"TRANSFER_ID\", Math.random());" + ], + "type": "text/javascript" + } + }, { "listen": "test", "script": { "exec": [ "pm.test(\"Body matches string\", function () {", " var jsonData = pm.response.json();", - " pm.collectionVariables.set(\"NEGOTIATION_ID\", jsonData.id);", - "", + " pm.collectionVariables.set(\"TRANSFER_PROCESS_ID\", jsonData.id);", "});" ], "type": "text/javascript" @@ -532,7 +494,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{POLICY_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": [\n {\n \"edctype\": \"AtomicConstraint\",\n \"leftExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"BusinessPartnerNumber\"\n },\n \"rightExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"{{POLICY_BPN}}\"\n },\n \"operator\": \"EQ\"\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"@context\": {\n \"odrl\": \"http://www.w3.org/ns/odrl/2/\"\n },\n \"assetId\": \"{{ASSET_ID}}\",\n \"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}\",\n \"contractId\": \"\",\n \"dataDestination\": {\n \"properties\": {\n \"type\": \"HttpProxy\"\n }\n },\n \"managedResources\": false,\n \"privateProperties\": {\n \"receiverHttpEndpoint\": \"{{BACKEND_SERVICE}}\"\n },\n \"protocol\": \"dataspace-protocol-http\",\n \"transferType\": {\n \"contentType\": \"application/octet-stream\",\n \"isFinite\": true\n }\n}", "options": { "raw": { "language": "json" @@ -540,19 +502,19 @@ } }, "url": { - "raw": "{{CONSUMER_MANAGEMENT_URL}}/contractnegotiations", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/transferprocesses", "host": [ "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "contractnegotiations" + "transferprocesses" ] } }, "response": [] }, { - "name": "Negotation (init AGREEMENT_ID)", + "name": "Get Transfer by ID", "event": [ { "listen": "prerequest", @@ -567,10 +529,7 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Body matches string\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.collectionVariables.set(\"AGREEMENT_ID\", jsonData.contractAgreementId);\r", - "});" + "" ], "type": "text/javascript" } @@ -580,26 +539,26 @@ "method": "GET", "header": [], "url": { - "raw": "{{CONSUMER_MANAGEMENT_URL}}/contractnegotiations/{{NEGOTIATION_ID}}", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/transferprocesses/8e428b80-46a5-4325-87e5-592518f7666b", "host": [ "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "contractnegotiations", - "{{NEGOTIATION_ID}}" + "transferprocesses", + "8e428b80-46a5-4325-87e5-592518f7666b" ] } }, "response": [] }, { - "name": "Transfer", + "name": "Get all Transfers", "event": [ { "listen": "prerequest", "script": { "exec": [ - "pm.collectionVariables.set(\"TRANSFER_ID\", Math.random());" + "" ], "type": "text/javascript" } @@ -608,10 +567,7 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Body matches string\", function () {", - " var jsonData = pm.response.json();", - " pm.collectionVariables.set(\"TRANSFER_PROCESS_ID\", jsonData.id);", - "});" + "" ], "type": "text/javascript" } @@ -622,7 +578,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{ \"id\": \"{{TRANSFER_ID}}\",\n \"connectorId\": \"foo\", \n \"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\", \n \"contractId\": \"{{AGREEMENT_ID}}\", \n \"assetId\": \"{{ASSET_ID}}\",\n \"managedResources\": \"false\", \n \"dataDestination\": { \"type\": \"HttpProxy\" }\n}", + "raw": "{}", "options": { "raw": { "language": "json" @@ -630,36 +586,29 @@ } }, "url": { - "raw": "{{CONSUMER_MANAGEMENT_URL}}/transferprocess", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/transferprocesses/request", "host": [ "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "transferprocess" + "transferprocesses", + "request" ] } }, "response": [] }, { - "name": "Transfer dynamic http receiver", + "name": "Initiate EDR Negotation", "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "pm.collectionVariables.set(\"TRANSFER_ID\", Math.random());" - ], - "type": "text/javascript" - } - }, { "listen": "test", "script": { "exec": [ "pm.test(\"Body matches string\", function () {", " var jsonData = pm.response.json();", - " pm.collectionVariables.set(\"TRANSFER_PROCESS_ID\", jsonData.id);", + " pm.collectionVariables.set(\"NEGOTIATION_ID\", jsonData.id);", + "", "});" ], "type": "text/javascript" @@ -671,7 +620,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{ \"id\": \"{{TRANSFER_ID}}\",\n \"connectorId\": \"foo\", \n \"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\", \n \"contractId\": \"{{AGREEMENT_ID}}\", \n \"assetId\": \"{{ASSET_ID}}\",\n \"managedResources\": \"false\", \n \"dataDestination\": { \"type\": \"HttpProxy\" },\n \"properties\": {\n \"receiver.http.endpoint\": \"{{BACKEND_SERVICE}}\"\n }\n}", + "raw": "{\n\t\"@context\": {\n\t\t\"odrl\": \"http://www.w3.org/ns/odrl/2/\"\n\t},\n\t\"@type\": \"NegotiationInitiateRequestDto\",\n\t\"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}\",\n\t\"protocol\": \"dataspace-protocol-http\",\n\t\"connectorId\": \"{{PROVIDER_ID}}\",\n\t\"providerId\": \"{{PROVIDER_ID}}\",\n\t\"offer\": {\n\t\t\"offerId\": \"1:1:9f9375e3-ed28-449a-8a98-a340f4c20c26\",\n\t\t\"assetId\": \"{{ASSET_ID}}\",\n\t\t\"policy\": {\n\t\t\t\"@type\": \"odrl:Set\",\n\t\t\t\"odrl:permission\": {\n\t\t\t\t\"odrl:target\": \"{{ASSET_ID}}\",\n\t\t\t\t\"odrl:action\": {\n\t\t\t\t\t\"odrl:type\": \"USE\"\n\t\t\t\t},\n\t\t\t\t\"odrl:constraint\": {\n\t\t\t\t\t\"odrl:or\": {\n\t\t\t\t\t\t\"odrl:leftOperand\": \"BusinessPartnerNumber\",\n\t\t\t\t\t\t\"odrl:operator\": \"EQ\",\n\t\t\t\t\t\t\"odrl:rightOperand\": \"{{POLICY_BPN}}\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"odrl:prohibition\": [],\n\t\t\t\"odrl:obligation\": [],\n\t\t\t\"odrl:target\": \"{{ASSET_ID}}\"\n\t\t}\n\t}\n}", "options": { "raw": { "language": "json" @@ -679,65 +628,123 @@ } }, "url": { - "raw": "{{CONSUMER_MANAGEMENT_URL}}/transferprocess", + "raw": "{{CONSUMER_ADAPTER_URL}}/edrs", "host": [ - "{{CONSUMER_MANAGEMENT_URL}}" + "{{CONSUMER_ADAPTER_URL}}" ], "path": [ - "transferprocess" + "edrs" ] } }, "response": [] }, { - "name": "Transfer", + "name": "Query EDRs Cached", "event": [ { - "listen": "prerequest", + "listen": "test", "script": { "exec": [ - "" + "pm.test(\"Body matches string\", function () {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set(\"NEGOTIATION_ID\", jsonData.id);", + "", + "});" ], "type": "text/javascript" } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } }, + "url": { + "raw": "{{CONSUMER_ADAPTER_URL}}/edrs?assetId={{ASSET_ID}}", + "host": [ + "{{CONSUMER_ADAPTER_URL}}" + ], + "path": [ + "edrs" + ], + "query": [ + { + "key": "assetId", + "value": "{{ASSET_ID}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Get EDR by tp ID", + "event": [ { "listen": "test", "script": { "exec": [ - "" + "pm.test(\"Body matches string\", function () {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set(\"NEGOTIATION_ID\", jsonData.id);", + "", + "});" ], "type": "text/javascript" } } ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, "request": { "method": "GET", "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, "url": { - "raw": "{{CONSUMER_MANAGEMENT_URL}}/transferprocess/{{TRANSFER_PROCESS_ID}}", + "raw": "{{CONSUMER_ADAPTER_URL}}/edrs/4b383155-9147-4912-819e-6172b4a3eb02", "host": [ - "{{CONSUMER_MANAGEMENT_URL}}" + "{{CONSUMER_ADAPTER_URL}}" ], "path": [ - "transferprocess", - "{{TRANSFER_PROCESS_ID}}" + "edrs", + "4b383155-9147-4912-819e-6172b4a3eb02" ] } }, "response": [] }, { - "name": "CPA (getData)", + "name": "Get Asset Data with proxy", "event": [ { "listen": "test", "script": { "exec": [ - "pm.test(\"Body matches string\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.collectionVariables.set(\"authCode\", jsonData.authCode);\r", + "pm.test(\"Body matches string\", function () {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set(\"NEGOTIATION_ID\", jsonData.id);", + "", "});" ], "type": "text/javascript" @@ -745,28 +752,28 @@ } ], "request": { - "method": "GET", + "method": "POST", "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"assetId\": \"{{ASSET_ID}}\",\n \"endpointUrl\": \"http://plato-dataplane:8080/api/gateway/aas/1\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, "url": { - "raw": "{{CONSUMER_MANAGEMENT_URL}}/adapter/asset/sync/{{ASSET_ID}}?providerUrl={{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data&contractAgreementReuse=false", + "raw": "http://localhost:8186/proxy/aas/request", + "protocol": "http", "host": [ - "{{CONSUMER_MANAGEMENT_URL}}" + "localhost" ], + "port": "8186", "path": [ - "adapter", - "asset", - "sync", - "{{ASSET_ID}}" - ], - "query": [ - { - "key": "providerUrl", - "value": "{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data" - }, - { - "key": "contractAgreementReuse", - "value": "false" - } + "proxy", + "aas", + "request" ] } }, @@ -778,7 +785,7 @@ "apikey": [ { "key": "value", - "value": "", + "value": "password", "type": "string" }, { @@ -811,15 +818,15 @@ "variable": [ { "key": "CONSUMER_MANAGEMENT_URL", - "value": "https://sokrates-txdc.int.demo.catena-x.net/management" + "value": "http://localhost:31364/management/v2" }, { "key": "PROVIDER_PROTOCOL_URL", - "value": "https://plato-txdc.int.demo.catena-x.net" + "value": "http://plato-controlplane:8084/api/v1/dsp" }, { "key": "PROVIDER_MANAGEMENT_URL", - "value": "https://plato-txdc.int.demo.catena-x.net/management" + "value": "http://localhost:30279/management/v2" }, { "key": "ASSET_ID", @@ -879,6 +886,21 @@ { "key": "authCode", "value": "" + }, + { + "key": "PROVIDER_ID", + "value": "BPNPLATO", + "type": "string" + }, + { + "key": "EDC_NAMESPACE", + "value": "https://w3id.org/edc/v0.0.1/ns/", + "type": "string" + }, + { + "key": "CONSUMER_ADAPTER_URL", + "value": "http://localhost:31364/management/adapter", + "type": "string" } ] } \ No newline at end of file diff --git a/docs/migration/Version_0.3.4_0.4.0.md b/docs/migration/Version_0.3.4_0.4.0.md index cca287921..5090fc137 100644 --- a/docs/migration/Version_0.3.4_0.4.0.md +++ b/docs/migration/Version_0.3.4_0.4.0.md @@ -1,4 +1,4 @@ -# Migration from 0.3.3 to 0.3.4 +# Migration from 0.3.4 to 0.4.0 ## Switching to DSP @@ -10,9 +10,16 @@ From the Tractus-X EDC perspective this causes breaking changes in the following The old Management API is now deprecated and is **not** tested for compliance. Please upgrade using the `/v2/` path for every endpoint, e.g. `/management/v2/assets`. Please also refer to the [EDC OpenAPI spec](https://app.swaggerhub.com/apis/eclipse-edc-bot/management-api/0.0.1-SNAPSHOT#/). + An updated postman collection with the `v2` flow is available [here](../development/postman/collection.json) + - modules: all `*ids*` modules are deprecated and cannot be used anymore. Please migrate over to `org.eclipse.edc:dsp:0.0.1-milestone-9`. +- path: the default protocol path is now `/api/v1/dsp` instead of `/api/v1/ids` + +- `edc.participant.id`: new mandatory configuration for the participant id in the dataspace (BPN number). + It's configured via mandatory property in the charts with object ```yaml participant: id: "id"```. + **Please note that this is not a complete documentation of the DSP Protocol, please refer to the [official documentation](https://docs.internationaldataspaces.org/dataspace-protocol/overview/readme)** @@ -27,3 +34,65 @@ The Business tests were brittle, consumed a lot of resources and were quite cumb Since the old Control-Plane-Adapter is incompatible with DSP, a new iteration was created. **Due to time constraints with this release documentation for this feature will to be published subsequently** + +## New Policies for expressing validity of the agreement + +The dates in `ContractOffer` and `ContractAgreement` has been removed in favour of a policy based contract validity check, see [here](https://github.com/eclipse-edc/Connector/issues/2758) + +## Other changes + +- When using the EDR [HttpDynamicReceiverExtension](https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/transfer/transfer-pull-http-dynamic-receiver) in the transfer process initiation + the properties for configuring the receiver on single transfer process has been changed to: + + ```json + "privateProperties": { + "receiverHttpEndpoint": "{{BACKEND_SERVICE}}" + } + ``` + + instead of: + + ```json + "properties": { + "receiver.http.endpoint": "{{BACKEND_SERVICE}}" + } + ``` + +## New Catalog + +The DSP catalog is expressed as [DCat Catalog](https://www.w3.org/TR/vocab-dcat-3/), when querying the catalog a response like this will return: + +```json +{ + "@id": "5a3207ae-bd0d-4a3b-bc8a-05adfbe75d95", + "@type": "dcat:Catalog", + "dcat:dataset": { + "@id": "e6279569-17a9-4ba3-9401-a8ae4100e4eb", + "@type": "dcat:Dataset", + "odrl:hasPolicy": { + "@id": "2:1:535def6e-8321-4c0e-a595-aabdd9c18eed", + "@type": "odrl:Set", + "odrl:permission": [], + "odrl:prohibition": [], + "odrl:obligation": [], + "odrl:target": "1" + }, + "dcat:distribution": [ + ... + ], + "edc:description": "Product EDC Demo Asset", + "edc:id": "1" + }, + "dcat:service": { + ... + }, + "edc:participantId": "participantId", + "@context": { + } +} +``` + +When starting a new contract negotiation for an asset: + +- the `@id` of `odrl:hasPolicy` object should be passed in the `offerId` field +- the `edc:participantId` should be passed in the `providerId` and `connectorId` fields. `connectorId` it's still needed for backward compatibility and it will probably be removed in the next versions. diff --git a/docs/samples/example-dataspace/README.md b/docs/samples/example-dataspace/README.md new file mode 100644 index 000000000..9842186cd --- /dev/null +++ b/docs/samples/example-dataspace/README.md @@ -0,0 +1,119 @@ +# How-To run two connectors and a DAPS + +## 1. Prepare environment + +This guide will bring up two connectors named "Sokrates" and "Plato", each alongside their dependencies (Hashicorp +Vault, PostgreSQL) and a DAPS instance that both share. + +We've tested this setup with [KinD](https://kind.sigs.k8s.io/), but other runtimes such +as [Minikube](https://minikube.sigs.k8s.io/docs/start/) may work as well, we just haven't tested them. + +Furthermore, this guide assumes: + +- the Tractus-X EDC repository is checked out, the working directory for this guide is `docs/samples/example-dataspace` +- a Kubernetes runtime (e.g. KinD) is already installed and ready-to-use +- basic knowledge about `helm` and Kubernetes +- the following tools are available: `yq`, `openssl`, `base64` +- a POSIX-compliant shell, e.g. `bash` or `zsh` unless stated otherwise + +### 1.1 Create certificates for both runtimes + +We'll need a x509 certificate in order to communicate with DAPS, as well as a private key and a Data Encryption signing +key. + +```shell +# SOKRATES key/cert for daps +openssl req -newkey rsa:2048 -new -nodes -x509 -days 1 -keyout sokrates.key -out sokrates.cert -subj "/CN=test" +echo "aes_enckey_test" | base64 > sokrates.aes.key + +# PLATO key/cert for daps +openssl req -newkey rsa:2048 -new -nodes -x509 -days 1 -keyout plato.key -out plato.cert -subj "/CN=test" +echo "aes_enckey_test" | base64 > plato.aes.key +``` + +Any arbitrary string can be used for the AES key, but it has to be 16, 24, or 32 characters in length, assuming UTF-8 +encoding. + +### 1.2 Modify the DAPS's `values.yaml` located at `daps/values.yaml` + +With the following command, we "inject" the previously created certificates and client ids into the DAPS's config: + +```shell +VALUES_FILE=daps/values.yaml + +# Add both public keys to daps +yq -i ".connectors[0].certificate=\"$(cat sokrates.cert)\"" "$VALUES_FILE" +yq -i ".connectors[1].certificate=\"$(cat plato.cert)\"" "$VALUES_FILE" +``` + +### 1.3 Install/Launch DAPS + +`helm install daps daps/` + +## 2. Prepare Connectors + +Next, the certificates and private keys we created previously must be stored in each connector's vault by injecting +a `postStart` element to the chart's configuration file: + +```shell +# for sokrates +CONFIG_FILE=sokrates-values.yaml + +yq -i ".vault.server.postStart |= [\"sh\",\"-c\",\"{\nsleep 5\n\ncat << EOF | /bin/vault kv put secret/daps-crt content=-\n$(cat sokrates.cert)\nEOF\n\n +cat << EOF | /bin/vault kv put secret/daps-key content=-\n$(cat sokrates.key)\nEOF\n\n +/bin/vault kv put secret/aes-keys content=$(cat sokrates.aes.key)\n\n}\"]" "$CONFIG_FILE" + +# for plato +CONFIG_FILE=plato-values.yaml + +yq -i ".vault.server.postStart |= [\"sh\",\"-c\",\"{\nsleep 5\n\ncat << EOF | /bin/vault kv put secret/daps-crt content=-\n$(cat plato.cert)\nEOF\n\n +cat << EOF | /bin/vault kv put secret/daps-key content=-\n$(cat plato.key)\nEOF\n\n +/bin/vault kv put secret/aes-keys content=$(cat plato.aes.key)\n\n}\"]" "$CONFIG_FILE" +``` + +## 3 Install the connectors + +Use `helm` to install the Tractus-X EDC Helm charts. In this example we are using the _local_ charts, assuming you have +Tractus-X EDC checked out in your local filesystem at ``. + +```shell +# install sokrates +helm install tx-sokrates /charts/tractusx-connector \ + -f sokrates-values.yaml \ + --dependency-update + +# install plato +helm install tx-plato /charts/tractusx-connector \ + -f plato-values.yaml \ + --dependency-update +``` + +_Note: if you prefer to use the published version of the `tractusx-connector` chart, please add the Tractus-X Helm repo +first:_ + +```shell +helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev +helm install tx-[sokrates|plato] tractusx-edc/tractusx-connector \ + -f [sokrates|plato]-values.yaml \ + --dependency-update +``` + +## 3.1 [Optional] Verify the correct installation + +There is several ways of making sure everything worked out well: + +- simply look at the logs of the Helm releases, e.g. with a tool + like [stern](https://kubernetes.io/blog/2016/10/tail-kubernetes-with-stern/) and look out for a log line similar to: + + ```shell + stern tx-sokrates + ``` + + then look out for something similar to: + + ```shell + tx-sokrates-controlplane-b9456f97b-s5jts tractusx-connector INFO 2023-05-31T07:24:53.020975888 tx-sokrates-controlplane ready + ``` + +- wait for the Kubernetes rollout to be successful, e.g. `kubectl rollout status deployment tx-plato-controlplane` +- use `helm test` to execute tests: `helm test tx-plato` diff --git a/docs/samples/example-dataspace/daps/.helmignore b/docs/samples/example-dataspace/daps/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/docs/samples/example-dataspace/daps/.helmignore @@ -0,0 +1,23 @@ +# 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/edc-tests/deployment/src/main/resources/helm/omejdn/Chart.yaml b/docs/samples/example-dataspace/daps/Chart.yaml similarity index 99% rename from edc-tests/deployment/src/main/resources/helm/omejdn/Chart.yaml rename to docs/samples/example-dataspace/daps/Chart.yaml index b12948a5d..f0a4e6e4e 100644 --- a/edc-tests/deployment/src/main/resources/helm/omejdn/Chart.yaml +++ b/docs/samples/example-dataspace/daps/Chart.yaml @@ -18,7 +18,7 @@ --- apiVersion: v2 -name: ids-daps +name: daps description: A Helm chart for Kubernetes # A chart can be either an 'application' or a 'library' chart. diff --git a/docs/samples/example-dataspace/daps/README.md b/docs/samples/example-dataspace/daps/README.md new file mode 100644 index 000000000..1f0c20e36 --- /dev/null +++ b/docs/samples/example-dataspace/daps/README.md @@ -0,0 +1,39 @@ +# 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/docs/samples/example-dataspace/daps/templates/_helpers.tpl b/docs/samples/example-dataspace/daps/templates/_helpers.tpl new file mode 100644 index 000000000..95b115eee --- /dev/null +++ b/docs/samples/example-dataspace/daps/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +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/docs/samples/example-dataspace/daps/templates/configmap.yaml b/docs/samples/example-dataspace/daps/templates/configmap.yaml new file mode 100644 index 000000000..5ad21648d --- /dev/null +++ b/docs/samples/example-dataspace/daps/templates/configmap.yaml @@ -0,0 +1,92 @@ +# 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://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://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/docs/samples/example-dataspace/daps/templates/deployment.yaml b/docs/samples/example-dataspace/daps/templates/deployment.yaml new file mode 100644 index 000000000..58bfff105 --- /dev/null +++ b/docs/samples/example-dataspace/daps/templates/deployment.yaml @@ -0,0 +1,168 @@ +# 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/docs/samples/example-dataspace/daps/templates/hpa.yaml b/docs/samples/example-dataspace/daps/templates/hpa.yaml new file mode 100644 index 000000000..f1f072f6c --- /dev/null +++ b/docs/samples/example-dataspace/daps/templates/hpa.yaml @@ -0,0 +1,47 @@ +# 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/docs/samples/example-dataspace/daps/templates/imagepullsecret.yaml b/docs/samples/example-dataspace/daps/templates/imagepullsecret.yaml new file mode 100644 index 000000000..44f573e0f --- /dev/null +++ b/docs/samples/example-dataspace/daps/templates/imagepullsecret.yaml @@ -0,0 +1,31 @@ +# 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/docs/samples/example-dataspace/daps/templates/service.yaml b/docs/samples/example-dataspace/daps/templates/service.yaml new file mode 100644 index 000000000..947e69742 --- /dev/null +++ b/docs/samples/example-dataspace/daps/templates/service.yaml @@ -0,0 +1,34 @@ +# 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: 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 }} diff --git a/docs/samples/example-dataspace/daps/templates/serviceaccount.yaml b/docs/samples/example-dataspace/daps/templates/serviceaccount.yaml new file mode 100644 index 000000000..536f31871 --- /dev/null +++ b/docs/samples/example-dataspace/daps/templates/serviceaccount.yaml @@ -0,0 +1,31 @@ +# 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.serviceAccount.create -}} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "omejdn.serviceAccountName" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/docs/samples/example-dataspace/daps/values.yaml b/docs/samples/example-dataspace/daps/values.yaml new file mode 100644 index 000000000..3553dcc86 --- /dev/null +++ b/docs/samples/example-dataspace/daps/values.yaml @@ -0,0 +1,96 @@ +# 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 +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. +fullnameOverride: "daps" +url: "" +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 + - 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:69 + name: plato + attributes: + referringConnector: http://plato-controlplane/BPNPLATO diff --git a/docs/samples/example-dataspace/plato-values.yaml b/docs/samples/example-dataspace/plato-values.yaml new file mode 100644 index 000000000..21c5675d7 --- /dev/null +++ b/docs/samples/example-dataspace/plato-values.yaml @@ -0,0 +1,78 @@ +# +# 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 +# + + +install: + daps: false +fullnameOverride: tx-plato +################################ +# EDC ControlPlane + DataPlane # +################################ +participant: + id: "BPNPLATO" +controlplane: + service: + type: NodePort + endpoints: + management: + authKey: password + image: + pullPolicy: Never + tag: "latest" + repository: "edc-controlplane-postgresql-hashicorp-vault" + 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: + fullnameOverride: "plato-postgresql" + username: user + password: password + jdbcUrl: jdbc:postgresql://plato-postgresql:5432/edc +vault: + fullnameOverride: "plato-vault" + hashicorp: + url: http://plato-vault:8200 + token: root + secretNames: + transferProxyTokenSignerPublicKey: daps-crt + transferProxyTokenSignerPrivateKey: daps-key + transferProxyTokenEncryptionAesKey: aes-keys + dapsPrivateKey: daps-key + dapsPublicKey: daps-crt + # 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: +daps: + url: "http://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:69" +backendService: + httpProxyTokenReceiverUrl: "http://backend:8080" diff --git a/docs/samples/example-dataspace/sokrates-values.yaml b/docs/samples/example-dataspace/sokrates-values.yaml new file mode 100644 index 000000000..086eefde5 --- /dev/null +++ b/docs/samples/example-dataspace/sokrates-values.yaml @@ -0,0 +1,77 @@ +# +# 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 +# + +install: + daps: false +fullnameOverride: tx-sokrates +################################ +# EDC ControlPlane + DataPlane # +################################ +participant: + id: "BPNSOKRATES" +controlplane: + service: + type: NodePort + endpoints: + management: + authKey: password + image: + pullPolicy: Never + tag: "latest" + repository: "edc-controlplane-postgresql-hashicorp-vault" + 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: + fullnameOverride: "sokrates-postgresql" + username: user + password: password + jdbcUrl: jdbc:postgresql://sokrates-postgresql:5432/edc +vault: + fullnameOverride: "sokrates-vault" + hashicorp: + url: http://sokrates-vault:8200 + token: root + secretNames: + transferProxyTokenSignerPublicKey: daps-crt + transferProxyTokenSignerPrivateKey: daps-key + transferProxyTokenEncryptionAesKey: aes-keys + dapsPrivateKey: daps-key + dapsPublicKey: daps-crt + # 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: +daps: + url: "http://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" 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 2848f38e0..82e1dc8f7 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 @@ -25,7 +25,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in HEALTHCHECK NONE -RUN apk update && apk add curl=8.1.0-r0 --no-cache +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 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 90972bdef..29e0ffc36 100644 --- a/edc-controlplane/edc-controlplane-postgresql-azure-vault/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql-azure-vault/build.gradle.kts @@ -29,6 +29,7 @@ plugins { dependencies { runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) runtimeOnly(project(":edc-extensions:postgresql-migration")) + runtimeOnly(project(":edc-extensions:edr-cache-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 c2a217e50..b4faaf422 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 @@ -24,7 +24,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in HEALTHCHECK NONE -RUN apk update && apk add curl=8.1.0-r0 --no-cache +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 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 79ce6ef7b..8b8398dcc 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts @@ -30,6 +30,7 @@ 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(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 c2a217e50..b4faaf422 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 @@ -24,7 +24,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in HEALTHCHECK NONE -RUN apk update && apk add curl=8.1.0-r0 --no-cache +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 diff --git a/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java b/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java index c8d2cc7d2..434e7886f 100644 --- a/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java +++ b/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java @@ -17,7 +17,11 @@ import org.eclipse.edc.runtime.metamodel.annotation.Provider; import org.eclipse.edc.runtime.metamodel.annotation.Provides; import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.security.*; +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.VaultCertificateResolver; +import org.eclipse.edc.spi.security.VaultPrivateKeyResolver; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; diff --git a/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java index bec589d79..7012d5285 100644 --- a/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java +++ b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java @@ -22,7 +22,12 @@ import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.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 VaultMemoryExtensionTest { private VaultMemoryExtension extension; diff --git a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts index 134319535..43f401bb8 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts @@ -27,12 +27,16 @@ dependencies { implementation(project(":edc-dataplane:edc-dataplane-base")) implementation(libs.edc.azure.vault) constraints { - implementation("net.minidev:json-smart:2.4.10") { + implementation("net.minidev:json-smart:2.4.11") { because("version 2.4.8 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-1370.") } } implementation(libs.edc.azure.identity) - implementation("com.azure:azure-security-keyvault-secrets:4.6.0") + implementation("com.azure:azure-security-keyvault-secrets:4.6.2") + runtimeOnly(project(":edc-extensions:edr-cache-sql")) + runtimeOnly(libs.edc.transaction.local) + runtimeOnly(libs.edc.sql.pool) + runtimeOnly(libs.postgres) } tasks.withType { 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 25864f70a..32add3db5 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -24,7 +24,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in HEALTHCHECK NONE -RUN apk update && apk add curl=8.1.0-r0 --no-cache +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 diff --git a/edc-dataplane/edc-dataplane-base/build.gradle.kts b/edc-dataplane/edc-dataplane-base/build.gradle.kts index 09d88a015..4b2999a02 100644 --- a/edc-dataplane/edc-dataplane-base/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-base/build.gradle.kts @@ -23,7 +23,11 @@ plugins { } dependencies { + runtimeOnly(project(":core:edr-cache-core")) runtimeOnly(project(":edc-extensions:observability-api-customization")) + runtimeOnly(project(":edc-dataplane:edc-dataplane-proxy-consumer-api")) + runtimeOnly(project(":edc-dataplane:edc-dataplane-proxy-provider-api")) + runtimeOnly(project(":edc-dataplane:edc-dataplane-proxy-provider-core")) runtimeOnly(libs.edc.config.filesystem) runtimeOnly(libs.edc.dpf.awss3) diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts index 42bbd4d57..247744df2 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts @@ -26,6 +26,10 @@ 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.transaction.local) + runtimeOnly(libs.edc.sql.pool) + runtimeOnly(libs.postgres) } tasks.withType { 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 25864f70a..32add3db5 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -24,7 +24,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in HEALTHCHECK NONE -RUN apk update && apk add curl=8.1.0-r0 --no-cache +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 diff --git a/edc-dataplane/edc-dataplane-proxy-consumer-api/build.gradle.kts b/edc-dataplane/edc-dataplane-proxy-consumer-api/build.gradle.kts index 6af73c993..ee5edb153 100644 --- a/edc-dataplane/edc-dataplane-proxy-consumer-api/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-proxy-consumer-api/build.gradle.kts @@ -24,7 +24,6 @@ dependencies { implementation(libs.edc.spi.http) implementation(libs.edc.util) implementation(libs.edc.dpf.framework) - implementation(libs.edc.api.observability) implementation(libs.edc.dpf.util) implementation(libs.edc.ext.http) diff --git a/edc-dataplane/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java b/edc-dataplane/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java index 566a63b7b..048f56ead 100644 --- a/edc-dataplane/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java +++ b/edc-dataplane/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/DataPlaneProxyConsumerApiExtension.java @@ -25,8 +25,8 @@ import org.eclipse.edc.web.spi.WebService; import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer; import org.eclipse.edc.web.spi.configuration.WebServiceSettings; -import org.eclipse.tractusx.edc.dataplane.proxy.consumer.api.asset.ConsumerAssetRequestController; 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 java.util.concurrent.ExecutorService; @@ -38,21 +38,16 @@ */ @Extension(value = DataPlaneProxyConsumerApiExtension.NAME) public class DataPlaneProxyConsumerApiExtension implements ServiceExtension { + public static final int DEFAULT_THREAD_POOL = 10; static final String NAME = "Data Plane Proxy Consumer API"; - private static final int DEFAULT_PROXY_PORT = 8186; private static final String CONSUMER_API_ALIAS = "consumer.api"; private static final String CONSUMER_CONTEXT_PATH = "/proxy"; private static final String CONSUMER_CONFIG_KEY = "web.http.proxy"; - @Setting(value = "Data plane proxy API consumer port", type = "int") private static final String CONSUMER_PORT = "tx.dpf.consumer.proxy.port"; - @Setting(value = "Thread pool size for the consumer data plane proxy gateway", type = "int") private static final String THREAD_POOL_SIZE = "tx.dpf.consumer.proxy.thread.pool"; - - public static final int DEFAULT_THREAD_POOL = 10; - @Inject private WebService webService; diff --git a/edc-dataplane/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java b/edc-dataplane/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java index 2ceb4a393..89b715fad 100644 --- a/edc-dataplane/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java +++ b/edc-dataplane/edc-dataplane-proxy-consumer-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/consumer/api/asset/ConsumerAssetRequestController.java @@ -137,6 +137,8 @@ private void handleCompletion(AsyncResponse response, StreamResult result, case GENERAL_ERROR: response.resume(status(INTERNAL_SERVER_ERROR).type(APPLICATION_JSON).build()); break; + default: + throw new IllegalStateException("Unexpected value: " + result.reason()); } } else if (throwable != null) { reportError(response, throwable); diff --git a/edc-dataplane/edc-dataplane-proxy-provider-api/build.gradle.kts b/edc-dataplane/edc-dataplane-proxy-provider-api/build.gradle.kts index 37aabac01..9a9158299 100644 --- a/edc-dataplane/edc-dataplane-proxy-provider-api/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-proxy-provider-api/build.gradle.kts @@ -22,7 +22,6 @@ dependencies { implementation(libs.edc.spi.http) implementation(libs.edc.util) implementation(libs.edc.dpf.framework) - implementation(libs.edc.api.observability) implementation(libs.edc.dpf.util) implementation(libs.edc.ext.http) implementation(libs.edc.spi.jwt) diff --git a/edc-dataplane/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/gateway/ProviderGatewayController.java b/edc-dataplane/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/gateway/ProviderGatewayController.java index 2f392fdce..51e83c363 100644 --- a/edc-dataplane/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/gateway/ProviderGatewayController.java +++ b/edc-dataplane/edc-dataplane-proxy-provider-api/src/main/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/api/gateway/ProviderGatewayController.java @@ -51,7 +51,7 @@ * Implements the HTTP data proxy API. */ @Path("/" + ProviderGatewayController.GATEWAY_PATH) -public class ProviderGatewayController implements ProviderGatewayApi{ +public class ProviderGatewayController implements ProviderGatewayApi { protected static final String GATEWAY_PATH = "gateway"; private static final String HTTP_DATA = "HttpData"; @@ -185,6 +185,8 @@ private void handleCompletion(AsyncResponse response, StreamResult result, case GENERAL_ERROR: response.resume(status(INTERNAL_SERVER_ERROR).type(APPLICATION_JSON).build()); break; + default: + throw new IllegalStateException("Unexpected value: " + result.reason()); } } else if (throwable != null) { reportError(response, throwable); diff --git a/edc-dataplane/edc-dataplane-proxy-provider-core/build.gradle.kts b/edc-dataplane/edc-dataplane-proxy-provider-core/build.gradle.kts index fae93ad79..338cab554 100644 --- a/edc-dataplane/edc-dataplane-proxy-provider-core/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-proxy-provider-core/build.gradle.kts @@ -20,7 +20,6 @@ dependencies { implementation(libs.edc.util) implementation(libs.edc.dpf.framework) - implementation(libs.edc.api.observability) implementation(libs.edc.dpf.util) implementation(libs.edc.jwt.core) implementation(libs.edc.ext.http) diff --git a/edc-dataplane/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/RsaPublicKeyParserTest.java b/edc-dataplane/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/RsaPublicKeyParserTest.java index d345ce388..b6e62ccfa 100644 --- a/edc-dataplane/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/RsaPublicKeyParserTest.java +++ b/edc-dataplane/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/RsaPublicKeyParserTest.java @@ -16,7 +16,7 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Verifies RSA public key parsing. diff --git a/edc-dataplane/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/TestTokens.java b/edc-dataplane/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/TestTokens.java index 8db44bee8..9c8091014 100644 --- a/edc-dataplane/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/TestTokens.java +++ b/edc-dataplane/edc-dataplane-proxy-provider-core/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/provider/core/gateway/auth/TestTokens.java @@ -22,13 +22,14 @@ * Tokens for testing. */ public class TestTokens { + public static final String TEST_TOKEN = "eyJhbGciOiJSUzI1NiIsInZlcnNpb24iOnRydWV9.eyJpc3MiOiJ0ZXN0LWNvbm5lY3RvciIsInN1YiI6ImNvbnN1bWVyLWNvbm5lY3RvciIsImF1ZCI6InRlc3QtY29ubmV" + + "jdG9yIiwiaWF0IjoxNjgxOTEzNjM2LCJleHAiOjMzNDU5NzQwNzg4LCJjaWQiOiIzMmE2M2E3ZC04MGQ2LTRmMmUtOTBlNi04MGJhZjVmYzJiM2MifQ.QAuotoRxpEqfuzkTcTq2w5Tcyy3Rc3UzUjjvNc_zwgNROGLe-w" + + "O9tFET1dJ_I5BttRxkngDS37dS4R6lN5YXaGHgcH2rf_FuVcJUSFqTp_usGAcx6m7pQQwqpNdcYgmq0NJp3xP87EFPHAy4kBxB5bqpmx4J-zrj9U_gerZ2WlRqpu0SdgP0S5v5D1Gm-vYkLqgvsugrAWH3Ti7OjC5UMdj0k" + + "DFwro2NpMY8SSNryiVvBEv8hn0KZdhhebIqPdhqbEQZ9d8WKzcgoqQ3DBd4ijzkd3Fz7ADD2gy_Hxn8Hi2LcItuB514TjCxYAncTNqZC_JSFEyuxwcGFVz3LdSXgw"; private static final String DELIMITER = "-----"; private static final String HEADER = DELIMITER + "BEGIN" + " PUBLIC " + "KEY" + DELIMITER + "\n"; private static final String FOOTER = "\n" + DELIMITER + "END" + " PUBLIC " + "KEY" + DELIMITER + "\n"; - public static final String TEST_TOKEN = "eyJhbGciOiJSUzI1NiIsInZlcnNpb24iOnRydWV9.eyJpc3MiOiJ0ZXN0LWNvbm5lY3RvciIsInN1YiI6ImNvbnN1bWVyLWNvbm5lY3RvciIsImF1ZCI6InRlc3QtY29ubmVjdG9yIiwiaWF0IjoxNjgxOTEzNjM2LCJleHAiOjMzNDU5NzQwNzg4LCJjaWQiOiIzMmE2M2E3ZC04MGQ2LTRmMmUtOTBlNi04MGJhZjVmYzJiM2MifQ.QAuotoRxpEqfuzkTcTq2w5Tcyy3Rc3UzUjjvNc_zwgNROGLe-wO9tFET1dJ_I5BttRxkngDS37dS4R6lN5YXaGHgcH2rf_FuVcJUSFqTp_usGAcx6m7pQQwqpNdcYgmq0NJp3xP87EFPHAy4kBxB5bqpmx4J-zrj9U_gerZ2WlRqpu0SdgP0S5v5D1Gm-vYkLqgvsugrAWH3Ti7OjC5UMdj0kDFwro2NpMY8SSNryiVvBEv8hn0KZdhhebIqPdhqbEQZ9d8WKzcgoqQ3DBd4ijzkd3Fz7ADD2gy_Hxn8Hi2LcItuB514TjCxYAncTNqZC_JSFEyuxwcGFVz3LdSXgw"; - - public static String generatePublic() { try { var generator = KeyPairGenerator.getInstance("RSA"); diff --git a/edc-extensions/business-partner-validation/build.gradle.kts b/edc-extensions/business-partner-validation/build.gradle.kts index b4996ba37..6d5b6059a 100644 --- a/edc-extensions/business-partner-validation/build.gradle.kts +++ b/edc-extensions/business-partner-validation/build.gradle.kts @@ -23,6 +23,7 @@ plugins { } dependencies { + implementation(project(":spi:core-spi")) api(libs.edc.spi.core) implementation(libs.edc.spi.policy) implementation(libs.edc.spi.contract) 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 index 1786897bd..d88293a72 100644 --- 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 @@ -35,7 +35,6 @@ import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerProhibitionFunction; import static org.eclipse.edc.policy.engine.spi.PolicyEngine.ALL_SCOPES; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; public class BusinessPartnerValidationExtension implements ServiceExtension { @@ -55,8 +54,7 @@ public class BusinessPartnerValidationExtension implements ServiceExtension { * } * */ - // TODO replace with TX namespace - public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = EDC_NAMESPACE + "BusinessPartnerNumber"; + public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartnerNumber"; public static final String DEFAULT_LOG_AGREEMENT_EVALUATION = "true"; diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java index ecb5b81ef..cd124684b 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java @@ -55,7 +55,7 @@ public abstract class AbstractBusinessPartnerValidation { private static final String REFERRING_CONNECTOR_CLAIM = "referringConnector"; private final Monitor monitor; private final boolean logAgreementEvaluation; - + protected AbstractBusinessPartnerValidation(Monitor monitor, boolean logAgreementEvaluation) { this.monitor = Objects.requireNonNull(monitor); this.logAgreementEvaluation = logAgreementEvaluation; @@ -131,13 +131,7 @@ protected boolean evaluate( } } - /** - * @param referringConnectorClaim of the participant - * @param businessPartnerNumber object - * @return true if object is string and successfully evaluated against the claim - */ - private boolean isBusinessPartnerNumber( - String referringConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { + private boolean isBusinessPartnerNumber(String referringConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { if (businessPartnerNumber == null) { final String message = format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, "null"); monitor.warning(message); diff --git a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java b/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java index c6ed58e43..d7ad4db9f 100644 --- a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java +++ b/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java @@ -50,7 +50,7 @@ class AbstractBusinessPartnerValidationTest { private ParticipantAgent participantAgent; @BeforeEach - void BeforeEach() { + void beforeEach() { this.monitor = Mockito.mock(Monitor.class); this.policyContext = Mockito.mock(PolicyContext.class); this.participantAgent = Mockito.mock(ParticipantAgent.class); diff --git a/edc-extensions/control-plane-adapter-api/build.gradle.kts b/edc-extensions/control-plane-adapter-api/build.gradle.kts index 2541d1346..36e1437b9 100644 --- a/edc-extensions/control-plane-adapter-api/build.gradle.kts +++ b/edc-extensions/control-plane-adapter-api/build.gradle.kts @@ -20,6 +20,9 @@ plugins { dependencies { implementation(project(":spi:control-plane-adapter-spi")) + implementation(project(":spi:edr-cache-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/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterApiExtension.java b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterApiExtension.java index e67f35579..028468fed 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterApiExtension.java +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterApiExtension.java @@ -21,10 +21,14 @@ import org.eclipse.edc.spi.system.ServiceExtensionContext; 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.spi.cp.adapter.service.AdapterTransferProcessService; +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 { @Inject @@ -43,8 +47,10 @@ public class AdapterApiExtension implements ServiceExtension { @Override public void initialize(ServiceExtensionContext context) { + jsonLdService.registerNamespace(TX_PREFIX, TX_NAMESPACE); transformerRegistry.register(new NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer()); transformerRegistry.register(new JsonObjectToNegotiateEdrRequestDtoTransformer()); + transformerRegistry.register(new JsonObjectFromEndpointDataReferenceEntryTransformer()); webService.registerResource(apiConfig.getContextAlias(), new AdapterEdrController(adapterTransferProcessService, jsonLdService, transformerRegistry)); } } diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApi.java b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApi.java index d10d133ba..36d05c073 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApi.java +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApi.java @@ -22,9 +22,13 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.json.JsonObject; +import org.eclipse.edc.api.model.DataAddressDto; import org.eclipse.edc.api.model.IdResponseDto; 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; @OpenAPIDefinition @Tag(name = "Control Plane Adapter EDR Api") @@ -39,4 +43,25 @@ public interface AdapterEdrApi { content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), }) JsonObject initiateEdrNegotiation(@Schema(implementation = NegotiateEdrRequestDto.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 = "400", description = "Request was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class))))} + ) + List queryEdrs(String assetId, String agreementId); + + @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))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "404", description = "An EDR with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) + } + ) + JsonObject getEdr(String transferProcessId); } 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 index bfbbc483f..55a6b3e44 100644 --- 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 @@ -16,19 +16,30 @@ 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.edr.EndpointDataAddressConstants; +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}) @@ -40,6 +51,8 @@ public class AdapterEdrController implements AdapterEdrApi { 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; @@ -65,4 +78,36 @@ public JsonObject initiateEdrNegotiation(JsonObject requestObject) { .compose(jsonLdService::compact) .orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail())); } + + @GET + @Path("/{id}") + @Override + public JsonObject getEdr(@PathParam("id") String transferProcessId) { + var edr = adapterTransferProcessService.findByTransferProcessId(transferProcessId).orElseThrow(exceptionMapper(EndpointDataReference.class, transferProcessId)); + + return transformerRegistry.transform(EndpointDataAddressConstants.from(edr), 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()); + } + + private void logIfError(Result result) { + result.onFailure(f -> monitor.warning(f.getFailureDetail())); + } } 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/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/dto/NegotiateEdrRequestDto.java index 074c7017e..d37e618d4 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/dto/NegotiateEdrRequestDto.java +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/dto/NegotiateEdrRequestDto.java @@ -16,23 +16,24 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import org.eclipse.edc.api.model.CallbackAddressDto; import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription; +import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; import java.util.ArrayList; import java.util.List; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; public class NegotiateEdrRequestDto { - - public static final String TYPE = EDC_NAMESPACE + "NegotiateEdrRequestDto"; - public static final String CONNECTOR_ADDRESS = EDC_NAMESPACE + "connectorAddress"; - public static final String PROTOCOL = EDC_NAMESPACE + "protocol"; - public static final String CONNECTOR_ID = EDC_NAMESPACE + "connectorId"; - public static final String PROVIDER_ID = EDC_NAMESPACE + "providerId"; - public static final String OFFER = EDC_NAMESPACE + "offer"; - public static final String CALLBACK_ADDRESSES = EDC_NAMESPACE + "callbackAddresses"; + + public static final String EDR_REQUEST_DTO_TYPE = TX_NAMESPACE + "NegotiateEdrRequestDto"; + 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"; + public static final String EDR_REQUEST_DTO_PROVIDER_ID = EDC_NAMESPACE + "providerId"; + public static final String EDR_REQUEST_DTO_OFFER = EDC_NAMESPACE + "offer"; + public static final String EDR_REQUEST_DTO_CALLBACK_ADDRESSES = EDC_NAMESPACE + "callbackAddresses"; @NotBlank(message = "connectorAddress is mandatory") private String connectorAddress; @@ -45,7 +46,7 @@ public class NegotiateEdrRequestDto { @NotNull(message = "offer cannot be null") private ContractOfferDescription offer; - private List callbackAddresses = new ArrayList<>(); + private List callbackAddresses = new ArrayList<>(); private NegotiateEdrRequestDto() { @@ -67,7 +68,7 @@ public String getProviderId() { return providerId; } - public List getCallbackAddresses() { + public List getCallbackAddresses() { return callbackAddresses; } @@ -111,7 +112,7 @@ public Builder providerId(String providerId) { return this; } - public Builder callbackAddresses(List callbackAddresses) { + public Builder callbackAddresses(List callbackAddresses) { dto.callbackAddresses = callbackAddresses; return this; } 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/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java new file mode 100644 index 000000000..36676d0c3 --- /dev/null +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java @@ -0,0 +1,49 @@ +/* + * 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.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.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; + + +public class JsonObjectFromEndpointDataReferenceEntryTransformer extends AbstractJsonLdTransformer { + + public JsonObjectFromEndpointDataReferenceEntryTransformer() { + super(EndpointDataReferenceEntry.class, JsonObject.class); + } + + @Override + public @Nullable JsonObject transform(@NotNull EndpointDataReferenceEntry dto, @NotNull TransformerContext context) { + return Json.createObjectBuilder() + .add(TYPE, EDR_ENTRY_TYPE) + .add(EDR_ENTRY_AGREEMENT_ID, dto.getAgreementId()) + .add(EDR_ENTRY_TRANSFER_PROCESS_ID, dto.getTransferProcessId()) + .add(EDR_ENTRY_ASSET_ID, dto.getAssetId()) + .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/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java index 02a53ab18..8479852c6 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java @@ -16,9 +16,9 @@ import jakarta.json.JsonObject; import jakarta.json.JsonValue; -import org.eclipse.edc.api.model.CallbackAddressDto; import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription; import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer; +import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; import org.eclipse.edc.transform.spi.TransformerContext; import org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto; import org.jetbrains.annotations.NotNull; @@ -26,12 +26,13 @@ import java.util.ArrayList; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.CALLBACK_ADDRESSES; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.CONNECTOR_ADDRESS; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.CONNECTOR_ID; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.OFFER; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.PROTOCOL; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.PROVIDER_ID; +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; public class JsonObjectToNegotiateEdrRequestDtoTransformer extends AbstractJsonLdTransformer { @@ -50,38 +51,38 @@ public JsonObjectToNegotiateEdrRequestDtoTransformer() { private void setProperties(String key, JsonValue value, NegotiateEdrRequestDto.Builder builder, TransformerContext context) { switch (key) { - case CONNECTOR_ADDRESS: + case EDR_REQUEST_DTO_CONNECTOR_ADDRESS: transformString(value, builder::connectorAddress, context); break; - case PROTOCOL: + case EDR_REQUEST_DTO_PROTOCOL: transformString(value, builder::protocol, context); break; - case CONNECTOR_ID: + case EDR_REQUEST_DTO_CONNECTOR_ID: transformString(value, builder::connectorId, context); break; - case PROVIDER_ID: + case EDR_REQUEST_DTO_PROVIDER_ID: transformString(value, builder::providerId, context); break; - case CALLBACK_ADDRESSES: - var addresses = new ArrayList(); - transformArrayOrObject(value, CallbackAddressDto.class, addresses::add, context); + case EDR_REQUEST_DTO_CALLBACK_ADDRESSES: + var addresses = new ArrayList(); + transformArrayOrObject(value, CallbackAddress.class, addresses::add, context); builder.callbackAddresses(addresses); break; - case OFFER: + case EDR_REQUEST_DTO_OFFER: transformArrayOrObject(value, ContractOfferDescription.class, builder::offer, context); break; default: context.problem() .unexpectedType() - .type(NegotiateEdrRequestDto.TYPE) + .type(EDR_REQUEST_DTO_TYPE) .property(key) .actual(key) - .expected(CONNECTOR_ADDRESS) - .expected(PROTOCOL) - .expected(CONNECTOR_ID) - .expected(PROVIDER_ID) - .expected(CALLBACK_ADDRESSES) - .expected(OFFER) + .expected(EDR_REQUEST_DTO_CONNECTOR_ADDRESS) + .expected(EDR_REQUEST_DTO_PROTOCOL) + .expected(EDR_REQUEST_DTO_CONNECTOR_ID) + .expected(EDR_REQUEST_DTO_PROVIDER_ID) + .expected(EDR_REQUEST_DTO_CALLBACK_ADDRESSES) + .expected(EDR_REQUEST_DTO_OFFER) .report(); break; } diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java index 25878c0ed..675955af9 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.java @@ -16,17 +16,14 @@ import org.eclipse.edc.api.transformer.DtoTransformer; import org.eclipse.edc.connector.contract.spi.types.offer.ContractOffer; -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.spi.cp.adapter.model.NegotiateEdrRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.stream.Collectors; - public class NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer implements DtoTransformer { - + @Override public Class getInputType() { return NegotiateEdrRequestDto.class; @@ -39,8 +36,6 @@ public Class getOutputType() { @Override public @Nullable NegotiateEdrRequest transform(@NotNull NegotiateEdrRequestDto object, @NotNull TransformerContext context) { - var callbacks = object.getCallbackAddresses().stream().map(c -> context.transform(c, CallbackAddress.class)).collect(Collectors.toList()); - var contractOffer = ContractOffer.Builder.newInstance() .id(object.getOffer().getOfferId()) .assetId(object.getOffer().getAssetId()) @@ -53,7 +48,7 @@ public Class getOutputType() { .connectorAddress(object.getConnectorAddress()) .protocol(object.getProtocol()) .offer(contractOffer) - .callbackAddresses(callbacks) + .callbackAddresses(object.getCallbackAddresses()) .build(); } diff --git a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApiExtensionTest.java b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApiExtensionTest.java index fbe6c1744..8be15fa5e 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApiExtensionTest.java +++ b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApiExtensionTest.java @@ -20,6 +20,7 @@ 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.junit.jupiter.api.BeforeEach; @@ -61,5 +62,7 @@ void initialize_ShouldConfigureTheController(ServiceExtensionContext context) { verify(webService).registerResource(eq(alias), isA(AdapterEdrController.class)); verify(transformerRegistry).register(isA(NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.class)); verify(transformerRegistry).register(isA(JsonObjectToNegotiateEdrRequestDtoTransformer.class)); + verify(transformerRegistry).register(isA(JsonObjectFromEndpointDataReferenceEntryTransformer.class)); + } } diff --git a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrControllerTest.java b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrControllerTest.java index 022aa60df..a345406b1 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrControllerTest.java +++ b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrControllerTest.java @@ -25,19 +25,32 @@ import org.eclipse.edc.junit.annotations.ApiTest; import org.eclipse.edc.service.spi.result.ServiceResult; 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.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.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.List; + import static io.restassured.RestAssured.given; +import static java.lang.String.format; import static org.eclipse.edc.api.model.IdResponseDto.EDC_ID_RESPONSE_DTO_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.openRequest; import static org.eclipse.tractusx.edc.api.cp.adapter.TestFunctions.requestDto; +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.hamcrest.Matchers.is; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -52,6 +65,12 @@ public class AdapterEdrControllerTest extends RestControllerTestBase { AdapterTransferProcessService adapterTransferProcessService = mock(AdapterTransferProcessService.class); TypeTransformerRegistry transformerRegistry = mock(TypeTransformerRegistry.class); + @BeforeEach + void setup() { + jsonLdService.registerNamespace("edc", EDC_NAMESPACE); + jsonLdService.registerNamespace("tx", TX_NAMESPACE); + } + @Test void initEdrNegotiation_shouldWork_whenValidRequest() { @@ -90,6 +109,119 @@ void initEdrNegotiation_shouldReturnBadRequest_whenValidInvalidRequest() { } + @Test + void initEdrNegotiation_shouldReturnError_whenNotFound() { + var transferProcessId = "id"; + + when(adapterTransferProcessService.findByTransferProcessId(transferProcessId)).thenReturn(ServiceResult.notFound("")); + + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .get(ADAPTER_EDR_PATH + "/" + transferProcessId) + .then() + .statusCode(404); + } + + @Test + void getEdr_shouldReturnDataAddress_whenFound() { + var transferProcessId = "id"; + var edr = EndpointDataReference.Builder.newInstance().endpoint("test").id(transferProcessId).build(); + var response = Json.createObjectBuilder() + .add(DataAddress.TYPE, EndpointDataReference.EDR_SIMPLE_TYPE) + .add(EndpointDataReference.ENDPOINT, edr.getEndpoint()) + .add(EndpointDataReference.ID, edr.getId()) + .build(); + + when(adapterTransferProcessService.findByTransferProcessId(transferProcessId)).thenReturn(ServiceResult.success(edr)); + when(transformerRegistry.transform(any(DataAddress.class), eq(JsonObject.class))).thenReturn(Result.success(response)); + + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .get(ADAPTER_EDR_PATH + "/" + transferProcessId) + .then() + .statusCode(200) + .body("'edc:endpoint'", is(edr.getEndpoint())) + .body("'edc:id'", is(edr.getId())) + .body("'edc:type'", is(EndpointDataReference.EDR_SIMPLE_TYPE)); + + } + + @Test + void queryEdrs_shouldReturnCachedEntries_whenAssetIdIsProvided() { + var assetId = "id"; + var transferProcessId = "id"; + var agreementId = "id"; + + var entry = EndpointDataReferenceEntry.Builder.newInstance() + .transferProcessId(transferProcessId) + .agreementId(agreementId) + .assetId(assetId) + .build(); + + var response = Json.createObjectBuilder() + .add(TYPE, EDR_ENTRY_TYPE) + .add(EDR_ENTRY_ASSET_ID, entry.getAssetId()) + .add(EDR_ENTRY_TRANSFER_PROCESS_ID, entry.getTransferProcessId()) + .add(EDR_ENTRY_AGREEMENT_ID, entry.getAgreementId()) + .build(); + + when(adapterTransferProcessService.findByAssetAndAgreement(assetId, null)).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)) + .then() + .statusCode(200) + .body("[0].'edc:transferProcessId'", is(entry.getTransferProcessId())) + .body("[0].'edc:agreementId'", is(entry.getAgreementId())) + .body("[0].'edc:assetId'", is(entry.getAssetId())); + + } + + @Test + void queryEdrs_shouldReturnCachedEntries_whenAgreementIdIsProvided() { + var assetId = "id"; + var transferProcessId = "id"; + var agreementId = "id"; + + var entry = EndpointDataReferenceEntry.Builder.newInstance() + .transferProcessId(transferProcessId) + .agreementId(agreementId) + .assetId(assetId) + .build(); + + + var response = Json.createObjectBuilder() + .add(TYPE, EDR_ENTRY_TYPE) + .add(EDR_ENTRY_ASSET_ID, entry.getAssetId()) + .add(EDR_ENTRY_TRANSFER_PROCESS_ID, entry.getTransferProcessId()) + .add(EDR_ENTRY_AGREEMENT_ID, entry.getAgreementId()) + .build(); + + + when(adapterTransferProcessService.findByAssetAndAgreement(null, agreementId)).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())) + .then() + .statusCode(200) + .body("[0].'edc:transferProcessId'", is(entry.getTransferProcessId())) + .body("[0].'edc:agreementId'", is(entry.getAgreementId())) + .body("[0].'edc:assetId'", is(entry.getAssetId())); + } + + @Test + void queryEdrs_shouldFail_whenNoQueryParameter() { + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .get(ADAPTER_EDR_PATH) + .then() + .statusCode(400); + } + @Override protected Object controller() { return new AdapterEdrController(adapterTransferProcessService, jsonLdService, transformerRegistry); 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/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java new file mode 100644 index 000000000..79e0ba5ae --- /dev/null +++ b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java @@ -0,0 +1,54 @@ +/* + * 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.transform; + +import org.eclipse.edc.transform.spi.TransformerContext; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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.mockito.Mockito.mock; + +class JsonObjectFromEndpointDataReferenceEntryTransformerTest { + + private final TransformerContext context = mock(TransformerContext.class); + private JsonObjectFromEndpointDataReferenceEntryTransformer transformer; + + @BeforeEach + void setUp() { + transformer = new JsonObjectFromEndpointDataReferenceEntryTransformer(); + } + + @Test + void transform() { + + var dto = EndpointDataReferenceEntry.Builder.newInstance() + .assetId("id") + .transferProcessId("tpId") + .agreementId("aId") + .build(); + + var jsonObject = transformer.transform(dto, context); + + assertThat(jsonObject).isNotNull(); + 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()); + } +} \ 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/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java index 5c52ad494..8cb694632 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java +++ b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformerTest.java @@ -18,7 +18,6 @@ import jakarta.json.JsonArrayBuilder; import jakarta.json.JsonObject; import jakarta.json.JsonValue; -import org.eclipse.edc.api.model.CallbackAddressDto; 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; @@ -93,7 +92,7 @@ void transform() { .events(Set.of("foo", "bar")) .transactional(true) .build()); - when(context.transform(any(CallbackAddress.class), eq(CallbackAddressDto.class))).thenReturn(CallbackAddressDto.Builder.newInstance() + when(context.transform(any(CallbackAddress.class), eq(CallbackAddress.class))).thenReturn(CallbackAddress.Builder.newInstance() .uri("http://test.local") .events(Set.of("foo", "bar")) .transactional(true) @@ -121,7 +120,7 @@ void transform_reportErrors() { .build(); var dto = transformer.transform(jsonLd.expand(jsonObject).getContent(), context); - + assertThat(dto).isNotNull(); verify(context, times(1)).reportProblem(anyString()); } 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/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java index f51ba1435..70656ad71 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java +++ b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/NegotiateEdrRequestDtoToNegotiateEdrRequestTransformerTest.java @@ -14,7 +14,7 @@ package org.eclipse.tractusx.edc.api.cp.adapter.transform; -import org.eclipse.edc.api.model.CallbackAddressDto; +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.junit.jupiter.api.Test; @@ -39,7 +39,7 @@ void inputOutputType() { @Test void verify_transform() { - var callback = CallbackAddressDto.Builder.newInstance() + var callback = CallbackAddress.Builder.newInstance() .uri("local://test") .build(); var dto = NegotiateEdrRequestDto.Builder.newInstance() 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 index 0220b1330..90a3290f6 100644 --- 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 @@ -19,14 +19,25 @@ 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"; @@ -38,14 +49,17 @@ public class AdapterTransferProcessServiceImpl implements AdapterTransferProcess .build(); private final ContractNegotiationService contractNegotiationService; - public AdapterTransferProcessServiceImpl(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 ServiceResult.success(contractNegotiation); + return success(contractNegotiation); } private ContractRequest createContractRequest(NegotiateEdrRequest request) { @@ -62,4 +76,38 @@ private ContractRequest createContractRequest(NegotiateEdrRequest request) { .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/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtension.java b/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtension.java index 4fe11df5d..ba257790e 100644 --- a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtension.java +++ b/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtension.java @@ -20,7 +20,7 @@ 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.Provides; +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; @@ -32,7 +32,6 @@ import static org.eclipse.tractusx.edc.cp.adapter.callback.InProcessCallbackMessageDispatcher.CALLBACK_EVENT_LOCAL; -@Provides(AdapterTransferProcessService.class) @Extension(LocalCallbackExtension.NAME) public class LocalCallbackExtension implements ServiceExtension { public static final String NAME = "Local callbacks extension"; @@ -65,6 +64,9 @@ public class LocalCallbackExtension implements ServiceExtension { @Inject private TransactionContext transactionContext; + @Inject + private EndpointDataReferenceCache endpointDataReferenceCache; + @Override public String name() { return NAME; @@ -79,7 +81,11 @@ public void initialize(ServiceExtensionContext context) { resolverRegistry.registerResolver(this::resolveProtocol); registry.register(new InProcessCallbackMessageDispatcher(callbackRegistry)); - context.registerService(AdapterTransferProcessService.class, new AdapterTransferProcessServiceImpl(contractNegotiationService)); + } + + @Provider + public AdapterTransferProcessService adapterTransferProcessService() { + return new AdapterTransferProcessServiceImpl(contractNegotiationService, endpointDataReferenceCache); } private String resolveProtocol(String scheme) { diff --git a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/TransferProcessLocalCallback.java b/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/TransferProcessLocalCallback.java index 6d450bfd8..04040b0fa 100644 --- a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/TransferProcessLocalCallback.java +++ b/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/TransferProcessLocalCallback.java @@ -60,8 +60,8 @@ private Result storeEdr(EndpointDataReference edr) { var transferProcessId = transferProcessStore.processIdForDataRequestId(edr.getId()); var transferProcess = transferProcessStore.findById(transferProcessId); if (transferProcess != null) { - var cacheEntry = EndpointDataReferenceEntry.Builder.newInstance(). - transferProcessId(transferProcess.getId()) + var cacheEntry = EndpointDataReferenceEntry.Builder.newInstance() + .transferProcessId(transferProcess.getId()) .assetId(transferProcess.getDataRequest().getAssetId()) .agreementId(transferProcess.getDataRequest().getContractId()) .build(); diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImplTest.java b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImplTest.java index 1e95f0e1c..69977fdfe 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImplTest.java +++ b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImplTest.java @@ -19,7 +19,11 @@ 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.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.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -38,9 +42,11 @@ public class AdapterTransferProcessServiceImplTest { ContractNegotiationService contractNegotiationService = mock(ContractNegotiationService.class); + EndpointDataReferenceCache endpointDataReferenceCache = mock(EndpointDataReferenceCache.class); + @Test - void initEdrNegotiation_shouldFireAContractNegotiation_WhenUsingCallbacks() { - var transferService = new AdapterTransferProcessServiceImpl(contractNegotiationService); + void initEdrNegotiation_shouldFireContractNegotiation_WhenUsingCallbacks() { + var transferService = new AdapterTransferProcessServiceImpl(contractNegotiationService, endpointDataReferenceCache); var captor = ArgumentCaptor.forClass(ContractRequest.class); @@ -65,6 +71,39 @@ void initEdrNegotiation_shouldFireAContractNegotiation_WhenUsingCallbacks() { } + @Test + 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); + + assertThat(result) + .isNotNull() + .extracting(ServiceResult::getContent) + .isNotNull(); + } + + + @Test + void findByTransferProcessId_shouldNotFound_whenNotPresentInCache() { + var transferProcessId = "tpId"; + + var transferService = new AdapterTransferProcessServiceImpl(contractNegotiationService, endpointDataReferenceCache); + when(endpointDataReferenceCache.resolveReference(transferProcessId)).thenReturn(null); + + var result = transferService.findByTransferProcessId(transferProcessId); + + assertThat(result) + .isNotNull() + .extracting(ServiceResult::getFailure) + .extracting(ServiceFailure::getReason) + .isEqualTo(ServiceFailure.Reason.NOT_FOUND); + } + private NegotiateEdrRequest getNegotiateEdrRequest() { return NegotiateEdrRequest.Builder.newInstance() .protocol("protocol") diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/ContractNegotiationCallbackTest.java b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/ContractNegotiationCallbackTest.java index d24f05382..62545a8b5 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/ContractNegotiationCallbackTest.java +++ b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/ContractNegotiationCallbackTest.java @@ -67,7 +67,7 @@ void setup() { } @Test - void invoke_shouldStartATransferProcess() { + void invoke_shouldStartTransferProcess() { var captor = ArgumentCaptor.forClass(TransferRequest.class); @@ -99,8 +99,8 @@ void invoke_shouldStartATransferProcess() { } @Test - void invoke_shouldThrowException_whenATransferRequestFails() { - + void invoke_shouldThrowException_whenTransferRequestFails() { + when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.badRequest("test")); var event = getNegotiationFinalizedEvent(); diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackMessageDispatcherTest.java b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackMessageDispatcherTest.java index 27ef6565e..54e5bef21 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackMessageDispatcherTest.java +++ b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/InProcessCallbackMessageDispatcherTest.java @@ -55,7 +55,7 @@ void send_shouldInvokeRegisteredCallback() { } @Test - void send_shouldNotInvokeRegisteredCallback_whenItsNotACallbackRemoteMessage() { + void send_shouldNotInvokeRegisteredCallback_whenItsNotCallbackRemoteMessage() { assertThatThrownBy(() -> dispatcher.send(Object.class, new TestMessage()).join()) .hasCauseInstanceOf(EdcException.class); diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtensionTest.java b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtensionTest.java index 8e68c0ed1..afff1d6a4 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtensionTest.java +++ b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtensionTest.java @@ -22,7 +22,6 @@ 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.cp.adapter.service.AdapterTransferProcessService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -30,7 +29,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.tractusx.edc.cp.adapter.callback.InProcessCallbackMessageDispatcher.CALLBACK_EVENT_LOCAL; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; @ExtendWith(DependencyInjectionExtension.class) public class LocalCallbackExtensionTest { @@ -64,7 +65,7 @@ void shouldInitializeTheExtension(ServiceExtensionContext context) { assertThat(resolver.resolve("test")).isNull(); - var service = context.getService(AdapterTransferProcessService.class); + var service = extension.adapterTransferProcessService(); assertThat(service).isInstanceOf(AdapterTransferProcessServiceImpl.class); var callbackArgumentCaptor = ArgumentCaptor.forClass(InProcessCallback.class); diff --git a/edc-extensions/cx-oauth2/README.md b/edc-extensions/cx-oauth2/README.md index 479c783c7..f9ca70637 100644 --- a/edc-extensions/cx-oauth2/README.md +++ b/edc-extensions/cx-oauth2/README.md @@ -31,3 +31,17 @@ Instead of the `idsc:IDS_CONNECTORS_ALL` the connector requests a specific audie When a connector receives a message, it will checks the token audience is equal to the configured value in `edc.ids.endpoint.audience`. ![sequence diagram](./diagrams/sequence.png) + +## Catena-X Participant Extension + +Starting from `0.0.1-milestone-9` EDC requires a mandatory setting `edc.participant.id`, which in this case should be the BPN number which is transmitted over the wire to identifying the participants IDs. +To verify that in the DAPS token an extension has been created, that extract from the `ClaimToken` the BPN number and then EDC compare that identity with the one provided over the wire, for security reason. + +By default the extension parse the `referringConnector` url and extract the BPN number as the last parameter in the URL eg (http://sokrates-controlplane/BPNSOKRATES). + +### Configuration + +| Key | Description | Mandatory | Default | +|:--------------------------------------|:----------------------------------------------------------|-----------|---------------| +| tx.participant.id.regex | Regex for extracting the BPN from the referringConnector | | [^/]+(?=/$|$) | +| tx.participant.id.regexGroup | Group number for the regex match | | 0 | diff --git a/edc-extensions/cx-oauth2/build.gradle.kts b/edc-extensions/cx-oauth2/build.gradle.kts index 16d6219ae..772c60f47 100644 --- a/edc-extensions/cx-oauth2/build.gradle.kts +++ b/edc-extensions/cx-oauth2/build.gradle.kts @@ -30,4 +30,6 @@ dependencies { implementation(libs.slf4j.api) implementation(libs.nimbus.jwt) implementation(libs.okhttp) + + testImplementation(libs.edc.junit) } diff --git a/edc-extensions/cx-oauth2/src/main/java/org/eclipse/tractusx/edc/oauth2/CXOAuth2Extension.java b/edc-extensions/cx-oauth2/src/main/java/org/eclipse/tractusx/edc/oauth2/CxOauth2Extension.java similarity index 75% rename from edc-extensions/cx-oauth2/src/main/java/org/eclipse/tractusx/edc/oauth2/CXOAuth2Extension.java rename to edc-extensions/cx-oauth2/src/main/java/org/eclipse/tractusx/edc/oauth2/CxOauth2Extension.java index 4b5bffc74..338784ef3 100644 --- a/edc-extensions/cx-oauth2/src/main/java/org/eclipse/tractusx/edc/oauth2/CXOAuth2Extension.java +++ b/edc-extensions/cx-oauth2/src/main/java/org/eclipse/tractusx/edc/oauth2/CxOauth2Extension.java @@ -19,21 +19,21 @@ */ package org.eclipse.tractusx.edc.oauth2; -import java.util.Map; import org.eclipse.edc.iam.oauth2.spi.CredentialsRequestAdditionalParametersProvider; import org.eclipse.edc.runtime.metamodel.annotation.Provider; import org.eclipse.edc.spi.system.ServiceExtension; -public class CXOAuth2Extension implements ServiceExtension { +import java.util.Map; + +public class CxOauth2Extension implements ServiceExtension { - @Override - public String name() { - return "CX OAuth2"; - } + @Override + public String name() { + return "CX OAuth2"; + } - @Provider - public CredentialsRequestAdditionalParametersProvider - credentialsRequestAdditionalParametersProvider() { - return tokenParameters -> Map.of("resource", tokenParameters.getAudience()); - } + @Provider + public CredentialsRequestAdditionalParametersProvider credentialsRequestAdditionalParametersProvider() { + return tokenParameters -> Map.of("resource", tokenParameters.getAudience()); + } } diff --git a/edc-extensions/cx-oauth2/src/main/java/org/eclipse/tractusx/edc/oauth2/CxParticipantExtension.java b/edc-extensions/cx-oauth2/src/main/java/org/eclipse/tractusx/edc/oauth2/CxParticipantExtension.java new file mode 100644 index 000000000..037851bcd --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/org/eclipse/tractusx/edc/oauth2/CxParticipantExtension.java @@ -0,0 +1,75 @@ +/* + * 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.oauth2; + +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.agent.ParticipantAgentService; +import org.eclipse.edc.spi.agent.ParticipantAgentServiceExtension; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.regex.Pattern; + +import static org.eclipse.edc.spi.agent.ParticipantAgent.PARTICIPANT_IDENTITY; + +public class CxParticipantExtension implements ServiceExtension, ParticipantAgentServiceExtension { + + public static final String REFERRING_CONNECTOR_CLAIM = "referringConnector"; + + private static final String DEFAULT_PARTICIPANT_ID_REGEX = "[^/]+(?=/$|$)"; + private static final int DEFAULT_PARTICIPANT_ID_REGEX_GROUP = 0; + + @Setting(value = "Participant Extractor from referringConnector regex", defaultValue = CxParticipantExtension.DEFAULT_PARTICIPANT_ID_REGEX) + private static final String PARTICIPANT_ID_REGEX = "tx.participant.id.regex"; + + @Setting(value = "Participant Extractor from referringConnector regex group", defaultValue = "0") + private static final String PARTICIPANT_ID_REGEX_GROUP = "tx.participant.id.regexGroup"; + @Inject + private ParticipantAgentService agentService; + + private Pattern participantRegex; + + private int participantRegexGroup; + + @Inject + private Monitor monitor; + + @Override + public void initialize(ServiceExtensionContext context) { + this.participantRegex = Pattern.compile(context.getConfig().getString(PARTICIPANT_ID_REGEX, DEFAULT_PARTICIPANT_ID_REGEX)); + this.participantRegexGroup = context.getConfig().getInteger(PARTICIPANT_ID_REGEX_GROUP, DEFAULT_PARTICIPANT_ID_REGEX_GROUP); + + agentService.register(this); + } + + @Override + public @NotNull Map attributesFor(ClaimToken token) { + var referringConnector = token.getClaim(REFERRING_CONNECTOR_CLAIM); + if (referringConnector instanceof String referringConnectorUrl) { + var matcher = participantRegex.matcher(referringConnectorUrl); + if (matcher.find()) { + var id = matcher.group(participantRegexGroup); + return Map.of(PARTICIPANT_IDENTITY, id); + } + monitor.warning("Unable to extract the participant id from the referring connector claim"); + } + return Map.of(); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/cx-oauth2/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 2e60a45f0..d3d98beb2 100644 --- a/edc-extensions/cx-oauth2/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/cx-oauth2/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -18,4 +18,5 @@ # SPDX-License-Identifier: Apache-2.0 # -org.eclipse.tractusx.edc.oauth2.CXOAuth2Extension +org.eclipse.tractusx.edc.oauth2.CxOauth2Extension +org.eclipse.tractusx.edc.oauth2.CxParticipantExtension diff --git a/edc-extensions/cx-oauth2/src/test/java/org/eclipse/tractusx/edc/oauth2/CxParticipantExtensionTest.java b/edc-extensions/cx-oauth2/src/test/java/org/eclipse/tractusx/edc/oauth2/CxParticipantExtensionTest.java new file mode 100644 index 000000000..aabbe35e1 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/test/java/org/eclipse/tractusx/edc/oauth2/CxParticipantExtensionTest.java @@ -0,0 +1,90 @@ +/* + * 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.oauth2; + +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.agent.ParticipantAgentService; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +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 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.Map; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.spi.agent.ParticipantAgent.PARTICIPANT_IDENTITY; +import static org.eclipse.tractusx.edc.oauth2.CxParticipantExtension.REFERRING_CONNECTOR_CLAIM; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +@ExtendWith(DependencyInjectionExtension.class) +public class CxParticipantExtensionTest { + + CxParticipantExtension extension; + + ParticipantAgentService agentService = mock(ParticipantAgentService.class); + + ServiceExtensionContext context; + + @BeforeEach + void setUp(ObjectFactory factory, ServiceExtensionContext context) { + this.context = spy(context); + context.registerService(ParticipantAgentService.class, agentService); + extension = factory.constructInstance(CxParticipantExtension.class); + } + + @Test + void initialize() { + extension.initialize(context); + verify(agentService).register(isA(CxParticipantExtension.class)); + } + + + @ParameterizedTest + @ArgumentsSource(ClaimProvider.class) + void attributesFor_shouldMatchTheId(Map claims) { + var attributes = Map.of(PARTICIPANT_IDENTITY, "BPNSOKRATES"); + extension.initialize(context); + var claimToken = ClaimToken.Builder.newInstance().claims(claims).build(); + assertThat(extension.attributesFor(claimToken)).containsExactlyEntriesOf(attributes); + } + + static class ClaimProvider implements ArgumentsProvider { + ClaimProvider() { + } + + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + Map.of(REFERRING_CONNECTOR_CLAIM, "http://sokrates-controlplane/BPNSOKRATES"), + Map.of(REFERRING_CONNECTOR_CLAIM, "http://sokrates-controlplane/BPNSOKRATES/"), + Map.of(REFERRING_CONNECTOR_CLAIM, "http://sokrates-controlplane/test/path/BPNSOKRATES"), + Map.of(REFERRING_CONNECTOR_CLAIM, "https://sokrates-controlplane/test/path/BPNSOKRATES"), + Map.of(REFERRING_CONNECTOR_CLAIM, "BPNSOKRATES") + ).map(Arguments::arguments); + } + } +} + 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 index 8024bc276..074956eac 100644 --- 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 @@ -19,9 +19,6 @@ */ package org.eclipse.tractusx.edc.data.encryption; -import java.time.Duration; -import java.util.List; -import java.util.stream.Collectors; 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; @@ -37,107 +34,108 @@ 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 EXTENSION_NAME = "Data Encryption Extension"; - public static final String ENCRYPTION_ALGORITHM = "edc.data.encryption.algorithm"; - public static final String ENCRYPTION_ALGORITHM_DEFAULT = DataEncrypterFactory.AES_ALGORITHM; + public static final String ENCRYPTION_KEY_SET = "edc.data.encryption.keys.alias"; - public static final String CACHING_ENABLED = "edc.data.encryption.caching.enabled"; - public static final boolean CACHING_ENABLED_DEFAULT = false; + 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_SECONDS = "edc.data.encryption.caching.seconds"; - public static final int CACHING_SECONDS_DEFAULT = 3600; + public static final String CACHING_ENABLED = "edc.data.encryption.caching.enabled"; + public static final boolean CACHING_ENABLED_DEFAULT = false; - private static final CryptoKeyFactory cryptoKeyFactory = new CryptoKeyFactoryImpl(); + public static final String CACHING_SECONDS = "edc.data.encryption.caching.seconds"; + public static final int CACHING_SECONDS_DEFAULT = 3600; - private Monitor monitor; - private Vault vault; - private ServiceExtensionContext context; + private static final CryptoKeyFactory CRYPTO_KEY_FACTORY = new CryptoKeyFactoryImpl(); - @Override - public String name() { - return EXTENSION_NAME; - } + private Monitor monitor; + private Vault vault; + private ServiceExtensionContext context; - @Override - public void start() { + 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 String algorithm = context.getSetting(ENCRYPTION_ALGORITHM, ENCRYPTION_ALGORITHM_DEFAULT); + final boolean cachingEnabled = context.getSetting(CACHING_ENABLED, CACHING_ENABLED_DEFAULT); + final int cachingSeconds = context.getSetting(CACHING_SECONDS, CACHING_SECONDS_DEFAULT); - if (DataEncrypterFactory.NONE.equalsIgnoreCase(algorithm)) { - return; // no start-up checks for NONE algorithm + return new AesDataEncrypterConfiguration( + key, cachingEnabled, Duration.ofSeconds(cachingSeconds)); } - 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, cryptoKeyFactory); - 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, cryptoKeyFactory); - - 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); + @Override + public String name() { + return EXTENSION_NAME; } - context.registerService(DataEncrypter.class, dataEncrypter); - } - - 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); + @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); + } + } } - 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 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/algorithms/CryptoAlgorithm.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/CryptoAlgorithm.java index a49cce44d..d457677b9 100644 --- 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 @@ -19,22 +19,21 @@ */ 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; -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; public interface CryptoAlgorithm { - EncryptedData encrypt(DecryptedData data, T key) - throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, - NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException; + 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; + 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/data/CryptoData.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoData.java index edb3b3620..9dc59a8b0 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoData.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoData.java @@ -20,7 +20,7 @@ package org.eclipse.tractusx.edc.data.encryption.data; public interface CryptoData { - byte[] getBytes(); + byte[] getBytes(); - String getBase64(); + String getBase64(); } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactory.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactory.java index a37460111..0144ad3a4 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactory.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactory.java @@ -21,15 +21,15 @@ public interface CryptoDataFactory { - DecryptedData decryptedFromText(String text); + DecryptedData decryptedFromText(String text); - DecryptedData decryptedFromBase64(String base64); + DecryptedData decryptedFromBase64(String base64); - DecryptedData decryptedFromBytes(byte[] bytes); + DecryptedData decryptedFromBytes(byte[] bytes); - EncryptedData encryptedFromText(String text); + EncryptedData encryptedFromText(String text); - EncryptedData encryptedFromBase64(String base64); + EncryptedData encryptedFromBase64(String base64); - EncryptedData encryptedFromBytes(byte[] bytes); + EncryptedData encryptedFromBytes(byte[] bytes); } 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 index d8b4add87..e638df98c 100644 --- 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 @@ -64,12 +64,8 @@ public String encrypt(String value) { try { EncryptedData encryptedData = algorithm.encrypt(decryptedData, key); return encryptedData.getBase64(); - } catch (IllegalBlockSizeException - | BadPaddingException - | InvalidKeyException - | InvalidAlgorithmParameterException - | NoSuchPaddingException - | NoSuchAlgorithmException e) { + } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | + InvalidAlgorithmParameterException | NoSuchPaddingException | NoSuchAlgorithmException e) { throw new EdcException(e); } } @@ -86,11 +82,9 @@ public String decrypt(String value) { .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.")); + .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) { @@ -98,17 +92,9 @@ private Optional decrypt(EncryptedData data, AesKey key) { 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())); + } 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/key/AesKey.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/AesKey.java index 69ef83021..feb3260fa 100644 --- 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 @@ -20,7 +20,7 @@ package org.eclipse.tractusx.edc.data.encryption.key; public interface AesKey extends CryptoKey { - byte[] getBytes(); + byte[] getBytes(); - String getBase64(); + String getBase64(); } 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 index 52fdb3c00..b7d47808a 100644 --- 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 @@ -20,7 +20,7 @@ package org.eclipse.tractusx.edc.data.encryption.key; public interface CryptoKeyFactory { - AesKey fromBase64(String base64); + AesKey fromBase64(String base64); - AesKey fromBytes(byte[] key); + 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 index 7a5b0fc15..dc8eb2083 100644 --- 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 @@ -23,37 +23,37 @@ 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); + public AesKey fromBase64(String base64) { + return fromBytes(Base64.decode(base64)); } - return new AesKeyImpl(key, Base64.toBase64String(key)); - } + 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; - } + private static class AesKeyImpl implements AesKey { + private final byte[] bytes; + private final String base64; - @Override - public byte[] getBytes() { - return bytes; - } + private AesKeyImpl(byte[] bytes, String base64) { + this.bytes = bytes; + this.base64 = base64; + } + + @Override + public byte[] getBytes() { + return bytes; + } - @Override - public String getBase64() { - return base64; + @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 index e740a6f43..586909282 100644 --- 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 @@ -47,11 +47,7 @@ public AesKeyProvider(Vault vault, String vaultKeyAlias, CryptoKeyFactory crypto public AesKey getEncryptionKey() { return getKeysStream() .findFirst() - .orElseThrow( - () -> - new RuntimeException( - DataEncryptionExtension.EXTENSION_NAME - + ": Vault must contain at least one key.")); + .orElseThrow(() -> new RuntimeException(DataEncryptionExtension.EXTENSION_NAME + ": Vault must contain at least one key.")); } @Override 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 index 86bd2d16d..2a3f58d00 100644 --- 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 @@ -19,11 +19,12 @@ */ package org.eclipse.tractusx.edc.data.encryption.provider; -import java.util.stream.Stream; import org.eclipse.tractusx.edc.data.encryption.key.CryptoKey; +import java.util.stream.Stream; + public interface KeyProvider { - T getEncryptionKey(); + T getEncryptionKey(); - Stream getDecryptionKeySet(); + Stream getDecryptionKeySet(); } 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/util/ArrayUtil.java index a600f9385..63c22a39f 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/util/ArrayUtil.java @@ -21,28 +21,29 @@ public class ArrayUtil { - private ArrayUtil() {} - - public static byte[] concat(byte[] a, byte[] b) { - byte[] c = new byte[a.length + b.length]; - System.arraycopy(a, 0, c, 0, a.length); - System.arraycopy(b, 0, c, a.length, b.length); - return c; - } + private ArrayUtil() { + } - 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[] concat(byte[] a, byte[] b) { + byte[] c = new byte[a.length + b.length]; + System.arraycopy(a, 0, c, 0, a.length); + System.arraycopy(b, 0, c, a.length, b.length); + return c; + } - public static byte[] subArray(byte[] a, int startIndex, int length) { - if (startIndex + length > a.length) { - throw new IllegalArgumentException("Start index + length is greater than array length"); + 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(); } - byte[] b = new byte[length]; - System.arraycopy(a, startIndex, b, 0, length); - return b; - } + public static byte[] subArray(byte[] a, int startIndex, int length) { + if (startIndex + length > a.length) { + throw new IllegalArgumentException("Start index + length is greater than array length"); + } + + byte[] b = new byte[length]; + System.arraycopy(a, startIndex, b, 0, length); + return b; + } } diff --git a/edc-extensions/data-encryption/src/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 index c1e0bbb1a..bf6a4d508 100644 --- 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 @@ -32,90 +32,90 @@ 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()); - } + 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/algorithms/aes/ByteCounterTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounterTest.java index 5a16343a5..b9236f189 100644 --- 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 @@ -19,7 +19,6 @@ */ package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; -import java.util.stream.Stream; import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -29,68 +28,70 @@ 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) { + @ParameterizedTest + @ArgumentsSource(IncrementArgumentsProvider.class) + void testIncrements(byte[] counterValue, long numberOfIncrements, byte[] expected) { - ByteCounter initializationVector = new ByteCounter(counterValue); + ByteCounter initializationVector = new ByteCounter(counterValue); - for (int i = 0; i < numberOfIncrements; i++) { - initializationVector.increment(); - } + for (int i = 0; i < numberOfIncrements; i++) { + initializationVector.increment(); + } - var result = initializationVector.getBytes(); - Assertions.assertEquals(ArrayUtil.byteArrayToHex(expected), ArrayUtil.byteArrayToHex(result)); - } + var result = initializationVector.getBytes(); + Assertions.assertEquals(ArrayUtil.byteArrayToHex(expected), ArrayUtil.byteArrayToHex(result)); + } - @Test - void testIsMaxed() { + @Test + void testIsMaxed() { - byte[] counterValue = new byte[] {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; - ByteCounter initializationVector = new ByteCounter(counterValue); + byte[] counterValue = new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; + ByteCounter initializationVector = new ByteCounter(counterValue); - Assertions.assertTrue(initializationVector.isMaxed()); - } + Assertions.assertTrue(initializationVector.isMaxed()); + } - @Test - void testOverflow() { + @Test + void testOverflow() { - byte[] counterValue = new byte[] {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; - ByteCounter initializationVector = new ByteCounter(counterValue); + byte[] counterValue = new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; + ByteCounter initializationVector = new ByteCounter(counterValue); - Assertions.assertThrows(IllegalStateException.class, initializationVector::increment); - } + 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})); + 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/DataEncrypterFactoryTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java index 650fcfec5..7564931c1 100644 --- 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 @@ -19,7 +19,6 @@ */ package org.eclipse.tractusx.edc.data.encryption.encrypter; -import java.time.Duration; import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.security.Vault; @@ -31,52 +30,54 @@ 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"; + private static final String KEY_SET_ALIAS = "keySetAlias"; - DataEncrypterFactory factory; + DataEncrypterFactory factory; - // mocks - private Vault vault; - private Monitor monitor; + // mocks + private Vault vault; + private Monitor monitor; - @BeforeEach - void setup() { - vault = Mockito.mock(Vault.class); - monitor = Mockito.mock(Monitor.class); + @BeforeEach + void setup() { + vault = Mockito.mock(Vault.class); + monitor = Mockito.mock(Monitor.class); - factory = new DataEncrypterFactory(vault, monitor, new CryptoKeyFactoryImpl()); - } + 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)); - } + @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=="); + @Test + void testEncrypterWithCaching() { + Mockito.when(vault.resolveSecret(KEY_SET_ALIAS)).thenReturn("7h6sh6t6tchCmNnHjK2kFA=="); - final AesDataEncrypterConfiguration configuration = newConfiguration(true); - final DataEncrypter dataEncrypter = factory.createAesEncrypter(configuration); + 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); + 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); - } + // 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)); - } + 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 index 73354b76c..60f68e86d 100644 --- 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 @@ -25,20 +25,20 @@ 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 = {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])); - } + @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 index 04625fdb0..07152a95b 100644 --- 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 @@ -20,8 +20,6 @@ package org.eclipse.tractusx.edc.data.encryption.provider; -import java.util.List; -import java.util.stream.Collectors; 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; @@ -30,51 +28,54 @@ 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_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 static final String KEY_ALIAS = "foo"; - private AesKeyProvider keyProvider; + private AesKeyProvider keyProvider; - // mocks - private Vault vault; + // mocks + private Vault vault; - @BeforeEach - void setup() { - vault = Mockito.mock(Vault.class); - keyProvider = new AesKeyProvider(vault, KEY_ALIAS, new CryptoKeyFactoryImpl()); - } + @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)); + @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(); + AesKey key = keyProvider.getEncryptionKey(); - Assertions.assertEquals(KEY_1, key.getBase64()); - } + Assertions.assertEquals(KEY_1, key.getBase64()); + } - @Test - void testEncryptionThrowsOnNoKey() { - Mockito.when(vault.resolveSecret(KEY_ALIAS)).thenReturn(" "); + @Test + void testEncryptionThrowsOnNoKey() { + Mockito.when(vault.resolveSecret(KEY_ALIAS)).thenReturn(" "); - Assertions.assertThrows(RuntimeException.class, () -> keyProvider.getEncryptionKey()); - } + 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)); + @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); + List keys = + keyProvider.getDecryptionKeySet().map(AesKey::getBase64).collect(Collectors.toList()); + List expected = List.of(KEY_1, KEY_2, KEY_3); - Assertions.assertEquals(expected, keys); - } + 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 index 390b210f6..28ee89b33 100644 --- 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 @@ -19,83 +19,84 @@ */ package org.eclipse.tractusx.edc.data.encryption.provider; -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.util.stream.Stream; 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 CachingKeyProvider cachingKeyProvider; - private CryptoKey encryptionKey; - private CryptoKey decryptionKey; + private CryptoKey encryptionKey; + private CryptoKey decryptionKey; - // mocks - private KeyProvider decoratedProvider; - private Duration cacheExpiration; - private Clock clock; + // 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); + @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); + cachingKeyProvider = + new CachingKeyProvider(decoratedProvider, cacheExpiration, clock); - Mockito.when(decoratedProvider.getEncryptionKey()).thenReturn(encryptionKey); - Mockito.when(decoratedProvider.getDecryptionKeySet()) - .thenAnswer((i) -> Stream.of(decryptionKey)); - } + Mockito.when(decoratedProvider.getEncryptionKey()).thenReturn(encryptionKey); + Mockito.when(decoratedProvider.getDecryptionKeySet()) + .thenAnswer((i) -> Stream.of(decryptionKey)); + } - @Test - void testCaching() { + @Test + void testCaching() { - Mockito.when(clock.instant()).thenAnswer((i) -> Instant.now()); + Mockito.when(clock.instant()).thenAnswer((i) -> Instant.now()); - cachingKeyProvider.getDecryptionKeySet(); - cachingKeyProvider.getEncryptionKey(); + cachingKeyProvider.getDecryptionKeySet(); + cachingKeyProvider.getEncryptionKey(); - cachingKeyProvider.getDecryptionKeySet(); - cachingKeyProvider.getEncryptionKey(); + cachingKeyProvider.getDecryptionKeySet(); + cachingKeyProvider.getEncryptionKey(); - 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(); - } + Mockito.verify(decoratedProvider, Mockito.times(1)).getDecryptionKeySet(); + Mockito.verify(decoratedProvider, Mockito.times(1)).getEncryptionKey(); + } - @Test - void testCacheUpdate() { + @Test + void testCacheUpdate() { - Mockito.when(clock.instant()).thenAnswer((i) -> Instant.now()); + 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.when(clock.instant()) - .thenAnswer((i) -> Instant.now().plus(cacheExpiration.plusSeconds(1))); + Mockito.when(clock.instant()) + .thenAnswer((i) -> Instant.now().plus(cacheExpiration.plusSeconds(1))); - cachingKeyProvider.getDecryptionKeySet(); - cachingKeyProvider.getEncryptionKey(); + cachingKeyProvider.getDecryptionKeySet(); + cachingKeyProvider.getEncryptionKey(); - Mockito.verify(decoratedProvider, Mockito.times(2)).getDecryptionKeySet(); - Mockito.verify(decoratedProvider, Mockito.times(2)).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 index 45b604b64..2ddd5694d 100644 --- 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 @@ -19,7 +19,6 @@ */ package org.eclipse.tractusx.edc.data.encryption.util; -import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtensionContext; @@ -28,45 +27,47 @@ 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)); - } + @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); + @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)); - } + Assertions.assertEquals(ArrayUtil.byteArrayToHex(expected), ArrayUtil.byteArrayToHex(subArray)); + } - private static class ArrayArgumentsProvider implements ArgumentsProvider { + 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})); + @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-selector-configuration/src/main/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java b/edc-extensions/dataplane-selector-configuration/src/main/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java index d80fd53db..e57124464 100644 --- a/edc-extensions/dataplane-selector-configuration/src/main/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java +++ b/edc-extensions/dataplane-selector-configuration/src/main/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java @@ -23,11 +23,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.function.Predicate; -import java.util.stream.Collectors; import org.eclipse.edc.connector.dataplane.selector.spi.DataPlaneSelectorService; import org.eclipse.edc.connector.dataplane.selector.spi.instance.DataPlaneInstance; import org.eclipse.edc.runtime.metamodel.annotation.Requires; @@ -37,113 +32,113 @@ import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.spi.system.configuration.Config; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; + @Requires({DataPlaneSelectorService.class}) public class DataPlaneSelectorConfigurationServiceExtension implements ServiceExtension { - public static final String CONFIG_PREFIX = "edc.dataplane.selector"; - public static final String URL_SUFFIX = "url"; - public static final String DESTINATION_TYPES_SUFFIX = "destinationtypes"; - public static final String SOURCE_TYPES_SUFFIX = "sourcetypes"; - public static final String PROPERTIES_SUFFIX = "properties"; - public static final String PUBLIC_API_URL_PROPERTY = "publicApiUrl"; - - private static final String NAME = "Data Plane Selector Configuration Extension"; - private static final String COMMA = ","; - private static final String LOG_MISSING_CONFIGURATION = - NAME + ": Missing configuration for " + CONFIG_PREFIX + ".%s.%s"; - private static final String LOG_SKIP_BC_MISSING_CONFIGURATION = - NAME + ": Configuration issues. Skip registering of Data Plane Instance '%s'"; - private static final String LOG_REGISTERED = - NAME - + ": Registered Data Plane Instance. (id=%s, url=%s, sourceTypes=%s, destinationTypes=%s, properties=)"; - - private Monitor monitor; - private DataPlaneSelectorService dataPlaneSelectorService; - - @Override - public String name() { - return NAME; - } - - @Override - public void initialize(final ServiceExtensionContext serviceExtensionContext) { - this.dataPlaneSelectorService = - serviceExtensionContext.getService(DataPlaneSelectorService.class); - this.monitor = serviceExtensionContext.getMonitor(); - - final Config config = serviceExtensionContext.getConfig(CONFIG_PREFIX); - - config.partition().forEach(this::configureDataPlaneInstance); - } - - private void configureDataPlaneInstance(final Config config) { - final String id = config.currentNode(); - - final String url = config.getString(URL_SUFFIX, ""); - final List sourceTypes = - Arrays.stream(config.getString(SOURCE_TYPES_SUFFIX, "").split(COMMA)) - .map(String::trim) - .filter(Predicate.not(String::isEmpty)) - .distinct() - .collect(Collectors.toList()); - final List destinationTypes = - Arrays.stream(config.getString(DESTINATION_TYPES_SUFFIX, "").split(COMMA)) - .map(String::trim) - .filter(Predicate.not(String::isEmpty)) - .distinct() - .collect(Collectors.toList()); - final String propertiesJson = config.getString(PROPERTIES_SUFFIX, "{}"); - - if (url.isEmpty()) { - monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, URL_SUFFIX)); + public static final String CONFIG_PREFIX = "edc.dataplane.selector"; + public static final String URL_SUFFIX = "url"; + public static final String DESTINATION_TYPES_SUFFIX = "destinationtypes"; + public static final String SOURCE_TYPES_SUFFIX = "sourcetypes"; + public static final String PROPERTIES_SUFFIX = "properties"; + public static final String PUBLIC_API_URL_PROPERTY = "publicApiUrl"; + + private static final String NAME = "Data Plane Selector Configuration Extension"; + private static final String COMMA = ","; + private static final String LOG_MISSING_CONFIGURATION = NAME + ": Missing configuration for " + CONFIG_PREFIX + ".%s.%s"; + private static final String LOG_SKIP_BC_MISSING_CONFIGURATION = NAME + ": Configuration issues. Skip registering of Data Plane Instance '%s'"; + private static final String LOG_REGISTERED = NAME + ": Registered Data Plane Instance. (id=%s, url=%s, sourceTypes=%s, destinationTypes=%s, properties=)"; + + private Monitor monitor; + private DataPlaneSelectorService dataPlaneSelectorService; + + @Override + public String name() { + return NAME; } - if (sourceTypes.isEmpty()) { - monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, SOURCE_TYPES_SUFFIX)); - } + @Override + public void initialize(final ServiceExtensionContext serviceExtensionContext) { + this.dataPlaneSelectorService = + serviceExtensionContext.getService(DataPlaneSelectorService.class); + this.monitor = serviceExtensionContext.getMonitor(); - if (destinationTypes.isEmpty()) { - monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, DESTINATION_TYPES_SUFFIX)); - } - - final Map properties; - try { - ObjectMapper mapper = new ObjectMapper(); - properties = mapper.readValue(propertiesJson, new TypeReference>() {}); - } catch (JsonProcessingException e) { - throw new EdcException(e); - } + final Config config = serviceExtensionContext.getConfig(CONFIG_PREFIX); - final boolean missingPublicApiProperty = !properties.containsKey(PUBLIC_API_URL_PROPERTY); - if (missingPublicApiProperty) { - monitor.warning( - String.format(LOG_MISSING_CONFIGURATION, id, PROPERTIES_SUFFIX) - + "." - + PUBLIC_API_URL_PROPERTY); + config.partition().forEach(this::configureDataPlaneInstance); } - final boolean invalidConfiguration = - url.isEmpty() || sourceTypes.isEmpty() || destinationTypes.isEmpty(); - if (invalidConfiguration || missingPublicApiProperty) { - monitor.warning(String.format(LOG_SKIP_BC_MISSING_CONFIGURATION, id)); - return; + private void configureDataPlaneInstance(final Config config) { + final String id = config.currentNode(); + + final String url = config.getString(URL_SUFFIX, ""); + final List sourceTypes = + Arrays.stream(config.getString(SOURCE_TYPES_SUFFIX, "").split(COMMA)) + .map(String::trim) + .filter(Predicate.not(String::isEmpty)) + .distinct() + .collect(Collectors.toList()); + final List destinationTypes = + Arrays.stream(config.getString(DESTINATION_TYPES_SUFFIX, "").split(COMMA)) + .map(String::trim) + .filter(Predicate.not(String::isEmpty)) + .distinct() + .collect(Collectors.toList()); + final String propertiesJson = config.getString(PROPERTIES_SUFFIX, "{}"); + + if (url.isEmpty()) { + monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, URL_SUFFIX)); + } + + if (sourceTypes.isEmpty()) { + monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, SOURCE_TYPES_SUFFIX)); + } + + if (destinationTypes.isEmpty()) { + monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, DESTINATION_TYPES_SUFFIX)); + } + + final Map properties; + try { + ObjectMapper mapper = new ObjectMapper(); + properties = mapper.readValue(propertiesJson, new TypeReference>() { + }); + } catch (JsonProcessingException e) { + throw new EdcException(e); + } + + final boolean missingPublicApiProperty = !properties.containsKey(PUBLIC_API_URL_PROPERTY); + if (missingPublicApiProperty) { + monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, PROPERTIES_SUFFIX) + "." + PUBLIC_API_URL_PROPERTY); + } + + final boolean invalidConfiguration = + url.isEmpty() || sourceTypes.isEmpty() || destinationTypes.isEmpty(); + if (invalidConfiguration || missingPublicApiProperty) { + monitor.warning(String.format(LOG_SKIP_BC_MISSING_CONFIGURATION, id)); + return; + } + + final DataPlaneInstance.Builder builder = + DataPlaneInstance.Builder.newInstance().id(id).url(url); + + sourceTypes.forEach(builder::allowedSourceType); + destinationTypes.forEach(builder::allowedDestType); + properties.forEach(builder::property); + + dataPlaneSelectorService.addInstance(builder.build()); + + monitor.debug( + String.format( + LOG_REGISTERED, + id, + url, + String.join(", ", sourceTypes), + String.join(", ", destinationTypes))); } - - final DataPlaneInstance.Builder builder = - DataPlaneInstance.Builder.newInstance().id(id).url(url); - - sourceTypes.forEach(builder::allowedSourceType); - destinationTypes.forEach(builder::allowedDestType); - properties.forEach(builder::property); - - dataPlaneSelectorService.addInstance(builder.build()); - - monitor.debug( - String.format( - LOG_REGISTERED, - id, - url, - String.join(", ", sourceTypes), - String.join(", ", destinationTypes))); - } } diff --git a/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java b/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java index 278e2493e..6e1c2098c 100644 --- a/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java +++ b/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java @@ -39,119 +39,123 @@ @ExtendWith(EdcExtension.class) class DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest { - private static final String S3_BUCKET = "s3-bucket"; - private static final String BLOB_STORAGE = "blob-storage"; - private static final String LOCAL_FILE_SYSTEM = "local-file-system"; - - private static final String DATA_PLANE_INSTANCE_ID = "test-plane"; - private static final String DATA_PLANE_INSTANCE_URL = "http://127.0.0.1:8080/test"; - private static final String DATA_PLANE_INSTANCE_SOURCE_TYPES = - String.format("%s, %s", S3_BUCKET, BLOB_STORAGE); - private static final String DATA_PLANE_INSTANCE_DESTINATION_TYPES = LOCAL_FILE_SYSTEM; - - // mocks - private DataPlaneSelectorService dataPlaneSelectorService; - - @BeforeEach - final void beforeEach(EdcExtension extension) { - dataPlaneSelectorService = Mockito.mock(DataPlaneSelectorService.class); - - extension.registerSystemExtension(ServiceExtension.class, new TestExtension()); - extension.setConfiguration(getConfig()); - } - - private Map getConfig() { - final String urlKey = - String.format( - "%s.%s.%s", - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.URL_SUFFIX); - final String sourceTypesKey = - String.format( - "%s.%s.%s", - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.SOURCE_TYPES_SUFFIX); - final String destinationTypesKey = - String.format( - "%s.%s.%s", - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.DESTINATION_TYPES_SUFFIX); - final String propertiesKey = - String.format( - "%s.%s.%s", - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.PROPERTIES_SUFFIX); - - return new HashMap<>() { - { - put(urlKey, DATA_PLANE_INSTANCE_URL); - put(sourceTypesKey, DATA_PLANE_INSTANCE_SOURCE_TYPES); - put(destinationTypesKey, DATA_PLANE_INSTANCE_DESTINATION_TYPES); - put( - propertiesKey, - String.format( - "{ \"%s\": \"%s\" }", - DataPlaneSelectorConfigurationServiceExtension.PUBLIC_API_URL_PROPERTY, - DATA_PLANE_INSTANCE_URL)); - } - }; - } - - @Test - void registersDataPlaneInstance() { - Mockito.verify(dataPlaneSelectorService, Mockito.times(1)) - .addInstance( - Mockito.argThat( - dataPlaneInstance -> { - final DataAddress s3Source = - DataAddress.Builder.newInstance().type(S3_BUCKET).build(); - final DataAddress blobSource = - DataAddress.Builder.newInstance().type(BLOB_STORAGE).build(); - final DataAddress fsSink = - DataAddress.Builder.newInstance().type(LOCAL_FILE_SYSTEM).build(); - - final boolean matchingId = - dataPlaneInstance.getId().equals(DATA_PLANE_INSTANCE_ID); - final boolean matchingUrl = - dataPlaneInstance.getUrl().toString().equals(DATA_PLANE_INSTANCE_URL); - final boolean matchingCanHandleS3ToFileSystem = - dataPlaneInstance.canHandle(s3Source, fsSink); - final boolean matchingCanHandleBlobToFileSystem = - dataPlaneInstance.canHandle(blobSource, fsSink); - - if (!matchingId) - System.err.printf( - "Expected ID %s, but got %s%n", - DATA_PLANE_INSTANCE_ID, dataPlaneInstance.getId()); - if (!matchingUrl) - System.err.printf( - "Expected URL %s, but got %s%n", - DATA_PLANE_INSTANCE_URL, dataPlaneInstance.getUrl()); - if (!matchingCanHandleS3ToFileSystem) - System.err.printf( - "Expected Instance to be handle source %s and sink %s%n", - S3_BUCKET, LOCAL_FILE_SYSTEM); - if (!matchingCanHandleBlobToFileSystem) - System.err.printf( - "Expected Instance to be handle source %s and sink %s%n", - BLOB_STORAGE, LOCAL_FILE_SYSTEM); - - return matchingId - && matchingUrl - && matchingCanHandleS3ToFileSystem - && matchingCanHandleBlobToFileSystem; - })); - } - - @Provides({DataPlaneSelectorService.class}) - private class TestExtension implements ServiceExtension { - - public void initialize(ServiceExtensionContext context) { - context.registerService(DataPlaneSelectorService.class, dataPlaneSelectorService); + private static final String S3_BUCKET = "s3-bucket"; + private static final String BLOB_STORAGE = "blob-storage"; + private static final String LOCAL_FILE_SYSTEM = "local-file-system"; + + private static final String DATA_PLANE_INSTANCE_ID = "test-plane"; + private static final String DATA_PLANE_INSTANCE_URL = "http://127.0.0.1:8080/test"; + private static final String DATA_PLANE_INSTANCE_SOURCE_TYPES = + String.format("%s, %s", S3_BUCKET, BLOB_STORAGE); + private static final String DATA_PLANE_INSTANCE_DESTINATION_TYPES = LOCAL_FILE_SYSTEM; + + // mocks + private DataPlaneSelectorService dataPlaneSelectorService; + + @BeforeEach + final void beforeEach(EdcExtension extension) { + dataPlaneSelectorService = Mockito.mock(DataPlaneSelectorService.class); + + extension.registerSystemExtension(ServiceExtension.class, new TestExtension()); + extension.setConfiguration(getConfig()); + } + + private Map getConfig() { + final String urlKey = + String.format( + "%s.%s.%s", + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.URL_SUFFIX); + final String sourceTypesKey = + String.format( + "%s.%s.%s", + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.SOURCE_TYPES_SUFFIX); + final String destinationTypesKey = + String.format( + "%s.%s.%s", + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.DESTINATION_TYPES_SUFFIX); + final String propertiesKey = + String.format( + "%s.%s.%s", + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.PROPERTIES_SUFFIX); + + return new HashMap<>() { + { + put(urlKey, DATA_PLANE_INSTANCE_URL); + put(sourceTypesKey, DATA_PLANE_INSTANCE_SOURCE_TYPES); + put(destinationTypesKey, DATA_PLANE_INSTANCE_DESTINATION_TYPES); + put( + propertiesKey, + String.format( + "{ \"%s\": \"%s\" }", + DataPlaneSelectorConfigurationServiceExtension.PUBLIC_API_URL_PROPERTY, + DATA_PLANE_INSTANCE_URL)); + } + }; + } + + @Test + void registersDataPlaneInstance() { + Mockito.verify(dataPlaneSelectorService, Mockito.times(1)) + .addInstance( + Mockito.argThat( + dataPlaneInstance -> { + final DataAddress s3Source = + DataAddress.Builder.newInstance().type(S3_BUCKET).build(); + final DataAddress blobSource = + DataAddress.Builder.newInstance().type(BLOB_STORAGE).build(); + final DataAddress fsSink = + DataAddress.Builder.newInstance().type(LOCAL_FILE_SYSTEM).build(); + + final boolean matchingId = + dataPlaneInstance.getId().equals(DATA_PLANE_INSTANCE_ID); + final boolean matchingUrl = + dataPlaneInstance.getUrl().toString().equals(DATA_PLANE_INSTANCE_URL); + final boolean matchingCanHandleS3ToFileSystem = + dataPlaneInstance.canHandle(s3Source, fsSink); + final boolean matchingCanHandleBlobToFileSystem = + dataPlaneInstance.canHandle(blobSource, fsSink); + + if (!matchingId) { + System.err.printf( + "Expected ID %s, but got %s%n", + DATA_PLANE_INSTANCE_ID, dataPlaneInstance.getId()); + } + if (!matchingUrl) { + System.err.printf( + "Expected URL %s, but got %s%n", + DATA_PLANE_INSTANCE_URL, dataPlaneInstance.getUrl()); + } + if (!matchingCanHandleS3ToFileSystem) { + System.err.printf( + "Expected Instance to be handle source %s and sink %s%n", + S3_BUCKET, LOCAL_FILE_SYSTEM); + } + if (!matchingCanHandleBlobToFileSystem) { + System.err.printf( + "Expected Instance to be handle source %s and sink %s%n", + BLOB_STORAGE, LOCAL_FILE_SYSTEM); + } + + return matchingId && + matchingUrl && + matchingCanHandleS3ToFileSystem && + matchingCanHandleBlobToFileSystem; + })); + } + + @Provides({DataPlaneSelectorService.class}) + private class TestExtension implements ServiceExtension { + + public void initialize(ServiceExtensionContext context) { + context.registerService(DataPlaneSelectorService.class, dataPlaneSelectorService); + } } - } } diff --git a/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java b/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java index 5a9298e83..d16100b63 100644 --- a/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java +++ b/edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java @@ -20,9 +20,6 @@ package org.eclipse.tractusx.edc.dataplane.selector.configuration; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Stream; import org.eclipse.edc.connector.dataplane.selector.spi.DataPlaneSelectorService; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.monitor.Monitor; @@ -40,185 +37,193 @@ import org.junit.jupiter.params.provider.ArgumentsSource; import org.mockito.Mockito; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + class DataPlaneSelectorConfigurationServiceExtensionTest { - private static final String S3_BUCKET = "s3-bucket"; - private static final String BLOB_STORAGE = "blob-storage"; - private static final String LOCAL_FILE_SYSTEM = "local-file-system"; - - private static final String DATA_PLANE_INSTANCE_ID = "test-plane"; - private static final String DATA_PLANE_INSTANCE_URL = "http://127.0.0.1:8080/test"; - private static final String DATA_PLANE_INSTANCE_SOURCE_TYPES = - String.format("%s, %s", S3_BUCKET, BLOB_STORAGE); - private static final String DATA_PLANE_INSTANCE_DESTINATION_TYPES = LOCAL_FILE_SYSTEM; - - private static final String urlKey = - String.format( - "%s.%s", - DATA_PLANE_INSTANCE_ID, DataPlaneSelectorConfigurationServiceExtension.URL_SUFFIX); - private static final String sourceTypesKey = - String.format( - "%s.%s", - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.SOURCE_TYPES_SUFFIX); - private static final String destinationTypesKey = - String.format( - "%s.%s", - DATA_PLANE_INSTANCE_ID, - DataPlaneSelectorConfigurationServiceExtension.DESTINATION_TYPES_SUFFIX); - private static final String propertiesKey = - String.format( - "%s.%s", - DATA_PLANE_INSTANCE_ID, DataPlaneSelectorConfigurationServiceExtension.PROPERTIES_SUFFIX); - - private DataPlaneSelectorConfigurationServiceExtension extension; - - // mocks - private ServiceExtensionContext serviceExtensionContext; - private DataPlaneSelectorService dataPlaneSelectorService; - private Monitor monitor; - - @BeforeEach - void setup() { - extension = new DataPlaneSelectorConfigurationServiceExtension(); - - serviceExtensionContext = Mockito.mock(ServiceExtensionContext.class); - dataPlaneSelectorService = Mockito.mock(DataPlaneSelectorService.class); - monitor = Mockito.mock(Monitor.class); - - Mockito.when(serviceExtensionContext.getService(DataPlaneSelectorService.class)) - .thenReturn(dataPlaneSelectorService); - Mockito.when(serviceExtensionContext.getMonitor()).thenReturn(monitor); - } - - private Map getConfig() { - return new HashMap<>() { - { - put(urlKey, DATA_PLANE_INSTANCE_URL); - put(sourceTypesKey, DATA_PLANE_INSTANCE_SOURCE_TYPES); - put(destinationTypesKey, DATA_PLANE_INSTANCE_DESTINATION_TYPES); - put( - propertiesKey, + private static final String S3_BUCKET = "s3-bucket"; + private static final String BLOB_STORAGE = "blob-storage"; + private static final String LOCAL_FILE_SYSTEM = "local-file-system"; + + private static final String DATA_PLANE_INSTANCE_ID = "test-plane"; + private static final String DATA_PLANE_INSTANCE_URL = "http://127.0.0.1:8080/test"; + private static final String DATA_PLANE_INSTANCE_SOURCE_TYPES = + String.format("%s, %s", S3_BUCKET, BLOB_STORAGE); + private static final String DATA_PLANE_INSTANCE_DESTINATION_TYPES = LOCAL_FILE_SYSTEM; + + private static final String URL_KEY = + String.format( + "%s.%s", + DATA_PLANE_INSTANCE_ID, DataPlaneSelectorConfigurationServiceExtension.URL_SUFFIX); + private static final String SOURCE_TYPES_KEY = String.format( - "{ \"%s\": \"%s\" }", - DataPlaneSelectorConfigurationServiceExtension.PUBLIC_API_URL_PROPERTY, - DATA_PLANE_INSTANCE_URL)); - } - }; - } - - @Test - void testName() { - final DataPlaneSelectorConfigurationServiceExtension extension = - new DataPlaneSelectorConfigurationServiceExtension(); - - Assertions.assertNotNull(extension.name()); - Assertions.assertEquals("Data Plane Selector Configuration Extension", extension.name()); - } - - @Test - void testInitialize() { - - final Config config = ConfigFactory.fromMap(getConfig()); - - Mockito.when(serviceExtensionContext.getConfig("edc.dataplane.selector")).thenReturn(config); - - extension.initialize(serviceExtensionContext); - - Mockito.verify(serviceExtensionContext, Mockito.times(1)) - .getService(DataPlaneSelectorService.class); - Mockito.verify(serviceExtensionContext, Mockito.times(1)).getMonitor(); - Mockito.when( - serviceExtensionContext.getConfig( - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) - .thenReturn(config); - - Mockito.verify(dataPlaneSelectorService, Mockito.times(1)) - .addInstance( - Mockito.argThat( - dataPlaneInstance -> { - final DataAddress s3Source = - DataAddress.Builder.newInstance().type(S3_BUCKET).build(); - final DataAddress blobSource = - DataAddress.Builder.newInstance().type(BLOB_STORAGE).build(); - final DataAddress fsSink = - DataAddress.Builder.newInstance().type(LOCAL_FILE_SYSTEM).build(); - - final boolean matchingId = - dataPlaneInstance.getId().equals(DATA_PLANE_INSTANCE_ID); - final boolean matchingUrl = - dataPlaneInstance.getUrl().toString().equals(DATA_PLANE_INSTANCE_URL); - final boolean matchingCanHandleS3ToFileSystem = - dataPlaneInstance.canHandle(s3Source, fsSink); - final boolean matchingCanHandleBlobToFileSystem = - dataPlaneInstance.canHandle(blobSource, fsSink); - - if (!matchingId) - System.err.printf( - "Expected ID %s, but got %s%n", - DATA_PLANE_INSTANCE_ID, dataPlaneInstance.getId()); - if (!matchingUrl) - System.err.printf( - "Expected URL %s, but got %s%n", - DATA_PLANE_INSTANCE_URL, dataPlaneInstance.getUrl()); - if (!matchingCanHandleS3ToFileSystem) - System.err.printf( - "Expected Instance to be handle source %s and sink %s%n", - S3_BUCKET, LOCAL_FILE_SYSTEM); - if (!matchingCanHandleBlobToFileSystem) - System.err.printf( - "Expected Instance to be handle source %s and sink %s%n", - BLOB_STORAGE, LOCAL_FILE_SYSTEM); - - return matchingId - && matchingUrl - && matchingCanHandleS3ToFileSystem - && matchingCanHandleBlobToFileSystem; - })); - } - - @ParameterizedTest - @ArgumentsSource(MissingConfigArgumentsProvider.class) - void testWarningOnPropertyMissing(String configKey, String configValue) { - Map configMap = getConfig(); - configMap.put(configKey, configValue); - - final Config config = ConfigFactory.fromMap(configMap); - - Mockito.when( - serviceExtensionContext.getConfig( - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) - .thenReturn(config); - - extension.initialize(serviceExtensionContext); - - // one warning config missing, one warning data plane instance skipped - Mockito.verify(monitor, Mockito.times(2)).warning(Mockito.anyString()); - } - - @Test - void throwsExceptionOnPropertiesNoJson() { - Map configMap = getConfig(); - configMap.put(propertiesKey, "no json"); - - final Config config = ConfigFactory.fromMap(configMap); - - Mockito.when( - serviceExtensionContext.getConfig( - DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) - .thenReturn(config); - - Assertions.assertThrows( - EdcException.class, () -> extension.initialize(serviceExtensionContext)); - } - - private static class MissingConfigArgumentsProvider implements ArgumentsProvider { - @Override - public Stream provideArguments(ExtensionContext context) { - return Stream.of( - Arguments.of(urlKey, ""), - Arguments.of(sourceTypesKey, ""), - Arguments.of(destinationTypesKey, ""), - Arguments.of(propertiesKey, "{}")); + "%s.%s", + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.SOURCE_TYPES_SUFFIX); + private static final String DESTINATION_TYPES_KEY = + String.format( + "%s.%s", + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.DESTINATION_TYPES_SUFFIX); + private static final String PROPERTIES_KEY = + String.format( + "%s.%s", + DATA_PLANE_INSTANCE_ID, DataPlaneSelectorConfigurationServiceExtension.PROPERTIES_SUFFIX); + + private DataPlaneSelectorConfigurationServiceExtension extension; + + // mocks + private ServiceExtensionContext serviceExtensionContext; + private DataPlaneSelectorService dataPlaneSelectorService; + private Monitor monitor; + + @BeforeEach + void setup() { + extension = new DataPlaneSelectorConfigurationServiceExtension(); + + serviceExtensionContext = Mockito.mock(ServiceExtensionContext.class); + dataPlaneSelectorService = Mockito.mock(DataPlaneSelectorService.class); + monitor = Mockito.mock(Monitor.class); + + Mockito.when(serviceExtensionContext.getService(DataPlaneSelectorService.class)) + .thenReturn(dataPlaneSelectorService); + Mockito.when(serviceExtensionContext.getMonitor()).thenReturn(monitor); + } + + private Map getConfig() { + return new HashMap<>() { + { + put(URL_KEY, DATA_PLANE_INSTANCE_URL); + put(SOURCE_TYPES_KEY, DATA_PLANE_INSTANCE_SOURCE_TYPES); + put(DESTINATION_TYPES_KEY, DATA_PLANE_INSTANCE_DESTINATION_TYPES); + put( + PROPERTIES_KEY, + String.format( + "{ \"%s\": \"%s\" }", + DataPlaneSelectorConfigurationServiceExtension.PUBLIC_API_URL_PROPERTY, + DATA_PLANE_INSTANCE_URL)); + } + }; + } + + @Test + void testName() { + final DataPlaneSelectorConfigurationServiceExtension extension = + new DataPlaneSelectorConfigurationServiceExtension(); + + Assertions.assertNotNull(extension.name()); + Assertions.assertEquals("Data Plane Selector Configuration Extension", extension.name()); + } + + @Test + void testInitialize() { + + final Config config = ConfigFactory.fromMap(getConfig()); + + Mockito.when(serviceExtensionContext.getConfig("edc.dataplane.selector")).thenReturn(config); + + extension.initialize(serviceExtensionContext); + + Mockito.verify(serviceExtensionContext, Mockito.times(1)) + .getService(DataPlaneSelectorService.class); + Mockito.verify(serviceExtensionContext, Mockito.times(1)).getMonitor(); + Mockito.when( + serviceExtensionContext.getConfig( + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) + .thenReturn(config); + + Mockito.verify(dataPlaneSelectorService, Mockito.times(1)) + .addInstance( + Mockito.argThat( + dataPlaneInstance -> { + final DataAddress s3Source = + DataAddress.Builder.newInstance().type(S3_BUCKET).build(); + final DataAddress blobSource = + DataAddress.Builder.newInstance().type(BLOB_STORAGE).build(); + final DataAddress fsSink = + DataAddress.Builder.newInstance().type(LOCAL_FILE_SYSTEM).build(); + + final boolean matchingId = + dataPlaneInstance.getId().equals(DATA_PLANE_INSTANCE_ID); + final boolean matchingUrl = + dataPlaneInstance.getUrl().toString().equals(DATA_PLANE_INSTANCE_URL); + final boolean matchingCanHandleS3ToFileSystem = + dataPlaneInstance.canHandle(s3Source, fsSink); + final boolean matchingCanHandleBlobToFileSystem = + dataPlaneInstance.canHandle(blobSource, fsSink); + + if (!matchingId) { + System.err.printf( + "Expected ID %s, but got %s%n", + DATA_PLANE_INSTANCE_ID, dataPlaneInstance.getId()); + } + if (!matchingUrl) { + System.err.printf( + "Expected URL %s, but got %s%n", + DATA_PLANE_INSTANCE_URL, dataPlaneInstance.getUrl()); + } + if (!matchingCanHandleS3ToFileSystem) { + System.err.printf( + "Expected Instance to be handle source %s and sink %s%n", + S3_BUCKET, LOCAL_FILE_SYSTEM); + } + if (!matchingCanHandleBlobToFileSystem) { + System.err.printf( + "Expected Instance to be handle source %s and sink %s%n", + BLOB_STORAGE, LOCAL_FILE_SYSTEM); + } + + return matchingId && + matchingUrl && + matchingCanHandleS3ToFileSystem && + matchingCanHandleBlobToFileSystem; + })); + } + + @ParameterizedTest + @ArgumentsSource(MissingConfigArgumentsProvider.class) + void testWarningOnPropertyMissing(String configKey, String configValue) { + Map configMap = getConfig(); + configMap.put(configKey, configValue); + + final Config config = ConfigFactory.fromMap(configMap); + + Mockito.when( + serviceExtensionContext.getConfig( + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) + .thenReturn(config); + + extension.initialize(serviceExtensionContext); + + // one warning config missing, one warning data plane instance skipped + Mockito.verify(monitor, Mockito.times(2)).warning(Mockito.anyString()); + } + + @Test + void throwsExceptionOnPropertiesNoJson() { + Map configMap = getConfig(); + configMap.put(PROPERTIES_KEY, "no json"); + + final Config config = ConfigFactory.fromMap(configMap); + + Mockito.when( + serviceExtensionContext.getConfig( + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) + .thenReturn(config); + + Assertions.assertThrows( + EdcException.class, () -> extension.initialize(serviceExtensionContext)); + } + + private static class MissingConfigArgumentsProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + Arguments.of(URL_KEY, ""), + Arguments.of(SOURCE_TYPES_KEY, ""), + Arguments.of(DESTINATION_TYPES_KEY, ""), + Arguments.of(PROPERTIES_KEY, "{}")); + } } - } } diff --git a/edc-extensions/edr-cache-sql/README.md b/edc-extensions/edr-cache-sql/README.md new file mode 100644 index 000000000..712aeb944 --- /dev/null +++ b/edc-extensions/edr-cache-sql/README.md @@ -0,0 +1,27 @@ +# SQL-based `EndpointDataReferenceCache` extension + +This extensions provide a persistent implementation of `EndpointDataReferenceCache`. + +It will store in the database this fields: + +- tranferProcessId +- agreementId +- assetId +- edrId + +It represent a single EDR negotiation done with the new Control Plane Adapter APIs. + +The EDR itself it is stored in the participant vault with a prefixed key `edr__`. + +**_Note that the SQL statements (DDL) are specific to and only tested with PostgreSQL. Using it with other RDBMS may +work but might have unexpected side effects!_** + +## 1. Table schema + +see [schema.sql](docs/schema.sql). + +## 2. Configuration + +| Key | Description | Mandatory | Default | +|:---------------------------------------|:----------------------------------|-----------|---------| +| edc.datasource.edr.name | Datasource used by this extension | | edr | diff --git a/edc-extensions/edr-cache-sql/build.gradle.kts b/edc-extensions/edr-cache-sql/build.gradle.kts new file mode 100644 index 000000000..c1da99b15 --- /dev/null +++ b/edc-extensions/edr-cache-sql/build.gradle.kts @@ -0,0 +1,35 @@ +/* + * 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` +} + +dependencies { + implementation(project(":spi:edr-cache-spi")) + + implementation(libs.edc.spi.core) + implementation(libs.edc.core.sql) + implementation(libs.edc.spi.transactionspi) + implementation(libs.edc.spi.transaction.datasource) + + testImplementation(libs.edc.transaction.local) + + testImplementation(testFixtures(project(":spi:edr-cache-spi"))) + testImplementation(testFixtures(libs.edc.core.sql)) + + testImplementation(testFixtures(libs.edc.junit)) + +} diff --git a/edc-extensions/edr-cache-sql/docs/schema.sql b/edc-extensions/edr-cache-sql/docs/schema.sql new file mode 100644 index 000000000..fe4ef2104 --- /dev/null +++ b/edc-extensions/edr-cache-sql/docs/schema.sql @@ -0,0 +1,22 @@ +-- +-- 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_edr_cache +( + transfer_process_id VARCHAR NOT NULL PRIMARY KEY, + agreement_id VARCHAR NOT NULL, + asset_id VARCHAR NOT NULL, + edr_id VARCHAR NOT NULL, + created_at BIGINT NOT NULL, + updated_at BIGINT NOT NULL +); 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 new file mode 100644 index 000000000..09ead7208 --- /dev/null +++ b/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCache.java @@ -0,0 +1,188 @@ +/* + * 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/SqlEndpointDataReferenceCacheExtension.java b/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtension.java new file mode 100644 index 000000000..1add47e9a --- /dev/null +++ b/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtension.java @@ -0,0 +1,68 @@ +/* + * 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 org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.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.store.sql.schema.EdrStatements; +import org.eclipse.tractusx.edc.edr.store.sql.schema.postgres.PostgresEdrStatements; + +import java.time.Clock; + +@Extension(value = SqlEndpointDataReferenceCacheExtension.NAME) +public class SqlEndpointDataReferenceCacheExtension implements ServiceExtension { + + public static final String NAME = "SQL EDR cache store"; + + @Setting(required = true, defaultValue = SqlEndpointDataReferenceCacheExtension.DEFAULT_DATASOURCE_NAME) + public static final String DATASOURCE_SETTING_NAME = "edc.datasource.edr.name"; + public static final String DEFAULT_DATASOURCE_NAME = "edr"; + @Inject + private DataSourceRegistry dataSourceRegistry; + @Inject + private TransactionContext transactionContext; + @Inject(required = false) + private EdrStatements statements; + @Inject + private TypeManager typeManager; + @Inject + private Clock clock; + @Inject + private Vault vault; + + @Override + public String name() { + return 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); + } + + private EdrStatements getStatementImpl() { + return statements == null ? new PostgresEdrStatements() : statements; + } +} 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 new file mode 100644 index 000000000..1e4111928 --- /dev/null +++ b/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/BaseSqlEdrStatements.java @@ -0,0 +1,55 @@ +/* + * 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/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrMapping.java b/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrMapping.java new file mode 100644 index 000000000..fbc615bff --- /dev/null +++ b/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrMapping.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.edr.store.sql.schema; + +import org.eclipse.edc.sql.translation.TranslationMapping; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; + +/** + * Maps fields of a {@link EndpointDataReferenceEntry} onto the + * corresponding SQL schema (= column names) + */ +public class EdrMapping extends TranslationMapping { + public EdrMapping(EdrStatements statements) { + add("assetId", statements.getAssetIdColumn()); + add("agreementId", statements.getAgreementIdColumn()); + } +} 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-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrStatements.java new file mode 100644 index 000000000..d755dc2e4 --- /dev/null +++ b/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/EdrStatements.java @@ -0,0 +1,63 @@ +/* + * 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.schema; + +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.sql.translation.SqlQueryStatement; + +/** + * Sql Statements for DataPlane Store + */ +public interface EdrStatements { + + default String getEdrTable() { + return "edc_edr_cache"; + } + + default String getTransferProcessIdColumn() { + return "transfer_process_id"; + } + + default String getAgreementIdColumn() { + return "agreement_id"; + } + + default String getAssetIdColumn() { + return "asset_id"; + } + + default String getEdrId() { + return "edr_id"; + } + + default String getCreatedAtColumn() { + return "created_at"; + } + + default String getUpdatedAtColumn() { + return "updated_at"; + } + + + String getFindByTransferProcessIdTemplate(); + + SqlQueryStatement createQuery(QuerySpec querySpec); + + String getInsertTemplate(); + + 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-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/postgres/PostgresEdrStatements.java new file mode 100644 index 000000000..3feb69028 --- /dev/null +++ b/edc-extensions/edr-cache-sql/src/main/java/org/eclipse/tractusx/edc/edr/store/sql/schema/postgres/PostgresEdrStatements.java @@ -0,0 +1,22 @@ +/* + * 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.schema.postgres; + +import org.eclipse.tractusx.edc.edr.store.sql.schema.BaseSqlEdrStatements; + +public class PostgresEdrStatements extends BaseSqlEdrStatements { + + +} diff --git a/edc-extensions/edr-cache-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/edr-cache-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..8405d9959 --- /dev/null +++ b/edc-extensions/edr-cache-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,15 @@ +# +# 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.edr.store.sql.SqlEndpointDataReferenceCacheExtension \ No newline at end of file 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 new file mode 100644 index 000000000..119484baf --- /dev/null +++ b/edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/PostgresqlTransactionalStoreSetupExtension.java @@ -0,0 +1,126 @@ +/* + * 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/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtensionTest.java b/edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtensionTest.java new file mode 100644 index 000000000..0bc1446a9 --- /dev/null +++ b/edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheExtensionTest.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.edr.store.sql; + +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.spi.system.injection.ObjectFactory; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.tractusx.edc.edr.store.sql.SqlEndpointDataReferenceCacheExtension.DATASOURCE_SETTING_NAME; +import static org.eclipse.tractusx.edc.edr.store.sql.SqlEndpointDataReferenceCacheExtension.DEFAULT_DATASOURCE_NAME; +import static org.mockito.ArgumentMatchers.any; +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) +public class SqlEndpointDataReferenceCacheExtensionTest { + + SqlEndpointDataReferenceCacheExtension extension; + ServiceExtensionContext context; + + + @BeforeEach + void setUp(ObjectFactory factory, ServiceExtensionContext context) { + this.context = spy(context); + context.registerService(TypeManager.class, new TypeManager()); + context.registerService(DataSourceRegistry.class, mock(DataSourceRegistry.class)); + extension = factory.constructInstance(SqlEndpointDataReferenceCacheExtension.class); + } + + @Test + void shouldInitializeTheStore() { + var config = mock(Config.class); + when(context.getConfig()).thenReturn(config); + when(config.getString(any(), any())).thenReturn(DEFAULT_DATASOURCE_NAME); + + assertThat(extension.edrCache(context)).isInstanceOf(SqlEndpointDataReferenceCache.class); + + verify(config).getString(DATASOURCE_SETTING_NAME, DEFAULT_DATASOURCE_NAME); + } +} diff --git a/edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTest.java b/edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTest.java new file mode 100644 index 000000000..211ef5038 --- /dev/null +++ b/edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTest.java @@ -0,0 +1,84 @@ +/* + * 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 org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.sql.testfixtures.PostgresqlStoreSetupExtension; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCacheBaseTest; +import org.eclipse.tractusx.edc.edr.store.sql.schema.EdrStatements; +import org.eclipse.tractusx.edc.edr.store.sql.schema.postgres.PostgresEdrStatements; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.sql.SQLException; +import java.time.Clock; + +import static org.eclipse.tractusx.edc.edr.spi.TestFunctions.edr; +import static org.eclipse.tractusx.edc.edr.store.sql.SqlEndpointDataReferenceCache.SEPARATOR; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@PostgresqlDbIntegrationTest +@ExtendWith(PostgresqlStoreSetupExtension.class) +public class SqlEndpointDataReferenceCacheTest extends EndpointDataReferenceCacheBaseTest { + + EdrStatements statements = new PostgresEdrStatements(); + SqlEndpointDataReferenceCache cache; + + Clock clock = Clock.systemUTC(); + + Vault vault = mock(Vault.class); + + TypeManager typeManager = new TypeManager(); + + + @BeforeEach + void setUp(PostgresqlStoreSetupExtension extension) 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); + var schema = Files.readString(Paths.get("./docs/schema.sql")); + extension.runQuery(schema); + + } + + @AfterEach + void tearDown(PostgresqlStoreSetupExtension extension) throws SQLException { + extension.runQuery("DROP TABLE " + statements.getEdrTable() + " CASCADE"); + } + + @Override + protected EndpointDataReferenceCache getStore() { + return cache; + } + + + 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-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTransactionalTest.java new file mode 100644 index 000000000..b88ce8f12 --- /dev/null +++ b/edc-extensions/edr-cache-sql/src/test/java/org/eclipse/tractusx/edc/edr/store/sql/SqlEndpointDataReferenceCacheTransactionalTest.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.edr.store.sql; + +import org.eclipse.edc.junit.annotations.PostgresqlDbIntegrationTest; +import org.eclipse.edc.spi.persistence.EdcPersistenceException; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; +import org.eclipse.tractusx.edc.edr.store.sql.schema.EdrStatements; +import org.eclipse.tractusx.edc.edr.store.sql.schema.postgres.PostgresEdrStatements; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.sql.SQLException; +import java.time.Clock; + +import static java.util.UUID.randomUUID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.eclipse.tractusx.edc.edr.spi.TestFunctions.edr; +import static org.eclipse.tractusx.edc.edr.spi.TestFunctions.edrEntry; +import static org.eclipse.tractusx.edc.edr.store.sql.SqlEndpointDataReferenceCache.VAULT_PREFIX; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@PostgresqlDbIntegrationTest +@ExtendWith(PostgresqlTransactionalStoreSetupExtension.class) +public class SqlEndpointDataReferenceCacheTransactionalTest { + + EdrStatements statements = new PostgresEdrStatements(); + SqlEndpointDataReferenceCache cache; + + Clock clock = Clock.systemUTC(); + + Vault vault = mock(Vault.class); + + TypeManager typeManager = new TypeManager(); + + @BeforeEach + void setUp(PostgresqlTransactionalStoreSetupExtension extension) 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); + var schema = Files.readString(Paths.get("./docs/schema.sql")); + extension.runQuery(schema); + + } + + @Test + void save_shouldFail_whenVaultError() { + + var tpId = "tp1"; + var assetId = "asset1"; + var edrId = "edr1"; + + var edr = edr(edrId); + var entry = edrEntry(assetId, randomUUID().toString(), tpId); + + when(vault.storeSecret(any(), any())).thenReturn(Result.failure("fail")); + when(vault.resolveSecret(edr.getId())).thenReturn(typeManager.writeValueAsString(edr)); + + assertThatThrownBy(() -> cache.save(entry, edr)).isInstanceOf(EdcPersistenceException.class); + + assertThat(cache.resolveReference(tpId)) + .isNull(); + + } + + @Test + void save() { + + var tpId = "tp1"; + var assetId = "asset1"; + var edrId = "edr1"; + + var edr = edr(edrId); + var entry = edrEntry(assetId, randomUUID().toString(), tpId); + + when(vault.storeSecret(any(), any())).thenReturn(Result.success()); + when(vault.resolveSecret(VAULT_PREFIX + edr.getId())).thenReturn(typeManager.writeValueAsString(edr)); + + cache.save(entry, edr); + + assertThat(cache.resolveReference(tpId)) + .isNotNull() + .extracting(EndpointDataReference::getId) + .isEqualTo(edrId); + + var edrs = cache.referencesForAsset(assetId); + assertThat(edrs.size()).isEqualTo(1); + assertThat(edrs.get((0)).getId()).isEqualTo(edrId); + + verify(vault).storeSecret(eq(VAULT_PREFIX + edr.getId()), any()); + verify(vault, times(2)).resolveSecret(eq(VAULT_PREFIX + edr.getId())); + + } + + @Test + void deleteByTransferProcessId_shouldDelete_WhenFound() { + + var entry = edrEntry("assetId", "agreementId", "tpId"); + var edr = edr("edrId"); + cache.save(entry, edr); + + assertThat(cache.deleteByTransferProcessId(entry.getTransferProcessId())) + .extracting(StoreResult::getContent) + .isEqualTo(entry); + + assertThat(cache.resolveReference(entry.getTransferProcessId())).isNull(); + assertThat(cache.referencesForAsset(entry.getAssetId())).hasSize(0); + assertThat(cache.queryForEntries(QuerySpec.max())).hasSize(0); + + verify(vault).storeSecret(eq(VAULT_PREFIX + edr.getId()), any()); + verify(vault).deleteSecret(eq(VAULT_PREFIX + edr.getId())); + } + + @AfterEach + void tearDown(PostgresqlTransactionalStoreSetupExtension extension) throws SQLException { + extension.runQuery("DROP TABLE " + statements.getEdrTable() + " CASCADE"); + } + +} diff --git a/edc-extensions/hashicorp-vault/build.gradle.kts b/edc-extensions/hashicorp-vault/build.gradle.kts index caa93d104..afc22687d 100644 --- a/edc-extensions/hashicorp-vault/build.gradle.kts +++ b/edc-extensions/hashicorp-vault/build.gradle.kts @@ -27,7 +27,7 @@ dependencies { implementation(libs.edc.junit) implementation(libs.bouncyCastle.bcpkixJdk18on) implementation(libs.okhttp) - implementation("org.testcontainers:vault:1.18.1") - implementation("org.testcontainers:junit-jupiter:1.18.1") + implementation("org.testcontainers:vault:1.18.2") + implementation("org.testcontainers:junit-jupiter:1.18.2") testImplementation(libs.mockito.inline) } 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 index dae92725f..237171daa 100644 --- 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 @@ -20,78 +20,88 @@ package org.eclipse.tractusx.edc.hashicorpvault; -import java.time.Duration; +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_URL = "edc.vault.hashicorp.url"; - public static final String VAULT_TOKEN = "edc.vault.hashicorp.token"; + 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 = "edc.vault.hashicorp.api.secret.path"; - public static final String VAULT_API_SECRET_PATH_DEFAULT = "/v1/secret"; + 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 = "edc.vault.hashicorp.api.health.check.path"; - public static final String VAULT_API_HEALTH_PATH_DEFAULT = "/v1/sys/health"; + 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 String VAULT_HEALTH_CHECK_STANDBY_OK = + "edc.vault.hashicorp.health.check.standby.ok"; - public static final boolean VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT = false; + public static final boolean VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT = false; - private static final String VAULT_TIMEOUT_SECONDS = "edc.vault.hashicorp.timeout.seconds"; + private static final String VAULT_TIMEOUT_SECONDS = "edc.vault.hashicorp.timeout.seconds"; - protected OkHttpClient createOkHttpClient(HashicorpVaultClientConfig config) { - OkHttpClient.Builder builder = - new OkHttpClient.Builder() - .callTimeout(config.getTimeout()) - .readTimeout(config.getTimeout()); + protected HashicorpVaultClient createVaultClient(ServiceExtensionContext context, ObjectMapper mapper) { + var config = loadHashicorpVaultClientConfig(context); - return builder.build(); - } + var okHttpClient = createOkHttpClient(config); + + return new HashicorpVaultClient(config, okHttpClient, mapper); + } - protected HashicorpVaultClientConfig loadHashicorpVaultClientConfig( - ServiceExtensionContext context) { + protected OkHttpClient createOkHttpClient(HashicorpVaultClientConfig config) { + OkHttpClient.Builder builder = + new OkHttpClient.Builder() + .callTimeout(config.getTimeout()) + .readTimeout(config.getTimeout()); - final String vaultUrl = context.getSetting(VAULT_URL, null); - if (vaultUrl == null) { - throw new HashicorpVaultException(String.format("Vault URL (%s) must be defined", VAULT_URL)); + return builder.build(); } - final int vaultTimeoutSeconds = Math.max(0, context.getSetting(VAULT_TIMEOUT_SECONDS, 30)); - final Duration vaultTimeoutDuration = Duration.ofSeconds(vaultTimeoutSeconds); + protected HashicorpVaultClientConfig loadHashicorpVaultClientConfig( + ServiceExtensionContext context) { - final String vaultToken = context.getSetting(VAULT_TOKEN, null); + final String vaultUrl = context.getSetting(VAULT_URL, null); + if (vaultUrl == null) { + throw new HashicorpVaultException(String.format("Vault URL (%s) must be defined", VAULT_URL)); + } - if (vaultToken == null) { - throw new HashicorpVaultException( - String.format("For Vault authentication [%s] is required", VAULT_TOKEN)); - } + 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); - final String apiSecretPath = - context.getSetting(VAULT_API_SECRET_PATH, VAULT_API_SECRET_PATH_DEFAULT); + if (vaultToken == null) { + throw new HashicorpVaultException( + String.format("For Vault authentication [%s] is required", VAULT_TOKEN)); + } - final String apiHealthPath = - context.getSetting(VAULT_API_HEALTH_PATH, VAULT_API_HEALTH_PATH_DEFAULT); + final String apiSecretPath = + context.getSetting(VAULT_API_SECRET_PATH, VAULT_API_SECRET_PATH_DEFAULT); - final boolean isHealthStandbyOk = - context.getSetting(VAULT_HEALTH_CHECK_STANDBY_OK, VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT); + final String apiHealthPath = + context.getSetting(VAULT_API_HEALTH_PATH, VAULT_API_HEALTH_PATH_DEFAULT); - return HashicorpVaultClientConfig.builder() - .vaultUrl(vaultUrl) - .vaultToken(vaultToken) - .vaultApiSecretPath(apiSecretPath) - .vaultApiHealthPath(apiHealthPath) - .isVaultApiHealthStandbyOk(isHealthStandbyOk) - .timeout(vaultTimeoutDuration) - .build(); - } + 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 index ed807cbb8..fbbfde119 100644 --- 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 @@ -20,40 +20,45 @@ 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; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -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; -/** Resolves an X.509 certificate in Hashicorp vault. */ -@RequiredArgsConstructor +/** + * Resolves an X.509 certificate in Hashicorp vault. + */ public class HashicorpCertificateResolver implements CertificateResolver { - @NonNull private final Vault vault; - @NonNull private final Monitor monitor; + private final Vault vault; + private final Monitor monitor; - @Override - public X509Certificate resolveCertificate(@NonNull String id) { - String certificateRepresentation = vault.resolveSecret(id); - if (certificateRepresentation == null) { - return null; + public HashicorpCertificateResolver(Vault vault, Monitor monitor) { + this.vault = vault; + this.monitor = monitor; } - 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); + + @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 index e8a59937a..b2ea9a0b1 100644 --- 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 @@ -20,39 +20,40 @@ package org.eclipse.tractusx.edc.hashicorpvault; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import org.eclipse.edc.spi.monitor.Monitor; 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. */ -@RequiredArgsConstructor +/** + * Implements a vault backed by Hashicorp Vault. + */ class HashicorpVault implements Vault { - @NonNull private final HashicorpVaultClient hashicorpVaultClient; - @NonNull private final Monitor monitor; + private final HashicorpVaultClient hashicorpVaultClient; + + HashicorpVault(HashicorpVaultClient hashicorpVaultClient) { + this.hashicorpVaultClient = hashicorpVaultClient; + } - @Override - public @Nullable String resolveSecret(@NonNull String key) { - Result result = hashicorpVaultClient.getSecretValue(key); + @Override + public @Nullable String resolveSecret(String key) { + Result result = hashicorpVaultClient.getSecretValue(key); - return result.succeeded() ? result.getContent() : null; - } + return result.succeeded() ? result.getContent() : null; + } - @Override - @NotNull - public Result storeSecret(@NotNull @NonNull String key, @NotNull @NonNull String value) { - Result result = - hashicorpVaultClient.setSecret(key, value); + @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()); - } + return result.succeeded() ? Result.success() : Result.failure(result.getFailureMessages()); + } - @Override - public Result deleteSecret(@NotNull @NonNull String key) { - return hashicorpVaultClient.destroySecret(key); - } + @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 index 850c33243..a34cb96f7 100644 --- 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 @@ -23,181 +23,182 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.Objects; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; import okhttp3.Headers; import okhttp3.HttpUrl; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; -import okhttp3.Response; +import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.result.Result; import org.jetbrains.annotations.NotNull; -@RequiredArgsConstructor -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"; - - @NonNull private final HashicorpVaultClientConfig config; - @NonNull private final OkHttpClient okHttpClient; - @NonNull private final ObjectMapper objectMapper; - - Result getSecretValue(@NonNull String key) { - HttpUrl requestURI = getSecretUrl(key, VAULT_SECRET_DATA_PATH); - Headers headers = getHeaders(); - Request request = new Request.Builder().url(requestURI).headers(headers).get().build(); - - try (Response response = okHttpClient.newCall(request).execute()) { - - if (response.code() == 404) { - return Result.failure(String.format(CALL_UNSUCCESSFUL_ERROR_TEMPLATE, "Secret not found")); - } - - if (response.isSuccessful()) { - String responseBody = Objects.requireNonNull(response.body()).string(); - HashicorpVaultGetEntryResponsePayload payload = - objectMapper.readValue(responseBody, HashicorpVaultGetEntryResponsePayload.class); - String 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()); +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()); + } } - } - - public HashicorpVaultHealthResponse getHealth() throws IOException { - - HashicorpVaultHealthResponse.HashicorpVaultHealthResponseBuilder healthResponseBuilder = - HashicorpVaultHealthResponse.builder(); - - HttpUrl requestURI = getHealthUrl(); - Headers headers = getHeaders(); - Request request = new Request.Builder().url(requestURI).headers(headers).get().build(); - try (Response response = okHttpClient.newCall(request).execute()) { - final int code = response.code(); - healthResponseBuilder.code(code); - - try { - String responseBody = Objects.requireNonNull(response.body()).string(); - HashicorpVaultHealthResponsePayload 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 - } + + @NotNull + private Headers getHeaders() { + return new Headers.Builder() + .add(VAULT_REQUEST_HEADER, Boolean.toString(true)) + .add(VAULT_TOKEN_HEADER, config.getVaultToken()) + .build(); } - return healthResponseBuilder.build(); - } - - Result setSecret( - @NonNull String key, @NonNull String value) { - HttpUrl requestURI = getSecretUrl(key, VAULT_SECRET_DATA_PATH); - Headers headers = getHeaders(); - HashicorpVaultCreateEntryRequestPayload requestPayload = - HashicorpVaultCreateEntryRequestPayload.builder() - .data(Collections.singletonMap(VAULT_DATA_ENTRY_NAME, value)) - .build(); - Request request = - new Request.Builder() - .url(requestURI) - .headers(headers) - .post(createRequestBody(requestPayload)) - .build(); - - try (Response response = okHttpClient.newCall(request).execute()) { - if (response.isSuccessful()) { - String responseBody = Objects.requireNonNull(response.body()).string(); - HashicorpVaultCreateEntryResponsePayload 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()); + 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(); } - } - - Result destroySecret(@NonNull String key) { - HttpUrl requestURI = getSecretUrl(key, VAULT_SECRET_METADATA_PATH); - Headers headers = getHeaders(); - Request request = new Request.Builder().url(requestURI).headers(headers).delete().build(); - - try (Response 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()); + + 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(); } - } - - @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 String vaultApiPath = config.getVaultApiSecretPath(); - - return Objects.requireNonNull(HttpUrl.parse(config.getVaultUrl())) - .newBuilder() - .addPathSegments(PathUtil.trimLeadingOrEndingSlash(vaultApiPath)) - .addPathSegment(entryType) - .addPathSegments(key) - .build(); - } - - private HttpUrl getHealthUrl() { - final String vaultHealthPath = config.getVaultApiHealthPath(); - final boolean 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); + + 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); } - 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 index 495d6937c..684351252 100644 --- 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 @@ -20,21 +20,84 @@ package org.eclipse.tractusx.edc.hashicorpvault; + import java.time.Duration; -import lombok.Builder; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -@Builder -@Getter -@RequiredArgsConstructor + class HashicorpVaultClientConfig { - @NonNull private final String vaultUrl; - @NonNull private final String vaultToken; - @NonNull private final String vaultApiSecretPath; - @NonNull private final String vaultApiHealthPath; - @NonNull private final Duration timeout; + 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; + } - private final boolean isVaultApiHealthStandbyOk; + 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 index fa31d7149..65d472f97 100644 --- 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 @@ -22,32 +22,64 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.Map; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Data + @JsonIgnoreProperties(ignoreUnknown = true) class HashicorpVaultCreateEntryRequestPayload { - @JsonProperty("options") - private Options options; - - @JsonProperty("data") - private Map data; - - @Builder - @NoArgsConstructor - @AllArgsConstructor - @Data - @JsonIgnoreProperties(ignoreUnknown = true) - static class Options { - @JsonProperty("cas") - private Integer cas; - } + @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 index 77ab76772..d75a19355 100644 --- 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 @@ -22,18 +22,14 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Data @JsonIgnoreProperties(ignoreUnknown = true) class HashicorpVaultCreateEntryResponsePayload { - @JsonProperty("data") - private HashicorpVaultEntryMetadata data; + @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 index bd59309c9..4f92bca85 100644 --- 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 @@ -22,25 +22,30 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.Map; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Data + @JsonIgnoreProperties(ignoreUnknown = true) class HashicorpVaultEntryMetadata { - @JsonProperty("custom_metadata") - private Map customMetadata; + @JsonProperty("custom_metadata") + private Map customMetadata; + + @JsonProperty("destroyed") + private Boolean destroyed; + + @JsonProperty("version") + private Integer version; + + public Map getCustomMetadata() { + return customMetadata; + } - @JsonProperty("destroyed") - private Boolean destroyed; + public Boolean getDestroyed() { + return destroyed; + } - @JsonProperty("version") - private Integer version; + 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 index bd3c60549..8488cfa34 100644 --- 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 @@ -24,11 +24,11 @@ public class HashicorpVaultException extends EdcException { - public HashicorpVaultException(String message) { - super(message); - } + public HashicorpVaultException(String message) { + super(message); + } - public HashicorpVaultException(String message, Throwable cause) { - super(message, cause); - } + 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 index 175c73d47..6e7c2763f 100644 --- 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 @@ -22,33 +22,34 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.Map; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Data + @JsonIgnoreProperties(ignoreUnknown = true) class HashicorpVaultGetEntryResponsePayload { - @JsonProperty("data") - private GetVaultEntryData data; + @JsonProperty("data") + private GetVaultEntryData data; - @Builder - @NoArgsConstructor - @AllArgsConstructor - @Data - @JsonIgnoreProperties(ignoreUnknown = true) - static class GetVaultEntryData { + public GetVaultEntryData getData() { + return data; + } - @JsonProperty("data") - private Map data; + @JsonIgnoreProperties(ignoreUnknown = true) + static class GetVaultEntryData { + + @JsonProperty("data") + private Map data; + + @JsonProperty("metadata") + private HashicorpVaultEntryMetadata metadata; + + public Map getData() { + return data; + } - @JsonProperty("metadata") - private HashicorpVaultEntryMetadata metadata; - } + 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 index 9834b0d03..00cf14b7d 100644 --- 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 @@ -14,81 +14,84 @@ package org.eclipse.tractusx.edc.hashicorpvault; -import java.io.IOException; -import lombok.RequiredArgsConstructor; +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; -@RequiredArgsConstructor -public class HashicorpVaultHealthCheck - implements ReadinessProvider, LivenessProvider, StartupStatusProvider { +import static java.lang.String.format; - private static final String HEALTH_CHECK_ERROR_TEMPLATE = - "HashiCorp Vault HealthCheck unsuccessful. %s %s"; +public class HashicorpVaultHealthCheck implements ReadinessProvider, LivenessProvider, StartupStatusProvider { - private final HashicorpVaultClient client; - private final Monitor monitor; + private static final String HEALTH_CHECK_ERROR_TEMPLATE = + "HashiCorp Vault HealthCheck unsuccessful. %s %s"; - @Override - public HealthCheckResult get() { + private final HashicorpVaultClient client; + private final Monitor monitor; - try { - final HashicorpVaultHealthResponse response = client.getHealth(); + public HashicorpVaultHealthCheck(HashicorpVaultClient client, Monitor monitor) { + this.client = client; + this.monitor = monitor; + } + + @Override + public HealthCheckResult get() { - 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 = - String.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 = - String.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 = - String.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 = - String.format( - HEALTH_CHECK_ERROR_TEMPLATE, "Vault is not initialized", response.getPayload()); - monitor.warning(notInitializedMsg); - return HealthCheckResult.failed(notInitializedMsg); - case SEALED: - final String sealedMsg = - String.format(HEALTH_CHECK_ERROR_TEMPLATE, "Vault is sealed", response.getPayload()); - monitor.warning(sealedMsg); - return HealthCheckResult.failed(sealedMsg); - case UNSPECIFIED: - default: - final String unspecifiedMsg = - String.format( - HEALTH_CHECK_ERROR_TEMPLATE, - "Unspecified response from vault. Code: " + response.getCode(), - response.getPayload()); - monitor.warning(unspecifiedMsg); - return HealthCheckResult.failed(unspecifiedMsg); - } + 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); + } - } catch (IOException e) { - final String exceptionMsg = - String.format(HEALTH_CHECK_ERROR_TEMPLATE, "IOException: " + e.getMessage(), ""); - monitor.severe(exceptionMsg); - 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 index 5b7cca1a3..8bbf4634f 100644 --- 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 @@ -20,50 +20,52 @@ package org.eclipse.tractusx.edc.hashicorpvault; -import okhttp3.OkHttpClient; +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 { + implements ServiceExtension { - public static final String VAULT_HEALTH_CHECK = "edc.vault.hashicorp.health.check.enabled"; + public static final String VAULT_HEALTH_CHECK = "edc.vault.hashicorp.health.check.enabled"; - public static final boolean VAULT_HEALTH_CHECK_DEFAULT = true; + public static final boolean VAULT_HEALTH_CHECK_DEFAULT = true; - @Override - public String name() { - return "Hashicorp Vault Health Check"; - } + @Inject + private HealthCheckService healthCheckService; - @Override - public void initialize(ServiceExtensionContext context) { - final HashicorpVaultClientConfig config = loadHashicorpVaultClientConfig(context); + @Inject + private TypeManager typeManager; - final OkHttpClient okHttpClient = createOkHttpClient(config); + @Override + public String name() { + return "Hashicorp Vault Health Check"; + } - final HashicorpVaultClient client = - new HashicorpVaultClient(config, okHttpClient, context.getTypeManager().getMapper()); - configureHealthCheck(client, context); + @Override + public void initialize(ServiceExtensionContext context) { + var client = createVaultClient(context, typeManager.getMapper()); - context.getMonitor().info("HashicorpVaultExtension: health check initialization complete."); - } + configureHealthCheck(client, context); - private void configureHealthCheck(HashicorpVaultClient client, ServiceExtensionContext context) { - final boolean healthCheckEnabled = - context.getSetting(VAULT_HEALTH_CHECK, VAULT_HEALTH_CHECK_DEFAULT); - if (!healthCheckEnabled) return; + context.getMonitor().info("HashicorpVaultExtension: health check initialization complete."); + } - final HashicorpVaultHealthCheck healthCheck = - new HashicorpVaultHealthCheck(client, context.getMonitor()); + private void configureHealthCheck(HashicorpVaultClient client, ServiceExtensionContext context) { + var healthCheckEnabled = + context.getSetting(VAULT_HEALTH_CHECK, VAULT_HEALTH_CHECK_DEFAULT); + if (!healthCheckEnabled) return; - final HealthCheckService healthCheckService = context.getService(HealthCheckService.class); - healthCheckService.addLivenessProvider(healthCheck); - healthCheckService.addReadinessProvider(healthCheck); - healthCheckService.addStartupStatusProvider(healthCheck); - } + 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 index bbf991684..5e48c8078 100644 --- 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 @@ -14,46 +14,78 @@ package org.eclipse.tractusx.edc.hashicorpvault; -import lombok.Builder; -import lombok.Getter; -import org.jetbrains.annotations.Nullable; - -@Builder -@Getter public class HashicorpVaultHealthResponse { - @Nullable private HashicorpVaultHealthResponsePayload payload; - - private int 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; + 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; + } } - } - - 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 - } } 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 index 67ef6533c..fd613a0a0 100644 --- 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 @@ -16,46 +16,76 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Data -@ToString + @JsonIgnoreProperties(ignoreUnknown = true) public class HashicorpVaultHealthResponsePayload { - @JsonProperty("initialized") - private boolean isInitialized; + @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; + } - @JsonProperty("sealed") - private boolean isSealed; + public boolean isSealed() { + return isSealed; + } - @JsonProperty("standby") - private boolean isStandby; + public boolean isStandby() { + return isStandby; + } - @JsonProperty("performance_standby") - private boolean isPerformanceStandby; + public boolean isPerformanceStandby() { + return isPerformanceStandby; + } - @JsonProperty("replication_performance_mode") - private String replicationPerformanceMode; + public String getReplicationPerformanceMode() { + return replicationPerformanceMode; + } - @JsonProperty("replication_dr_mode") - private String replicationDrMode; + public String getReplicationDrMode() { + return replicationDrMode; + } - @JsonProperty("server_time_utc") - private long serverTimeUtc; + public long getServerTimeUtc() { + return serverTimeUtc; + } - @JsonProperty("version") - private String version; + public String getVersion() { + return version; + } - @JsonProperty("cluster_name") - private String clusterName; + public String getClusterName() { + return clusterName; + } - @JsonProperty("cluster_id") - private String clusterId; + 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 index d8b41b2c1..3a979f639 100644 --- 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 @@ -20,7 +20,7 @@ package org.eclipse.tractusx.edc.hashicorpvault; -import okhttp3.OkHttpClient; +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; @@ -28,34 +28,33 @@ 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 { + implements ServiceExtension { - @Override - public String name() { - return "Hashicorp Vault"; - } + @Inject + private TypeManager typeManager; - @Override - public void initialize(ServiceExtensionContext context) { - final HashicorpVaultClientConfig config = loadHashicorpVaultClientConfig(context); + @Override + public String name() { + return "Hashicorp Vault"; + } - final OkHttpClient okHttpClient = createOkHttpClient(config); + @Override + public void initialize(ServiceExtensionContext context) { + var client = createVaultClient(context, typeManager.getMapper()); - final HashicorpVaultClient client = - new HashicorpVaultClient(config, okHttpClient, context.getTypeManager().getMapper()); + var vault = new HashicorpVault(client); + var certificateResolver = + new HashicorpCertificateResolver(vault, context.getMonitor()); + var privateKeyResolver = new VaultPrivateKeyResolver(vault); - final HashicorpVault vault = new HashicorpVault(client, context.getMonitor()); - final CertificateResolver certificateResolver = - new HashicorpCertificateResolver(vault, context.getMonitor()); - final VaultPrivateKeyResolver privateKeyResolver = new VaultPrivateKeyResolver(vault); + context.registerService(Vault.class, vault); + context.registerService(CertificateResolver.class, certificateResolver); + context.registerService(PrivateKeyResolver.class, privateKeyResolver); - context.registerService(Vault.class, vault); - context.registerService(CertificateResolver.class, certificateResolver); - context.registerService(PrivateKeyResolver.class, privateKeyResolver); - - context.getMonitor().info("HashicorpVaultExtension: authentication/initialization complete."); - } + 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 index f22895355..1652ab758 100644 --- 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 @@ -16,14 +16,15 @@ final class PathUtil { - private PathUtil() {} + private PathUtil() { + } - static String trimLeadingOrEndingSlash(String path) { - var fixedPath = path; + 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); + if (fixedPath.startsWith("/")) fixedPath = fixedPath.substring(1); + if (fixedPath.endsWith("/")) fixedPath = fixedPath.substring(0, fixedPath.length() - 1); - return fixedPath; - } + 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 index 6596d781b..0bd902b75 100644 --- 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 @@ -20,44 +20,48 @@ 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; -import lombok.NonNull; -import lombok.SneakyThrows; -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; final class PemUtil { - private PemUtil() { - throw new IllegalStateException("Private constructor invocation disallowed"); - } + private static final Provider PROVIDER = new BouncyCastleProvider(); + private static final JcaX509CertificateConverter X509_CONVERTER = + new JcaX509CertificateConverter().setProvider(PROVIDER); - 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); + } - @SneakyThrows - public static X509Certificate readX509Certificate(@NotNull @NonNull InputStream inputStream) { - X509CertificateHolder x509CertificateHolder = parsePem(inputStream); - if (x509CertificateHolder == null) { - return null; } - return X509_CONVERTER.getCertificate(x509CertificateHolder); - } - - @SuppressWarnings("unchecked") - private static T parsePem(@NotNull @NonNull InputStream inputStream) throws IOException { - try (Reader reader = new InputStreamReader(inputStream)) { - PEMParser pemParser = new PEMParser(reader); - return (T) pemParser.readObject(); + + @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 084cc158a..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpIT.java +++ /dev/null @@ -1,166 +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 lombok.Getter; -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(); - - private final TestExtension testExtension = new TestExtension(); - - protected Vault getVault() { - return testExtension.getVault(); - } - - protected CertificateResolver getCertificateResolver() { - return testExtension.getCertificateResolver(); - } - - @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)); - - @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); - } - }; - } - - @Getter - 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); - } - } - - 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/AbstractHashicorpIt.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpIt.java new file mode 100644 index 000000000..09108c77b --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpIt.java @@ -0,0 +1,171 @@ +/* + * 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/HashicorpCertificateResolverIT.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverIT.java deleted file mode 100644 index 48fc06eca..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverIT.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 java.security.cert.X509Certificate; -import java.util.UUID; -import lombok.SneakyThrows; -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; - -class HashicorpCertificateResolverIT extends AbstractHashicorpIT { - - @Test - @SneakyThrows - void resolveCertificate_success() { - 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 - @SneakyThrows - 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/HashicorpCertificateResolverIntegrationTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverIntegrationTest.java new file mode 100644 index 000000000..6476dd2d2 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverIntegrationTest.java @@ -0,0 +1,62 @@ +/* + * 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 index 754bdda28..2e2f4350d 100644 --- 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 @@ -20,53 +20,55 @@ package org.eclipse.tractusx.edc.hashicorpvault; -import java.security.cert.X509Certificate; -import lombok.SneakyThrows; +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"; + private static final String KEY = "key"; - // mocks - private HashicorpCertificateResolver certificateResolver; - private HashicorpVault vault; + // 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); - } + @BeforeEach + void setup() { + vault = Mockito.mock(HashicorpVault.class); + final Monitor monitor = Mockito.mock(Monitor.class); + certificateResolver = new HashicorpCertificateResolver(vault, monitor); + } - @Test - @SneakyThrows - void resolveCertificate() { - // prepare - X509Certificate certificateExpected = X509CertificateTestUtil.generateCertificate(5, "Test"); - String pem = X509CertificateTestUtil.convertToPem(certificateExpected); - Mockito.when(vault.resolveSecret(key)).thenReturn(pem); + @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); + // invoke + certificateResolver.resolveCertificate(KEY); - // verify - Mockito.verify(vault, Mockito.times(1)).resolveSecret(key); - } + // verify + Mockito.verify(vault, Mockito.times(1)).resolveSecret(KEY); + } - @Test - @SneakyThrows - void nullIfVaultEmpty() { - // prepare - Mockito.when(vault.resolveSecret(key)).thenReturn(null); + @Test + void nullIfVaultEmpty() { + // prepare + Mockito.when(vault.resolveSecret(KEY)).thenReturn(null); - // invoke - final X509Certificate certificate = certificateResolver.resolveCertificate(key); + // invoke + final X509Certificate certificate = certificateResolver.resolveCertificate(KEY); - // verify - Assertions.assertNull(certificate); - } + // 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 index 2bddac46e..c8c442a77 100644 --- 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 @@ -21,236 +21,235 @@ package org.eclipse.tractusx.edc.hashicorpvault; import com.fasterxml.jackson.databind.ObjectMapper; -import java.time.Duration; -import java.util.UUID; -import lombok.SneakyThrows; import okhttp3.Call; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; -import org.eclipse.edc.spi.result.Result; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -class HashicorpVaultClientTest { - private static final String key = "key"; - private static final String customSecretPath = "v1/test/secret"; - private static final String healthPath = "sys/health"; - private static final Duration timeout = Duration.ofSeconds(30); - private static final ObjectMapper objectMapper = new ObjectMapper(); - - @Test - @SneakyThrows - void getSecretValue() { - // prepare - String vaultUrl = "https://mock.url"; - String vaultToken = UUID.randomUUID().toString(); - HashicorpVaultClientConfig hashicorpVaultClientConfig = - HashicorpVaultClientConfig.builder() - .vaultUrl(vaultUrl) - .vaultApiSecretPath(customSecretPath) - .vaultApiHealthPath(healthPath) - .isVaultApiHealthStandbyOk(false) - .vaultToken(vaultToken) - .timeout(timeout) - .build(); - - OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class); - HashicorpVaultClient vaultClient = - new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, objectMapper); - Call call = Mockito.mock(Call.class); - Response response = Mockito.mock(Response.class); - ResponseBody body = Mockito.mock(ResponseBody.class); - HashicorpVaultGetEntryResponsePayload payload = new HashicorpVaultGetEntryResponsePayload(); - - Mockito.when(okHttpClient.newCall(Mockito.any(Request.class))).thenReturn(call); - Mockito.when(call.execute()).thenReturn(response); - Mockito.when(response.code()).thenReturn(200); - Mockito.when(response.body()).thenReturn(body); - Mockito.when(body.string()).thenReturn(payload.toString()); - - // invoke - Result result = vaultClient.getSecretValue(key); - - // verify - Assertions.assertNotNull(result); - Mockito.verify(okHttpClient, Mockito.times(1)) - .newCall( - Mockito.argThat( - request -> - request.method().equalsIgnoreCase("GET") - && request.url().encodedPath().contains(customSecretPath + "/data") - && request.url().encodedPathSegments().contains(key))); - } - - @Test - @SneakyThrows - void setSecretValue() { - // prepare - String vaultUrl = "https://mock.url"; - String vaultToken = UUID.randomUUID().toString(); - String secretValue = UUID.randomUUID().toString(); - HashicorpVaultClientConfig hashicorpVaultClientConfig = - HashicorpVaultClientConfig.builder() - .vaultUrl(vaultUrl) - .vaultApiSecretPath(customSecretPath) - .vaultApiHealthPath(healthPath) - .isVaultApiHealthStandbyOk(false) - .vaultToken(vaultToken) - .timeout(timeout) - .build(); - - OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class); - HashicorpVaultClient vaultClient = - new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, objectMapper); - HashicorpVaultCreateEntryResponsePayload payload = - new HashicorpVaultCreateEntryResponsePayload(); - - Call call = Mockito.mock(Call.class); - Response response = Mockito.mock(Response.class); - ResponseBody body = Mockito.mock(ResponseBody.class); - - Mockito.when(okHttpClient.newCall(Mockito.any(Request.class))).thenReturn(call); - Mockito.when(call.execute()).thenReturn(response); - Mockito.when(response.code()).thenReturn(200); - Mockito.when(response.body()).thenReturn(body); - Mockito.when(body.string()).thenReturn(payload.toString()); - - // invoke - Result result = - vaultClient.setSecret(key, secretValue); - // verify - Assertions.assertNotNull(result); - Mockito.verify(okHttpClient, Mockito.times(1)) - .newCall( - Mockito.argThat( - request -> - request.method().equalsIgnoreCase("POST") - && request.url().encodedPath().contains(customSecretPath + "/data") - && request.url().encodedPathSegments().contains(key))); - } - - @Test - @SneakyThrows - void getHealth() { - // prepare - String vaultUrl = "https://mock.url"; - String vaultToken = UUID.randomUUID().toString(); - String secretValue = UUID.randomUUID().toString(); - HashicorpVaultClientConfig hashicorpVaultClientConfig = - HashicorpVaultClientConfig.builder() - .vaultUrl(vaultUrl) - .vaultApiSecretPath(customSecretPath) - .vaultApiHealthPath(healthPath) - .isVaultApiHealthStandbyOk(false) - .vaultToken(vaultToken) - .timeout(timeout) - .build(); - - OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class); - HashicorpVaultClient vaultClient = - new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, objectMapper); - HashicorpVaultHealthResponsePayload payload = new HashicorpVaultHealthResponsePayload(); - - Call call = Mockito.mock(Call.class); - Response response = Mockito.mock(Response.class); - ResponseBody body = Mockito.mock(ResponseBody.class); - - Mockito.when(okHttpClient.newCall(Mockito.any(Request.class))).thenReturn(call); - Mockito.when(call.execute()).thenReturn(response); - Mockito.when(response.code()).thenReturn(200); - Mockito.when(response.body()).thenReturn(body); - Mockito.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 - HashicorpVaultHealthResponse result = vaultClient.getHealth(); - - // verify - Assertions.assertNotNull(result); - Mockito.verify(okHttpClient, Mockito.times(1)) - .newCall( - Mockito.argThat( - request -> - request.method().equalsIgnoreCase("GET") - && request.url().encodedPath().contains(healthPath) - && 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 - @SneakyThrows - void destroySecretValue() { - // prepare - String vaultUrl = "https://mock.url"; - String vaultToken = UUID.randomUUID().toString(); - HashicorpVaultClientConfig hashicorpVaultClientConfig = - HashicorpVaultClientConfig.builder() - .vaultUrl(vaultUrl) - .vaultApiSecretPath(customSecretPath) - .vaultApiHealthPath(healthPath) - .isVaultApiHealthStandbyOk(false) - .vaultToken(vaultToken) - .timeout(timeout) - .build(); - - OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class); - HashicorpVaultClient vaultClient = - new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, objectMapper); - - Call call = Mockito.mock(Call.class); - Response response = Mockito.mock(Response.class); - ResponseBody body = Mockito.mock(ResponseBody.class); - Mockito.when(okHttpClient.newCall(Mockito.any(Request.class))).thenReturn(call); - Mockito.when(call.execute()).thenReturn(response); - Mockito.when(response.code()).thenReturn(200); - Mockito.when(response.body()).thenReturn(body); +import java.io.IOException; +import java.time.Duration; +import java.util.UUID; - // invoke - Result result = vaultClient.destroySecret(key); +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; - // verify - Assertions.assertNotNull(result); - Mockito.verify(okHttpClient, Mockito.times(1)) - .newCall( - Mockito.argThat( - request -> - request.method().equalsIgnoreCase("DELETE") - && request.url().encodedPath().contains(customSecretPath + "/metadata") - && request.url().encodedPathSegments().contains(key))); - } +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 index dfba9fe1c..7e355d48f 100644 --- 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 @@ -20,71 +20,54 @@ 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.types.TypeManager; +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.mockito.Mockito; +import org.junit.jupiter.api.extension.ExtendWith; -class HashicorpVaultExtensionTest { - - private static final String VAULT_URL = "https://example.com"; - private static final String VAULT_TOKEN = "token"; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; - private HashicorpVaultVaultExtension extension; +@ExtendWith(DependencyInjectionExtension.class) +class HashicorpVaultExtensionTest { - // mocks - private ServiceExtensionContext context; - private Monitor monitor; - private HealthCheckService healthCheckService; + private static final String VAULT_URL = "https://example.com"; + private static final String VAULT_TOKEN = "token"; - @BeforeEach - void setup() { - context = Mockito.mock(ServiceExtensionContext.class); - monitor = Mockito.mock(Monitor.class); - healthCheckService = Mockito.mock(HealthCheckService.class); - extension = new HashicorpVaultVaultExtension(); + private HashicorpVaultVaultExtension extension; - Mockito.when(context.getService(HealthCheckService.class)).thenReturn(healthCheckService); - Mockito.when(context.getMonitor()).thenReturn(monitor); - Mockito.when(context.getTypeManager()).thenReturn(new TypeManager()); - Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)) - .thenReturn(VAULT_URL); - Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) - .thenReturn(VAULT_TOKEN); + // mocks + private ServiceExtensionContext context; + private Monitor monitor; + private HealthCheckService healthCheckService; - Mockito.when( - context.getSetting( - HashicorpVaultVaultExtension.VAULT_API_SECRET_PATH, - HashicorpVaultVaultExtension.VAULT_API_SECRET_PATH_DEFAULT)) - .thenReturn(HashicorpVaultVaultExtension.VAULT_API_SECRET_PATH_DEFAULT); - Mockito.when( - context.getSetting( - HashicorpVaultVaultExtension.VAULT_API_HEALTH_PATH, - HashicorpVaultVaultExtension.VAULT_API_HEALTH_PATH_DEFAULT)) - .thenReturn(HashicorpVaultVaultExtension.VAULT_API_HEALTH_PATH_DEFAULT); - Mockito.when( - context.getSetting( - HashicorpVaultVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK, - HashicorpVaultVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT)) - .thenReturn(HashicorpVaultVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT); - } + @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() { - Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)).thenReturn(null); + @Test + void throwsHashicorpVaultExceptionOnVaultUrlUndefined() { + when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)).thenReturn(null); - Assertions.assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); - } + Assertions.assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); + } - @Test - void throwsHashicorpVaultExceptionOnVaultTokenUndefined() { - Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) - .thenReturn(null); + @Test + void throwsHashicorpVaultExceptionOnVaultTokenUndefined() { + when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) + .thenReturn(null); - Assertions.assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); - } + 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 index 5991f8c1e..57a32ae99 100644 --- 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 @@ -20,100 +20,78 @@ package org.eclipse.tractusx.edc.hashicorpvault; -import org.eclipse.edc.spi.monitor.Monitor; +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.types.TypeManager; -import org.junit.jupiter.api.Assertions; +import org.eclipse.edc.spi.system.injection.ObjectFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; +import org.junit.jupiter.api.extension.ExtendWith; -class HashicorpVaultHealthCheckExtensionTest { - - private static final String VAULT_URL = "https://example.com"; - private static final String VAULT_TOKEN = "token"; - - private HashicorpVaultHealthExtension extension; - - // mocks - private ServiceExtensionContext context; - private Monitor monitor; - private HealthCheckService healthCheckService; - - @BeforeEach - void setup() { - context = Mockito.mock(ServiceExtensionContext.class); - monitor = Mockito.mock(Monitor.class); - healthCheckService = Mockito.mock(HealthCheckService.class); - extension = new HashicorpVaultHealthExtension(); - - Mockito.when(context.getService(HealthCheckService.class)).thenReturn(healthCheckService); - Mockito.when(context.getMonitor()).thenReturn(monitor); - Mockito.when(context.getTypeManager()).thenReturn(new TypeManager()); - Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)) - .thenReturn(VAULT_URL); - Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) - .thenReturn(VAULT_TOKEN); - - Mockito.when( - context.getSetting( - HashicorpVaultHealthExtension.VAULT_API_SECRET_PATH, - HashicorpVaultHealthExtension.VAULT_API_SECRET_PATH_DEFAULT)) - .thenReturn(HashicorpVaultHealthExtension.VAULT_API_SECRET_PATH_DEFAULT); - Mockito.when( - context.getSetting( - HashicorpVaultHealthExtension.VAULT_API_HEALTH_PATH, - HashicorpVaultHealthExtension.VAULT_API_HEALTH_PATH_DEFAULT)) - .thenReturn(HashicorpVaultHealthExtension.VAULT_API_HEALTH_PATH_DEFAULT); - Mockito.when( - context.getSetting( - HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK, - HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK_DEFAULT)) - .thenReturn(HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK_DEFAULT); - Mockito.when( - context.getSetting( - HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK_STANDBY_OK, - HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT)) - .thenReturn(HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT); - } - - @Test - void registersHealthCheckIfEnabled() { - Mockito.when(context.getSetting(HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK, true)) - .thenReturn(true); +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; - extension.initialize(context); - - Mockito.verify(healthCheckService, Mockito.times(1)).addReadinessProvider(Mockito.any()); - Mockito.verify(healthCheckService, Mockito.times(1)).addLivenessProvider(Mockito.any()); - Mockito.verify(healthCheckService, Mockito.times(1)).addStartupStatusProvider(Mockito.any()); - } - - @Test - void registersNoHealthCheckIfDisabled() { - Mockito.when(context.getSetting(HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK, true)) - .thenReturn(false); - - extension.initialize(context); - - Mockito.verify(healthCheckService, Mockito.times(0)).addReadinessProvider(Mockito.any()); - Mockito.verify(healthCheckService, Mockito.times(0)).addLivenessProvider(Mockito.any()); - Mockito.verify(healthCheckService, Mockito.times(0)).addStartupStatusProvider(Mockito.any()); - } - - @Test - void throwsHashicorpVaultExceptionOnVaultUrlUndefined() { - Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)).thenReturn(null); - - Assertions.assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); - } - - @Test - void throwsHashicorpVaultExceptionOnVaultTokenUndefined() { - Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) - .thenReturn(null); +@ExtendWith(DependencyInjectionExtension.class) +class HashicorpVaultHealthCheckExtensionTest { - Assertions.assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); - } + 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 index 3abf95289..dae708c81 100644 --- 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 @@ -20,9 +20,8 @@ package org.eclipse.tractusx.edc.hashicorpvault; -import java.io.IOException; +import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.health.HealthCheckResult; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -32,44 +31,43 @@ class HashicorpVaultHealthCheckTest { - private HashicorpVaultHealthCheck healthCheck; + private HashicorpVaultHealthCheck healthCheck; - // mocks - private Monitor monitor; - private HashicorpVaultClient client; + // mocks + private Monitor monitor; + private HashicorpVaultClient client; - @BeforeEach - void setup() { - monitor = Mockito.mock(Monitor.class); - client = Mockito.mock(HashicorpVaultClient.class); + @BeforeEach + void setup() { + monitor = Mockito.mock(Monitor.class); + client = Mockito.mock(HashicorpVaultClient.class); - healthCheck = new HashicorpVaultHealthCheck(client, monitor); - } + healthCheck = new HashicorpVaultHealthCheck(client, monitor); + } - @ParameterizedTest - @ValueSource(ints = {200, 409, 472, 473, 501, 503, 999}) - void testResponseFromCode(int code) throws IOException { + @ParameterizedTest + @ValueSource(ints = {200, 409, 472, 473, 501, 503, 999}) + void testResponseFromCode(int code) { - Mockito.when(client.getHealth()) - .thenReturn( - new HashicorpVaultHealthResponse(new HashicorpVaultHealthResponsePayload(), code)); + Mockito.when(client.getHealth()) + .thenReturn(HashicorpVaultHealthResponse.Builder.newInstance().payload(new HashicorpVaultHealthResponsePayload()).code(code).build()); - final HealthCheckResult result = healthCheck.get(); + 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()); + 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() throws IOException { - Mockito.when(client.getHealth()).thenThrow(new IOException()); + @Test + void testResponseFromException() { + Mockito.when(client.getHealth()).thenThrow(new EdcException("foo-bar")); - final HealthCheckResult result = healthCheck.get(); - Assertions.assertFalse(result.succeeded()); - } + 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 f6cb202b8..000000000 --- a/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultIT.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.hashicorpvault; - -import java.util.UUID; -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; - -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_inASubDirectory() { - 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/HashicorpVaultIt.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultIt.java new file mode 100644 index 000000000..fd56385d1 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultIt.java @@ -0,0 +1,122 @@ +/* + * 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 index 03512f2a6..2ca9e4da8 100644 --- 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 @@ -20,8 +20,6 @@ package org.eclipse.tractusx.edc.hashicorpvault; -import java.util.UUID; -import lombok.SneakyThrows; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.result.Result; import org.junit.jupiter.api.Assertions; @@ -29,123 +27,119 @@ 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, monitor); - } - - @Test - @SneakyThrows - 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 - @SneakyThrows - 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 - @SneakyThrows - 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 - @SneakyThrows - 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 - @SneakyThrows - 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 - @SneakyThrows - 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()); - } + 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 index a5d9821dd..b79a341b2 100644 --- 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 @@ -14,27 +14,28 @@ package org.eclipse.tractusx.edc.hashicorpvault; -import java.util.stream.Stream; 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 { - @ParameterizedTest - @MethodSource("provideStringsForTrimsPathsCorrect") - void trimsPathsCorrect(String path, String expected) { - final String result = PathUtil.trimLeadingOrEndingSlash(path); + 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")); + } - Assertions.assertEquals(expected, result); - } + @ParameterizedTest + @MethodSource("provideStringsForTrimsPathsCorrect") + void trimsPathsCorrect(String path, String expected) { + final String result = PathUtil.trimLeadingOrEndingSlash(path); - 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")); - } + 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 index 051fe00eb..45cd161f4 100644 --- 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 @@ -20,25 +20,6 @@ package org.eclipse.tractusx.edc.hashicorpvault; -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; -import lombok.SneakyThrows; -import lombok.experimental.UtilityClass; import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; @@ -59,80 +40,98 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.testcontainers.shaded.org.bouncycastle.openssl.jcajce.JcaPEMWriter; -@UtilityClass -final 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); +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; - static X509Certificate generateCertificate(int validity, String cn) - throws CertificateException, OperatorCreationException, IOException, - NoSuchAlgorithmException { +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); - KeyPair keyPair = generateKeyPair(); + public static X509Certificate generateCertificate(int validity, String cn) + throws CertificateException, OperatorCreationException, IOException, + NoSuchAlgorithmException { - 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)); - } + KeyPair keyPair = generateKeyPair(); - private static KeyPair generateKeyPair() throws NoSuchAlgorithmException { - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", PROVIDER); - keyPairGenerator.initialize(1024, new SecureRandom()); + 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)); + } - return keyPairGenerator.generateKeyPair(); - } + private static KeyPair generateKeyPair() throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", PROVIDER); + keyPairGenerator.initialize(1024, new SecureRandom()); - 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); - } + 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); - } + 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); + } - @SneakyThrows - static String convertToPem(X509Certificate certificate) { - try (ByteArrayOutputStream 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); + 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/observability-api-customization/src/main/java/org/eclipse/tractusx/edc/api/observability/TxObservabilityApiController.java b/edc-extensions/observability-api-customization/src/main/java/org/eclipse/tractusx/edc/api/observability/TxObservabilityApiController.java index 5320161f9..8d5bb33af 100644 --- a/edc-extensions/observability-api-customization/src/main/java/org/eclipse/tractusx/edc/api/observability/TxObservabilityApiController.java +++ b/edc-extensions/observability-api-customization/src/main/java/org/eclipse/tractusx/edc/api/observability/TxObservabilityApiController.java @@ -20,18 +20,17 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; -import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.health.HealthCheckService; import org.eclipse.edc.spi.system.health.HealthStatus; -import org.jetbrains.annotations.NotNull; -@Consumes({ MediaType.APPLICATION_JSON }) -@Produces({ MediaType.APPLICATION_JSON }) +@Consumes({MediaType.APPLICATION_JSON}) +@Produces({MediaType.APPLICATION_JSON}) @Path("/check") public class TxObservabilityApiController implements TxObservabilityApi { private final HealthCheckService healthCheckService; + public TxObservabilityApiController(HealthCheckService provider) { healthCheckService = provider; } diff --git a/edc-extensions/postgresql-migration/build.gradle.kts b/edc-extensions/postgresql-migration/build.gradle.kts index 62c6c7bb3..09d549b82 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.18.0") + implementation("org.flywaydb:flyway-core:9.19.1") } diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java index ab0efc62d..722518e5f 100644 --- a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java @@ -20,8 +20,6 @@ package org.eclipse.tractusx.edc.postgresql.migration; -import java.util.Objects; -import java.util.Properties; import org.eclipse.edc.spi.persistence.EdcPersistenceException; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; @@ -31,80 +29,75 @@ import org.flywaydb.core.api.MigrationVersion; import org.flywaydb.core.api.output.MigrateResult; +import java.util.Objects; +import java.util.Properties; + abstract class AbstractPostgresqlMigrationExtension implements ServiceExtension { - protected abstract String getDataSourceNameConfigurationKey(); + private static final String EDC_DATASOURCE_PREFIX = "edc.datasource"; + private static final String MIGRATION_LOCATION_BASE = + String.format("classpath:%s", AbstractPostgresqlMigrationExtension.class.getPackageName().replace(".", "/")); - protected abstract String getSubsystemName(); + protected abstract String getDataSourceNameConfigurationKey(); - private static final String EDC_DATASOURCE_PREFIX = "edc.datasource"; - private static final String MIGRATION_LOCATION_BASE = - String.format( - "classpath:%s", - AbstractPostgresqlMigrationExtension.class.getPackageName().replace(".", "/")); + protected abstract String getSubsystemName(); - @Override - public void initialize(final ServiceExtensionContext context) { - final String subSystemName = Objects.requireNonNull(getSubsystemName()); + @Override + public void initialize(final ServiceExtensionContext context) { + final String subSystemName = Objects.requireNonNull(getSubsystemName()); - final String dataSourceName = - context.getConfig().getString(getDataSourceNameConfigurationKey(), null); - if (dataSourceName == null) { - return; - } + final String dataSourceName = + context.getConfig().getString(getDataSourceNameConfigurationKey(), null); + if (dataSourceName == null) { + return; + } - boolean enabled = - context - .getConfig() - .getBoolean( - String.format( - "org.eclipse.tractusx.edc.postgresql.migration.%s.enabled", subSystemName), - true); + boolean enabled = context.getConfig() + .getBoolean(String.format("org.eclipse.tractusx.edc.postgresql.migration.%s.enabled", subSystemName), true); - if (!enabled) { - return; - } + if (!enabled) { + return; + } - Config datasourceConfiguration = - context.getConfig(String.join(".", EDC_DATASOURCE_PREFIX, dataSourceName)); + Config datasourceConfiguration = context.getConfig(String.join(".", EDC_DATASOURCE_PREFIX, dataSourceName)); - final String jdbcUrl = Objects.requireNonNull(datasourceConfiguration.getString("url")); - final Properties jdbcProperties = new Properties(); - jdbcProperties.putAll(datasourceConfiguration.getRelativeEntries()); + final String jdbcUrl = Objects.requireNonNull(datasourceConfiguration.getString("url")); + final Properties jdbcProperties = new Properties(); + jdbcProperties.putAll(datasourceConfiguration.getRelativeEntries()); - final DriverManagerConnectionFactory driverManagerConnectionFactory = - new DriverManagerConnectionFactory(jdbcUrl, jdbcProperties); - final ConnectionFactoryDataSource dataSource = - new ConnectionFactoryDataSource(driverManagerConnectionFactory); + final DriverManagerConnectionFactory driverManagerConnectionFactory = + new DriverManagerConnectionFactory(jdbcUrl, jdbcProperties); + final ConnectionFactoryDataSource dataSource = + new ConnectionFactoryDataSource(driverManagerConnectionFactory); - final String schemaHistoryTableName = getSchemaHistoryTableName(subSystemName); - final String migrationsLocation = getMigrationsLocation(); + final String schemaHistoryTableName = getSchemaHistoryTableName(subSystemName); + final String migrationsLocation = getMigrationsLocation(); - final Flyway flyway = - Flyway.configure() - .baselineVersion(MigrationVersion.fromVersion("0.0.0")) - .failOnMissingLocations(true) - .dataSource(dataSource) - .table(schemaHistoryTableName) - .locations(migrationsLocation) - .load(); + final Flyway flyway = + Flyway.configure() + .baselineVersion(MigrationVersion.fromVersion("0.0.0")) + .failOnMissingLocations(true) + .dataSource(dataSource) + .table(schemaHistoryTableName) + .locations(migrationsLocation) + .load(); - flyway.baseline(); + flyway.baseline(); - final MigrateResult migrateResult = flyway.migrate(); + final MigrateResult migrateResult = flyway.migrate(); - if (!migrateResult.success) { - throw new EdcPersistenceException( - String.format( - "Migrating DataSource %s for subsystem %s failed: %s", - dataSourceName, subSystemName, String.join(", ", migrateResult.warnings))); + if (!migrateResult.success) { + throw new EdcPersistenceException( + String.format( + "Migrating DataSource %s for subsystem %s failed: %s", + dataSourceName, subSystemName, String.join(", ", migrateResult.warnings))); + } } - } - private String getMigrationsLocation() { - return String.join("/", MIGRATION_LOCATION_BASE, getSubsystemName()); - } + private String getMigrationsLocation() { + return String.join("/", MIGRATION_LOCATION_BASE, getSubsystemName()); + } - private String getSchemaHistoryTableName(final String subSystemName) { - return String.format("flyway_schema_history_%s", subSystemName); - } + private String getSchemaHistoryTableName(final String subSystemName) { + return String.format("flyway_schema_history_%s", subSystemName); + } } diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java index d44663e2b..ab3d6489a 100644 --- a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java @@ -23,13 +23,13 @@ import org.eclipse.edc.connector.store.sql.assetindex.ConfigurationKeys; public class AssetPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { - private static final String NAME_SUBSYSTEM = "asset"; + private static final String NAME_SUBSYSTEM = "asset"; - protected String getDataSourceNameConfigurationKey() { - return ConfigurationKeys.DATASOURCE_SETTING_NAME; - } + protected String getDataSourceNameConfigurationKey() { + return ConfigurationKeys.DATASOURCE_SETTING_NAME; + } - protected String getSubsystemName() { - return NAME_SUBSYSTEM; - } + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } } diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java index a5b65aafa..168814580 100644 --- a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java @@ -20,17 +20,16 @@ package org.eclipse.tractusx.edc.postgresql.migration; -public class ContractDefinitionPostgresqlMigrationExtension - extends AbstractPostgresqlMigrationExtension { - private static final String NAME_SUBSYSTEM = "contractdefinition"; +public class ContractDefinitionPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "contractdefinition"; - private static final String DATASOURCE_SETTING_NAME = "edc.datasource.contractdefinition.name"; + private static final String DATASOURCE_SETTING_NAME = "edc.datasource.contractdefinition.name"; - protected String getDataSourceNameConfigurationKey() { - return DATASOURCE_SETTING_NAME; - } + protected String getDataSourceNameConfigurationKey() { + return DATASOURCE_SETTING_NAME; + } - protected String getSubsystemName() { - return NAME_SUBSYSTEM; - } + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } } diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java index c2da93732..760883b54 100644 --- a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java @@ -20,17 +20,16 @@ package org.eclipse.tractusx.edc.postgresql.migration; -public class ContractNegotiationPostgresqlMigrationExtension - extends AbstractPostgresqlMigrationExtension { - private static final String NAME_SUBSYSTEM = "contractnegotiation"; +public class ContractNegotiationPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "contractnegotiation"; - private static final String DATASOURCE_SETTING_NAME = "edc.datasource.contractnegotiation.name"; + private static final String DATASOURCE_SETTING_NAME = "edc.datasource.contractnegotiation.name"; - protected String getDataSourceNameConfigurationKey() { - return DATASOURCE_SETTING_NAME; - } + protected String getDataSourceNameConfigurationKey() { + return DATASOURCE_SETTING_NAME; + } - protected String getSubsystemName() { - return NAME_SUBSYSTEM; - } + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } } diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/CpAdapterPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/CpAdapterPostgresqlMigrationExtension.java deleted file mode 100644 index 15c3e710d..000000000 --- a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/CpAdapterPostgresqlMigrationExtension.java +++ /dev/null @@ -1,35 +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 - */ - -package org.eclipse.tractusx.edc.postgresql.migration; - -public class CpAdapterPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { - private static final String NAME_SUBSYSTEM = "cpadapter"; - private static final String DATASOURCE_SETTING_NAME = "edc.datasource.cpadapter.name"; - - @Override - protected String getDataSourceNameConfigurationKey() { - return DATASOURCE_SETTING_NAME; - } - - @Override - protected String getSubsystemName() { - return NAME_SUBSYSTEM; - } -} diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/DriverManagerConnectionFactory.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/DriverManagerConnectionFactory.java index 621bc8774..9a273cccc 100644 --- a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/DriverManagerConnectionFactory.java +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/DriverManagerConnectionFactory.java @@ -20,28 +20,29 @@ package org.eclipse.tractusx.edc.postgresql.migration; +import org.eclipse.edc.spi.persistence.EdcPersistenceException; +import org.eclipse.edc.sql.ConnectionFactory; + import java.sql.Connection; import java.sql.DriverManager; import java.util.Objects; import java.util.Properties; -import org.eclipse.edc.spi.persistence.EdcPersistenceException; -import org.eclipse.edc.sql.ConnectionFactory; class DriverManagerConnectionFactory implements ConnectionFactory { - private final String jdbcUrl; - private final Properties properties; + private final String jdbcUrl; + private final Properties properties; - public DriverManagerConnectionFactory(final String jdbcUrl, final Properties properties) { - this.jdbcUrl = Objects.requireNonNull(jdbcUrl); - this.properties = Objects.requireNonNull(properties); - } + DriverManagerConnectionFactory(final String jdbcUrl, final Properties properties) { + this.jdbcUrl = Objects.requireNonNull(jdbcUrl); + this.properties = Objects.requireNonNull(properties); + } - @Override - public Connection create() { - try { - return DriverManager.getConnection(jdbcUrl, properties); - } catch (Exception exception) { - throw new EdcPersistenceException(exception.getMessage(), exception); + @Override + public Connection create() { + try { + return DriverManager.getConnection(jdbcUrl, properties); + } catch (Exception exception) { + throw new EdcPersistenceException(exception.getMessage(), exception); + } } - } } diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/EdrPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/EdrPostgresqlMigrationExtension.java new file mode 100644 index 000000000..2ebb12bb1 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/EdrPostgresqlMigrationExtension.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.postgresql.migration; + +public class EdrPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "edr"; + + private static final String DATASOURCE_SETTING_NAME = "edc.datasource.edr.name"; + + protected String getDataSourceNameConfigurationKey() { + return DATASOURCE_SETTING_NAME; + } + + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } +} diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java index 88f47d858..02c7a5314 100644 --- a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java @@ -21,15 +21,15 @@ package org.eclipse.tractusx.edc.postgresql.migration; public class PolicyPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { - private static final String NAME_SUBSYSTEM = "policy"; + private static final String NAME_SUBSYSTEM = "policy"; - private static final String DATASOURCE_SETTING_NAME = "edc.datasource.policy.name"; + private static final String DATASOURCE_SETTING_NAME = "edc.datasource.policy.name"; - protected String getDataSourceNameConfigurationKey() { - return DATASOURCE_SETTING_NAME; - } + protected String getDataSourceNameConfigurationKey() { + return DATASOURCE_SETTING_NAME; + } - protected String getSubsystemName() { - return NAME_SUBSYSTEM; - } + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } } diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java index 1379391e7..f9d41b896 100644 --- a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java @@ -20,17 +20,16 @@ package org.eclipse.tractusx.edc.postgresql.migration; -public class TransferProcessPostgresqlMigrationExtension - extends AbstractPostgresqlMigrationExtension { - private static final String NAME_SUBSYSTEM = "transferprocess"; +public class TransferProcessPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "transferprocess"; - private static final String DATASOURCE_SETTING_NAME = "edc.datasource.transferprocess.name"; + private static final String DATASOURCE_SETTING_NAME = "edc.datasource.transferprocess.name"; - protected String getDataSourceNameConfigurationKey() { - return DATASOURCE_SETTING_NAME; - } + protected String getDataSourceNameConfigurationKey() { + return DATASOURCE_SETTING_NAME; + } - protected String getSubsystemName() { - return NAME_SUBSYSTEM; - } + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } } 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 b34529d7e..a641ec766 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 @@ -22,4 +22,4 @@ org.eclipse.tractusx.edc.postgresql.migration.ContractDefinitionPostgresqlMigrat org.eclipse.tractusx.edc.postgresql.migration.ContractNegotiationPostgresqlMigrationExtension org.eclipse.tractusx.edc.postgresql.migration.PolicyPostgresqlMigrationExtension org.eclipse.tractusx.edc.postgresql.migration.TransferProcessPostgresqlMigrationExtension -org.eclipse.tractusx.edc.postgresql.migration.CpAdapterPostgresqlMigrationExtension +org.eclipse.tractusx.edc.postgresql.migration.EdrPostgresqlMigrationExtension diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_6__Alter_ContractDefinition_Rename_selector_expression.sql b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_6__Alter_ContractDefinition_Rename_selector_expression.sql new file mode 100644 index 000000000..3d3cad56b --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_6__Alter_ContractDefinition_Rename_selector_expression.sql @@ -0,0 +1,15 @@ +-- +-- 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 columns +ALTER TABLE edc_contract_definitions RENAME COLUMN selector_expression TO assets_selector; diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/cpadapter/V0_0_7__Init_CpAdapter_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/cpadapter/V0_0_7__Init_CpAdapter_Database_Schema.sql deleted file mode 100644 index 3e64d3c45..000000000 --- a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/cpadapter/V0_0_7__Init_CpAdapter_Database_Schema.sql +++ /dev/null @@ -1,54 +0,0 @@ --- --- Copyright (c) 2022 ZF Friedrichshafen 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: --- ZF Friedrichshafen AG - Initial SQL Query --- - --- Statements are designed for and tested with Postgres only! - - -CREATE TABLE IF NOT EXISTS edc_lease -( - leased_by VARCHAR NOT NULL, - leased_at BIGINT, - lease_duration INTEGER NOT NULL, - lease_id VARCHAR NOT NULL - CONSTRAINT lease_pk - PRIMARY KEY -); - -CREATE TABLE IF NOT EXISTS edc_cpadapter_queue -( - id VARCHAR NOT NULL, - created_at BIGINT NOT NULL, - channel VARCHAR, - message JSON, - invoke_after BIGINT NOT NULL, - lease_id VARCHAR - CONSTRAINT cpadapter_queue_lease_lease_id_fk - REFERENCES edc_lease - ON DELETE SET NULL, - PRIMARY KEY (id) -); - -CREATE UNIQUE INDEX IF NOT EXISTS edc_cpadapter_queue_id_uindex - ON edc_cpadapter_queue (id); - -CREATE TABLE IF NOT EXISTS edc_cpadapter_object_store -( - id VARCHAR NOT NULL, - created_at BIGINT NOT NULL, - type VARCHAR, - object JSON, - PRIMARY KEY (id) -); - - - diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_1__Init_Edr_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_1__Init_Edr_Database_Schema.sql new file mode 100644 index 000000000..0e166045b --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/edr/V0_0_1__Init_Edr_Database_Schema.sql @@ -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 +-- + +-- Statements are designed for and tested with Postgres only! + + +CREATE TABLE IF NOT EXISTS edc_edr_cache +( + transfer_process_id VARCHAR NOT NULL PRIMARY KEY, + agreement_id VARCHAR NOT NULL, + asset_id VARCHAR NOT NULL, + edr_id VARCHAR NOT NULL, + created_at BIGINT NOT NULL, + updated_at BIGINT NOT NULL +); + + +CREATE INDEX IF NOT EXISTS edc_edr_asset_id_index + ON edc_edr_cache (asset_id); + + +CREATE INDEX IF NOT EXISTS edc_edr_agreement_id_index + ON edc_edr_cache (agreement_id); + diff --git a/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_10__Alter_TransferProcess_Rename_transfer_process_column.sql b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_10__Alter_TransferProcess_Rename_transfer_process_column.sql new file mode 100644 index 000000000..80301988a --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_10__Alter_TransferProcess_Rename_transfer_process_column.sql @@ -0,0 +1,15 @@ +-- +-- 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 RENAME COLUMN transferprocess_properties TO private_properties; diff --git a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResource.java b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResource.java index b0d3534ea..29b5328ed 100644 --- a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResource.java +++ b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResource.java @@ -28,17 +28,17 @@ @JsonDeserialize(builder = AdditionalHeadersProvisionedResource.Builder.class) class AdditionalHeadersProvisionedResource extends ProvisionedContentResource { - @JsonPOJOBuilder(withPrefix = "") - public static class Builder - extends ProvisionedContentResource.Builder { + @JsonPOJOBuilder(withPrefix = "") + public static class Builder + extends ProvisionedContentResource.Builder { - private Builder() { - super(new AdditionalHeadersProvisionedResource()); - } + private Builder() { + super(new AdditionalHeadersProvisionedResource()); + } - @JsonCreator - public static Builder newInstance() { - return new Builder(); + @JsonCreator + public static Builder newInstance() { + return new Builder(); + } } - } } diff --git a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisioner.java b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisioner.java index b5b4085e5..f4c7ccf67 100644 --- a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisioner.java +++ b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisioner.java @@ -20,8 +20,6 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; import org.eclipse.edc.connector.transfer.spi.provision.Provisioner; import org.eclipse.edc.connector.transfer.spi.types.DeprovisionedResource; import org.eclipse.edc.connector.transfer.spi.types.ProvisionResponse; @@ -31,48 +29,50 @@ import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.types.domain.HttpDataAddress; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + public class AdditionalHeadersProvisioner - implements Provisioner< + implements Provisioner< AdditionalHeadersResourceDefinition, AdditionalHeadersProvisionedResource> { - @Override - public boolean canProvision(ResourceDefinition resourceDefinition) { - return resourceDefinition instanceof AdditionalHeadersResourceDefinition; - } + @Override + public boolean canProvision(ResourceDefinition resourceDefinition) { + return resourceDefinition instanceof AdditionalHeadersResourceDefinition; + } - @Override - public boolean canDeprovision(ProvisionedResource provisionedResource) { - return false; // nothing to deprovision - } + @Override + public boolean canDeprovision(ProvisionedResource provisionedResource) { + return false; // nothing to deprovision + } - @Override - public CompletableFuture> provision( - AdditionalHeadersResourceDefinition resourceDefinition, Policy policy) { + @Override + public CompletableFuture> provision(AdditionalHeadersResourceDefinition resourceDefinition, Policy policy) { - var address = - HttpDataAddress.Builder.newInstance() - .copyFrom(resourceDefinition.getDataAddress()) - .addAdditionalHeader("Edc-Contract-Agreement-Id", resourceDefinition.getContractId()) - .build(); + var address = + HttpDataAddress.Builder.newInstance() + .copyFrom(resourceDefinition.getDataAddress()) + .addAdditionalHeader("Edc-Contract-Agreement-Id", resourceDefinition.getContractId()) + .build(); - var provisioned = - AdditionalHeadersProvisionedResource.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .resourceDefinitionId(resourceDefinition.getId()) - .transferProcessId(resourceDefinition.getTransferProcessId()) - .dataAddress(address) - .resourceName(UUID.randomUUID().toString()) - .hasToken(false) - .build(); + var provisioned = + AdditionalHeadersProvisionedResource.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .resourceDefinitionId(resourceDefinition.getId()) + .transferProcessId(resourceDefinition.getTransferProcessId()) + .dataAddress(address) + .resourceName(UUID.randomUUID().toString()) + .hasToken(false) + .build(); - var response = ProvisionResponse.Builder.newInstance().resource(provisioned).build(); - var result = StatusResult.success(response); - return CompletableFuture.completedFuture(result); - } + var response = ProvisionResponse.Builder.newInstance().resource(provisioned).build(); + var result = StatusResult.success(response); + return CompletableFuture.completedFuture(result); + } - @Override - public CompletableFuture> deprovision( - AdditionalHeadersProvisionedResource additionalHeadersProvisionedResource, Policy policy) { - return null; // nothing to deprovision - } + @Override + public CompletableFuture> deprovision( + AdditionalHeadersProvisionedResource additionalHeadersProvisionedResource, Policy policy) { + return null; // nothing to deprovision + } } diff --git a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinition.java b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinition.java index e609922be..ecd3f0437 100644 --- a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinition.java +++ b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinition.java @@ -31,43 +31,43 @@ @JsonTypeName("dataspaceconnector:additionalheadersresourcedefinition") class AdditionalHeadersResourceDefinition extends ResourceDefinition { - private String contractId; - private DataAddress dataAddress; + private String contractId; + private DataAddress dataAddress; - @Override - public Builder toBuilder() { - return initializeBuilder(new Builder()); - } + @Override + public Builder toBuilder() { + return initializeBuilder(new Builder()); + } - public DataAddress getDataAddress() { - return dataAddress; - } + public DataAddress getDataAddress() { + return dataAddress; + } - public String getContractId() { - return contractId; - } + public String getContractId() { + return contractId; + } - @JsonPOJOBuilder(withPrefix = "") - public static class Builder - extends ResourceDefinition.Builder { + @JsonPOJOBuilder(withPrefix = "") + public static class Builder + extends ResourceDefinition.Builder { - protected Builder() { - super(new AdditionalHeadersResourceDefinition()); - } + protected Builder() { + super(new AdditionalHeadersResourceDefinition()); + } - @JsonCreator - public static Builder newInstance() { - return new Builder(); - } + @JsonCreator + public static Builder newInstance() { + return new Builder(); + } - public Builder contractId(String contractId) { - this.resourceDefinition.contractId = contractId; - return this; - } + public Builder contractId(String contractId) { + this.resourceDefinition.contractId = contractId; + return this; + } - public Builder dataAddress(DataAddress dataAddress) { - this.resourceDefinition.dataAddress = dataAddress; - return this; + public Builder dataAddress(DataAddress dataAddress) { + this.resourceDefinition.dataAddress = dataAddress; + return this; + } } - } } diff --git a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGenerator.java b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGenerator.java index 1d85bdd94..62e0d6ce9 100644 --- a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGenerator.java +++ b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGenerator.java @@ -20,7 +20,6 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; -import java.util.UUID; import org.eclipse.edc.connector.transfer.spi.provision.ProviderResourceDefinitionGenerator; import org.eclipse.edc.connector.transfer.spi.types.DataRequest; import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; @@ -28,20 +27,22 @@ import org.eclipse.edc.spi.types.domain.DataAddress; import org.jetbrains.annotations.Nullable; +import java.util.UUID; + class AdditionalHeadersResourceDefinitionGenerator implements ProviderResourceDefinitionGenerator { - @Override - public boolean canGenerate(DataRequest dataRequest, DataAddress dataAddress, Policy policy) { - return "HttpData".equals(dataAddress.getType()); - } + @Override + public boolean canGenerate(DataRequest dataRequest, DataAddress dataAddress, Policy policy) { + return "HttpData".equals(dataAddress.getType()); + } - @Override - public @Nullable ResourceDefinition generate( - DataRequest dataRequest, DataAddress dataAddress, Policy policy) { - return AdditionalHeadersResourceDefinition.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .dataAddress(dataAddress) - .contractId(dataRequest.getContractId()) - .build(); - } + @Override + public @Nullable ResourceDefinition generate( + DataRequest dataRequest, DataAddress dataAddress, Policy policy) { + return AdditionalHeadersResourceDefinition.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .dataAddress(dataAddress) + .contractId(dataRequest.getContractId()) + .build(); + } } diff --git a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtension.java b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtension.java index ef58cd4a2..9860fdf46 100644 --- a/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtension.java +++ b/edc-extensions/provision-additional-headers/src/main/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtension.java @@ -29,17 +29,19 @@ public class ProvisionAdditionalHeadersExtension implements ServiceExtension { - @Inject private ResourceManifestGenerator resourceManifestGenerator; + @Inject + private ResourceManifestGenerator resourceManifestGenerator; - @Inject private ProvisionManager provisionManager; + @Inject + private ProvisionManager provisionManager; - @Inject private TypeManager typeManager; + @Inject + private TypeManager typeManager; - @Override - public void initialize(ServiceExtensionContext context) { - typeManager.registerTypes( - AdditionalHeadersResourceDefinition.class, AdditionalHeadersProvisionedResource.class); - resourceManifestGenerator.registerGenerator(new AdditionalHeadersResourceDefinitionGenerator()); - provisionManager.register(new AdditionalHeadersProvisioner()); - } + @Override + public void initialize(ServiceExtensionContext context) { + typeManager.registerTypes(AdditionalHeadersResourceDefinition.class, AdditionalHeadersProvisionedResource.class); + resourceManifestGenerator.registerGenerator(new AdditionalHeadersResourceDefinitionGenerator()); + provisionManager.register(new AdditionalHeadersProvisioner()); + } } diff --git a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResourceTest.java b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResourceTest.java index dc133d060..2079393d8 100644 --- a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResourceTest.java +++ b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionedResourceTest.java @@ -20,31 +20,32 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; - -import java.util.UUID; import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.spi.types.domain.DataAddress; import org.junit.jupiter.api.Test; +import java.util.UUID; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + class AdditionalHeadersProvisionedResourceTest { - @Test - void serdes() { - var typeManager = new TypeManager(); - var resource = - AdditionalHeadersProvisionedResource.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .resourceDefinitionId(UUID.randomUUID().toString()) - .transferProcessId(UUID.randomUUID().toString()) - .hasToken(false) - .resourceName("name") - .dataAddress(DataAddress.Builder.newInstance().type("type").build()) - .build(); - - var json = typeManager.writeValueAsString(resource); - var deserialized = typeManager.readValue(json, AdditionalHeadersProvisionedResource.class); - - assertThat(deserialized).usingRecursiveComparison().isEqualTo(resource); - } + @Test + void serdes() { + var typeManager = new TypeManager(); + var resource = + AdditionalHeadersProvisionedResource.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .resourceDefinitionId(UUID.randomUUID().toString()) + .transferProcessId(UUID.randomUUID().toString()) + .hasToken(false) + .resourceName("name") + .dataAddress(DataAddress.Builder.newInstance().type("type").build()) + .build(); + + var json = typeManager.writeValueAsString(resource); + var deserialized = typeManager.readValue(json, AdditionalHeadersProvisionedResource.class); + + assertThat(deserialized).usingRecursiveComparison().isEqualTo(resource); + } } diff --git a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionerTest.java b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionerTest.java index 45769a935..64c4dbc99 100644 --- a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionerTest.java +++ b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersProvisionerTest.java @@ -20,13 +20,6 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.assertj.core.api.InstanceOfAssertFactories.map; -import static org.assertj.core.api.InstanceOfAssertFactories.type; -import static org.mockito.Mockito.mock; - -import java.util.UUID; import org.eclipse.edc.connector.transfer.spi.types.ProvisionResponse; import org.eclipse.edc.connector.transfer.spi.types.ProvisionedDataAddressResource; import org.eclipse.edc.connector.transfer.spi.types.ProvisionedResource; @@ -36,46 +29,54 @@ import org.eclipse.edc.spi.types.domain.HttpDataAddress; import org.junit.jupiter.api.Test; +import java.util.UUID; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.map; +import static org.assertj.core.api.InstanceOfAssertFactories.type; +import static org.mockito.Mockito.mock; + class AdditionalHeadersProvisionerTest { - private final AdditionalHeadersProvisioner provisioner = new AdditionalHeadersProvisioner(); + private final AdditionalHeadersProvisioner provisioner = new AdditionalHeadersProvisioner(); - @Test - void canProvisionAdditionalHeadersResourceDefinition() { - assertThat(provisioner.canProvision(mock(AdditionalHeadersResourceDefinition.class))).isTrue(); - assertThat(provisioner.canProvision(mock(ResourceDefinition.class))).isFalse(); - } + @Test + void canProvisionAdditionalHeadersResourceDefinition() { + assertThat(provisioner.canProvision(mock(AdditionalHeadersResourceDefinition.class))).isTrue(); + assertThat(provisioner.canProvision(mock(ResourceDefinition.class))).isFalse(); + } - @Test - void cannotDeprovisionAdditionalHeadersResourceDefinition() { - assertThat(provisioner.canDeprovision(mock(AdditionalHeadersProvisionedResource.class))) - .isFalse(); - assertThat(provisioner.canDeprovision(mock(ProvisionedResource.class))).isFalse(); - } + @Test + void cannotDeprovisionAdditionalHeadersResourceDefinition() { + assertThat(provisioner.canDeprovision(mock(AdditionalHeadersProvisionedResource.class))) + .isFalse(); + assertThat(provisioner.canDeprovision(mock(ProvisionedResource.class))).isFalse(); + } - @Test - void shouldAddContractIdAdditionalHeader() { - var address = HttpDataAddress.Builder.newInstance().baseUrl("http://any").build(); - var resourceDefinition = - AdditionalHeadersResourceDefinition.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .transferProcessId(UUID.randomUUID().toString()) - .contractId("contractId") - .dataAddress(address) - .build(); + @Test + void shouldAddContractIdAdditionalHeader() { + var address = HttpDataAddress.Builder.newInstance().baseUrl("http://any").build(); + var resourceDefinition = + AdditionalHeadersResourceDefinition.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .transferProcessId(UUID.randomUUID().toString()) + .contractId("contractId") + .dataAddress(address) + .build(); - var result = provisioner.provision(resourceDefinition, Policy.Builder.newInstance().build()); + var result = provisioner.provision(resourceDefinition, Policy.Builder.newInstance().build()); - assertThat(result) - .succeedsWithin(5, SECONDS) - .matches(StatusResult::succeeded) - .extracting(StatusResult::getContent) - .extracting(ProvisionResponse::getResource) - .asInstanceOf(type(AdditionalHeadersProvisionedResource.class)) - .extracting(ProvisionedDataAddressResource::getDataAddress) - .extracting(a -> HttpDataAddress.Builder.newInstance().copyFrom(a).build()) - .extracting(HttpDataAddress::getAdditionalHeaders) - .asInstanceOf(map(String.class, String.class)) - .containsEntry("Edc-Contract-Agreement-Id", "contractId"); - } + assertThat(result) + .succeedsWithin(5, SECONDS) + .matches(StatusResult::succeeded) + .extracting(StatusResult::getContent) + .extracting(ProvisionResponse::getResource) + .asInstanceOf(type(AdditionalHeadersProvisionedResource.class)) + .extracting(ProvisionedDataAddressResource::getDataAddress) + .extracting(a -> HttpDataAddress.Builder.newInstance().copyFrom(a).build()) + .extracting(HttpDataAddress::getAdditionalHeaders) + .asInstanceOf(map(String.class, String.class)) + .containsEntry("Edc-Contract-Agreement-Id", "contractId"); + } } diff --git a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGeneratorTest.java b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGeneratorTest.java index 12b9a61e6..7f7e66675 100644 --- a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGeneratorTest.java +++ b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionGeneratorTest.java @@ -20,68 +20,69 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.assertj.core.api.InstanceOfAssertFactories.type; - -import java.util.UUID; import org.eclipse.edc.connector.transfer.spi.types.DataRequest; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.types.domain.DataAddress; import org.eclipse.edc.spi.types.domain.HttpDataAddress; import org.junit.jupiter.api.Test; +import java.util.UUID; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.type; + class AdditionalHeadersResourceDefinitionGeneratorTest { - private final AdditionalHeadersResourceDefinitionGenerator generator = - new AdditionalHeadersResourceDefinitionGenerator(); + private final AdditionalHeadersResourceDefinitionGenerator generator = + new AdditionalHeadersResourceDefinitionGenerator(); - @Test - void canGenerate_shouldReturnFalseForNotHttpDataAddresses() { - var dataAddress = DataAddress.Builder.newInstance().type("any").build(); - var dataRequest = - DataRequest.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .dataDestination(dataAddress) - .build(); - var build = Policy.Builder.newInstance().build(); + @Test + void canGenerate_shouldReturnFalseForNotHttpDataAddresses() { + var dataAddress = DataAddress.Builder.newInstance().type("any").build(); + var dataRequest = + DataRequest.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .dataDestination(dataAddress) + .build(); + var build = Policy.Builder.newInstance().build(); - var result = generator.canGenerate(dataRequest, dataAddress, build); + var result = generator.canGenerate(dataRequest, dataAddress, build); - assertThat(result).isFalse(); - } + assertThat(result).isFalse(); + } - @Test - void canGenerate_shouldReturnTrueForHttpDataAddresses() { - var dataAddress = DataAddress.Builder.newInstance().type("HttpData").build(); - var dataRequest = - DataRequest.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .dataDestination(dataAddress) - .build(); - var build = Policy.Builder.newInstance().build(); + @Test + void canGenerate_shouldReturnTrueForHttpDataAddresses() { + var dataAddress = DataAddress.Builder.newInstance().type("HttpData").build(); + var dataRequest = + DataRequest.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .dataDestination(dataAddress) + .build(); + var build = Policy.Builder.newInstance().build(); - var result = generator.canGenerate(dataRequest, dataAddress, build); + var result = generator.canGenerate(dataRequest, dataAddress, build); - assertThat(result).isTrue(); - } + assertThat(result).isTrue(); + } - @Test - void shouldCreateResourceDefinitionWithDataAddress() { - var dataAddress = HttpDataAddress.Builder.newInstance().baseUrl("http://any").build(); - var dataRequest = - DataRequest.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .dataDestination(dataAddress) - .build(); - var build = Policy.Builder.newInstance().build(); + @Test + void shouldCreateResourceDefinitionWithDataAddress() { + var dataAddress = HttpDataAddress.Builder.newInstance().baseUrl("http://any").build(); + var dataRequest = + DataRequest.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .dataDestination(dataAddress) + .build(); + var build = Policy.Builder.newInstance().build(); - var result = generator.generate(dataRequest, dataAddress, build); + var result = generator.generate(dataRequest, dataAddress, build); - assertThat(result) - .asInstanceOf(type(AdditionalHeadersResourceDefinition.class)) - .extracting(AdditionalHeadersResourceDefinition::getDataAddress) - .extracting(address -> HttpDataAddress.Builder.newInstance().copyFrom(address).build()) - .extracting(HttpDataAddress::getBaseUrl) - .isEqualTo("http://any"); - } + assertThat(result) + .asInstanceOf(type(AdditionalHeadersResourceDefinition.class)) + .extracting(AdditionalHeadersResourceDefinition::getDataAddress) + .extracting(address -> HttpDataAddress.Builder.newInstance().copyFrom(address).build()) + .extracting(HttpDataAddress::getBaseUrl) + .isEqualTo("http://any"); + } } diff --git a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionTest.java b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionTest.java index 4c69e297d..11a5173a4 100644 --- a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionTest.java +++ b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/AdditionalHeadersResourceDefinitionTest.java @@ -20,29 +20,30 @@ package org.eclipse.tractusx.edc.provision.additionalheaders; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; - -import java.util.UUID; import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.spi.types.domain.DataAddress; import org.junit.jupiter.api.Test; +import java.util.UUID; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + class AdditionalHeadersResourceDefinitionTest { - @Test - void serdes() { - var typeManager = new TypeManager(); - var definition = - AdditionalHeadersResourceDefinition.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .transferProcessId(UUID.randomUUID().toString()) - .dataAddress(DataAddress.Builder.newInstance().type("type").build()) - .contractId(UUID.randomUUID().toString()) - .build(); - - var json = typeManager.writeValueAsString(definition); - var deserialized = typeManager.readValue(json, AdditionalHeadersResourceDefinition.class); - - assertThat(deserialized).usingRecursiveComparison().isEqualTo(definition); - } + @Test + void serdes() { + var typeManager = new TypeManager(); + var definition = + AdditionalHeadersResourceDefinition.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .transferProcessId(UUID.randomUUID().toString()) + .dataAddress(DataAddress.Builder.newInstance().type("type").build()) + .contractId(UUID.randomUUID().toString()) + .build(); + + var json = typeManager.writeValueAsString(definition); + var deserialized = typeManager.readValue(json, AdditionalHeadersResourceDefinition.class); + + assertThat(deserialized).usingRecursiveComparison().isEqualTo(definition); + } } diff --git a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtensionTest.java b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtensionTest.java index 1e3f72754..723398a3e 100644 --- a/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtensionTest.java +++ b/edc-extensions/provision-additional-headers/src/test/java/org/eclipse/tractusx/edc/provision/additionalheaders/ProvisionAdditionalHeadersExtensionTest.java @@ -43,6 +43,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import java.time.Duration; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -61,8 +62,8 @@ class ProvisionAdditionalHeadersExtensionTest { private final DataFlowController dataFlowController = mock(DataFlowController.class); private final RemoteMessageDispatcherRegistry dispatcherRegistry = mock(RemoteMessageDispatcherRegistry.class); - private ContractNegotiationStore contractNegotiationStore = mock(ContractNegotiationStore.class); - private ContractValidationService contractValidationService = mock(ContractValidationService.class); + private final ContractNegotiationStore contractNegotiationStore = mock(ContractNegotiationStore.class); + private final ContractValidationService contractValidationService = mock(ContractValidationService.class); @BeforeEach void setUp(EdcExtension extension) { @@ -101,7 +102,7 @@ void shouldPutContractIdAsHeaderInDataAddress( .id("id") .protocol("protocol") .assetId("assetId") - .contractId("aContractId") + .contractId("1:assetId:aContractId") .dataDestination(DataAddress.Builder.newInstance().type("HttpProxy").build()) .callbackAddress("callbackAddress") .build(); @@ -110,10 +111,7 @@ void shouldPutContractIdAsHeaderInDataAddress( assertThat(result).matches(ServiceResult::succeeded); - await().untilAsserted( - () -> { - verify(dataFlowController) - .initiateFlow(any(), argThat(it -> "aContractId".equals(it.getProperty("header:Edc-Contract-Agreement-Id"))), any()); - }); + await().atMost(Duration.ofSeconds(5)) + .untilAsserted(() -> verify(dataFlowController).initiateFlow(any(), argThat(it -> "1:assetId:aContractId".equals(it.getProperty("header:Edc-Contract-Agreement-Id"))), any())); } } diff --git a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientConfig.java b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientConfig.java index bf5da3a2f..dbfb0fc98 100644 --- a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientConfig.java +++ b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientConfig.java @@ -14,33 +14,117 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; +import org.apache.sshd.sftp.client.SftpClient; +import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; +import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; + import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; import java.util.List; -import lombok.Builder; -import lombok.Getter; -import org.apache.sshd.sftp.client.SftpClient; -import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; -import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; -@Builder -@Getter public class SftpClientConfig { - private SftpUser sftpUser; - private SftpLocation sftpLocation; - @Builder.Default private int bufferSize = 4096; - @Builder.Default private boolean hostVerification = true; + private int bufferSize = 4096; + private boolean hostVerification = true; + private Path knownHostFile = Paths.get(System.getenv("HOME"), ".ssh/known_hosts"); + private int connectionTimeoutSeconds = 10; + private Collection writeOpenModes = + List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Write); + private Collection readOpenModes = List.of(SftpClient.OpenMode.Read); + private SftpUser sftpUser; + private SftpLocation sftpLocation; + + public SftpUser getSftpUser() { + return sftpUser; + } + + public SftpLocation getSftpLocation() { + return sftpLocation; + } + + public int getBufferSize() { + return bufferSize; + } + + public boolean isHostVerification() { + return hostVerification; + } + + public Path getKnownHostFile() { + return knownHostFile; + } + + public int getConnectionTimeoutSeconds() { + return connectionTimeoutSeconds; + } + + public Collection getWriteOpenModes() { + return writeOpenModes; + } + + public Collection getReadOpenModes() { + return readOpenModes; + } + + + public static class Builder { + + private final SftpClientConfig config; + + private Builder() { + config = new SftpClientConfig(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public static Builder sftpClientConfig() { + return new Builder(); + } + + public Builder sftpUser(SftpUser sftpUser) { + this.config.sftpUser = sftpUser; + return this; + } + + public Builder sftpLocation(SftpLocation sftpLocation) { + this.config.sftpLocation = sftpLocation; + return this; + } + + public Builder bufferSize(int bufferSize) { + this.config.bufferSize = bufferSize; + return this; + } + + public Builder hostVerification(boolean hostVerification) { + this.config.hostVerification = hostVerification; + return this; + } + + public Builder knownHostFile(Path knownHostFile) { + this.config.knownHostFile = knownHostFile; + return this; + } - @Builder.Default - private Path knownHostFile = Paths.get(System.getenv("HOME"), ".ssh/known_hosts"); + public Builder connectionTimeoutSeconds(int connectionTimeoutSeconds) { + this.config.connectionTimeoutSeconds = connectionTimeoutSeconds; + return this; + } - @Builder.Default private int connectionTimeoutSeconds = 10; + public Builder writeOpenModes(Collection writeOpenModes) { + this.config.writeOpenModes = writeOpenModes; + return this; + } - @Builder.Default - private Collection writeOpenModes = - List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Write); + public Builder readOpenModes(Collection readOpenModes) { + this.config.readOpenModes = readOpenModes; + return this; + } - @Builder.Default - private Collection readOpenModes = List.of(SftpClient.OpenMode.Read); + public SftpClientConfig build() { + return config; + } + } } diff --git a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientExtension.java b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientExtension.java index 9143b3583..766300c80 100644 --- a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientExtension.java +++ b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientExtension.java @@ -23,16 +23,17 @@ @Provides(SftpClientWrapper.class) public class SftpClientExtension implements ServiceExtension { - @Inject PipelineService pipelineService; + @Inject + PipelineService pipelineService; - @Override - public void initialize(ServiceExtensionContext context) { - SftpDataSinkFactory sftpDataSinkFactory = new SftpDataSinkFactory(); - SftpDataSourceFactory sftpDataSourceFactory = new SftpDataSourceFactory(); - SftpClientWrapperFactory sftpClientWrapperFactory = new SftpClientWrapperFactoryImpl(); + @Override + public void initialize(ServiceExtensionContext context) { + SftpDataSinkFactory sftpDataSinkFactory = new SftpDataSinkFactory(); + SftpDataSourceFactory sftpDataSourceFactory = new SftpDataSourceFactory(); + SftpClientWrapperFactory sftpClientWrapperFactory = new SftpClientWrapperFactoryImpl(); - pipelineService.registerFactory(sftpDataSinkFactory); - pipelineService.registerFactory(sftpDataSourceFactory); - context.registerService(SftpClientWrapperFactory.class, sftpClientWrapperFactory); - } + pipelineService.registerFactory(sftpDataSinkFactory); + pipelineService.registerFactory(sftpDataSourceFactory); + context.registerService(SftpClientWrapperFactory.class, sftpClientWrapperFactory); + } } diff --git a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapper.java b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapper.java index c082106f2..6d938f2e1 100644 --- a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapper.java +++ b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapper.java @@ -18,7 +18,7 @@ import java.io.InputStream; public interface SftpClientWrapper { - void uploadFile(InputStream inputStream) throws IOException; + void uploadFile(InputStream inputStream) throws IOException; - InputStream downloadFile() throws IOException; + InputStream downloadFile() throws IOException; } diff --git a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperFactory.java b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperFactory.java index ba60d9c04..8d2d03783 100644 --- a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperFactory.java +++ b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperFactory.java @@ -15,5 +15,5 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; public interface SftpClientWrapperFactory { - SftpClientWrapper getSftpClientWrapper(SftpClientConfig config); + SftpClientWrapper getSftpClientWrapper(SftpClientConfig config); } diff --git a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperFactoryImpl.java b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperFactoryImpl.java index a300ea4d0..019ec9d67 100644 --- a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperFactoryImpl.java +++ b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperFactoryImpl.java @@ -16,8 +16,8 @@ public class SftpClientWrapperFactoryImpl implements SftpClientWrapperFactory { - @Override - public SftpClientWrapper getSftpClientWrapper(SftpClientConfig config) { - return new SftpClientWrapperImpl(config); - } + @Override + public SftpClientWrapper getSftpClientWrapper(SftpClientConfig config) { + return new SftpClientWrapperImpl(config); + } } diff --git a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperImpl.java b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperImpl.java index cad0ea6e5..8bacbf1b0 100644 --- a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperImpl.java +++ b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperImpl.java @@ -14,13 +14,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.time.Duration; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; import org.apache.sshd.client.ClientBuilder; import org.apache.sshd.client.SshClient; import org.apache.sshd.client.auth.password.PasswordIdentityProvider; @@ -34,127 +27,123 @@ import org.eclipse.tractusx.edc.transferprocess.sftp.common.EdcSftpException; import org.jetbrains.annotations.NotNull; +import java.io.IOException; +import java.io.InputStream; +import java.time.Duration; + public class SftpClientWrapperImpl implements SftpClientWrapper { - @Getter private final SftpClientConfig config; - private final SftpClient sftpClient; - - public SftpClientWrapperImpl(SftpClientConfig config) { - this.config = config; - try { - this.sftpClient = getSftpClient(config); - } catch (IOException e) { - throw new EdcSftpException("Unable to create SftpClient.", e); + private final SftpClientConfig config; + private final SftpClient sftpClient; + + public SftpClientWrapperImpl(SftpClientConfig config) { + this.config = config; + try { + this.sftpClient = getSftpClient(config); + } catch (IOException e) { + throw new EdcSftpException("Unable to create SftpClient.", e); + } } - } - - public SftpClientWrapperImpl(SftpClientConfig config, SftpClient sftpClient) { - this.config = config; - this.sftpClient = sftpClient; - } - - @Override - public void uploadFile(@NonNull final InputStream inputStream) throws IOException { - try (final OutputStream outputStream = - sftpClient.write( - config.getSftpLocation().getPath(), - config.getBufferSize(), - config.getWriteOpenModes())) { - inputStream.transferTo(outputStream); + + public SftpClientWrapperImpl(SftpClientConfig config, SftpClient sftpClient) { + this.config = config; + this.sftpClient = sftpClient; } - } - - @Override - public InputStream downloadFile() throws IOException { - final InputStream delegateInputStream; - try { - delegateInputStream = - sftpClient.read( - config.getSftpLocation().getPath(), - config.getBufferSize(), - config.getReadOpenModes()); - } catch (final IOException e) { - sftpClient.close(); - throw new EdcSftpException( - String.format("Unable to download file at %s", config.getSftpLocation().toString()), e); + + static SftpClient getSftpClient(SftpClientConfig config) throws IOException { + var session = getSshClientSession(config); + var factory = SftpClientFactory.instance(); + var sftpClient = factory.createSftpClient(session); + return sftpClient.singleSessionInstance(); } - return new SftpInputStreamWrapper(sftpClient, delegateInputStream); - } - - static SftpClient getSftpClient(SftpClientConfig config) throws IOException { - final ClientSession session = getSshClientSession(config); - final SftpClientFactory factory = SftpClientFactory.instance(); - final SftpClient sftpClient = factory.createSftpClient(session); - return sftpClient.singleSessionInstance(); - } - - private static ClientSession getSshClientSession(SftpClientConfig config) throws IOException { - final SshClient sshClient = getSshClient(config); - sshClient.start(); - final ClientSession session = - sshClient - .connect( - config.getSftpUser().getName(), - config.getSftpLocation().getHost(), - config.getSftpLocation().getPort()) - .verify() - .getSession(); - session.auth().await(Duration.ofSeconds(config.getConnectionTimeoutSeconds())); - - return session; - } - - private static SshClient getSshClient(SftpClientConfig config) { - final SshClient sshClient = ClientBuilder.builder().build(); - - if (config.getSftpUser().getKeyPair() != null) { - sshClient.setKeyIdentityProvider( - KeyIdentityProvider.wrapKeyPairs(config.getSftpUser().getKeyPair())); - } else if (config.getSftpUser().getPassword() != null) { - sshClient.setPasswordIdentityProvider( - PasswordIdentityProvider.wrapPasswords(config.getSftpUser().getPassword())); - } else { - sshClient.setPasswordIdentityProvider(PasswordIdentityProvider.EMPTY_PASSWORDS_PROVIDER); + private static ClientSession getSshClientSession(SftpClientConfig config) throws IOException { + var sshClient = getSshClient(config); + sshClient.start(); + var session = sshClient.connect(config.getSftpUser().getName(), config.getSftpLocation().getHost(), config.getSftpLocation().getPort()).verify().getSession(); + session.auth().await(Duration.ofSeconds(config.getConnectionTimeoutSeconds())); + + return session; } - if (config.isHostVerification()) { - final ServerKeyVerifier keyVerifier = - new KnownHostsServerKeyVerifier( - RejectAllServerKeyVerifier.INSTANCE, config.getKnownHostFile()); - sshClient.setServerKeyVerifier(keyVerifier); + private static SshClient getSshClient(SftpClientConfig config) { + final var sshClient = ClientBuilder.builder().build(); + + if (config.getSftpUser().getKeyPair() != null) { + sshClient.setKeyIdentityProvider(KeyIdentityProvider.wrapKeyPairs(config.getSftpUser().getKeyPair())); + } else if (config.getSftpUser().getPassword() != null) { + sshClient.setPasswordIdentityProvider(PasswordIdentityProvider.wrapPasswords(config.getSftpUser().getPassword())); + } else { + sshClient.setPasswordIdentityProvider(PasswordIdentityProvider.EMPTY_PASSWORDS_PROVIDER); + } + + if (config.isHostVerification()) { + final ServerKeyVerifier keyVerifier = new KnownHostsServerKeyVerifier(RejectAllServerKeyVerifier.INSTANCE, config.getKnownHostFile()); + sshClient.setServerKeyVerifier(keyVerifier); + } + + return sshClient; } - return sshClient; - } + public SftpClientConfig getConfig() { + return config; + } - @RequiredArgsConstructor - private static class SftpInputStreamWrapper extends InputStream { - @NonNull private final SftpClient sftpClient; - @NonNull private final InputStream delegateInputStream; + public SftpClient getSftpClient() { + return sftpClient; + } @Override - public int read() throws IOException { - return delegateInputStream.read(); + public void uploadFile(final InputStream inputStream) throws IOException { + try (var outputStream = sftpClient.write(config.getSftpLocation().getPath(), config.getBufferSize(), config.getWriteOpenModes())) { + inputStream.transferTo(outputStream); + } } @Override - public int read(byte @NotNull [] b, int off, int len) throws IOException { - return delegateInputStream.read(b, off, len); + public InputStream downloadFile() throws IOException { + final InputStream delegateInputStream; + try { + delegateInputStream = sftpClient.read(config.getSftpLocation().getPath(), config.getBufferSize(), config.getReadOpenModes()); + } catch (final IOException e) { + sftpClient.close(); + throw new EdcSftpException(String.format("Unable to download file at %s", config.getSftpLocation().toString()), e); + } + + return new SftpInputStreamWrapper(sftpClient, delegateInputStream); } - @Override - public void close() { - try { - delegateInputStream.close(); - } catch (IOException ignored) { - // Ignored. The exception should only be thrown of the stream is already closed. - } - - try { - sftpClient.close(); - } catch (IOException ignored) { - // Ignored. The exception should only be thrown of the client is already closed. - } + private static class SftpInputStreamWrapper extends InputStream { + private final SftpClient sftpClient; + private final InputStream delegateInputStream; + + private SftpInputStreamWrapper(SftpClient sftpClient, InputStream delegateInputStream) { + this.sftpClient = sftpClient; + this.delegateInputStream = delegateInputStream; + } + + @Override + public int read() throws IOException { + return delegateInputStream.read(); + } + + @Override + public int read(byte @NotNull [] b, int off, int len) throws IOException { + return delegateInputStream.read(b, off, len); + } + + @Override + public void close() { + try { + delegateInputStream.close(); + } catch (IOException ignored) { + // Ignored. The exception should only be thrown of the stream is already closed. + } + + try { + sftpClient.close(); + } catch (IOException ignored) { + // Ignored. The exception should only be thrown of the client is already closed. + } + } } - } } diff --git a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSink.java b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSink.java index f4a68f06f..4d372f96f 100644 --- a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSink.java +++ b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSink.java @@ -14,8 +14,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import lombok.Builder; -import lombok.NonNull; 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.ParallelSink; @@ -23,11 +21,14 @@ import java.io.IOException; import java.util.List; -@Builder public class SftpDataSink extends ParallelSink { - @NonNull private final SftpClientWrapper sftpClientWrapper; + public SftpDataSink(SftpClientWrapper sftpClientWrapper) { + this.sftpClientWrapper = sftpClientWrapper; + } + + @Override protected StreamResult transferParts(List parts) { for (DataSource.Part part : parts) { 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 4edcb9dec..d06d5ca54 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 @@ -14,8 +14,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import java.util.List; -import lombok.RequiredArgsConstructor; import org.apache.sshd.sftp.client.SftpClient; import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSink; import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSinkFactory; @@ -25,45 +23,46 @@ import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; import org.jetbrains.annotations.NotNull; -@RequiredArgsConstructor -public class SftpDataSinkFactory implements DataSinkFactory { - @Override - public boolean canHandle(DataFlowRequest request) { - try { - SftpDataAddress.fromDataAddress(request.getDestinationDataAddress()); - return true; - } catch (EdcSftpException e) { - return false; - } - } +import java.util.List; - @Override - public @NotNull Result validate(DataFlowRequest request) { - if (!canHandle(request)) { - return Result.failure(String.format("Invalid DataFlowRequest: %s", request.getId())); +public class SftpDataSinkFactory implements DataSinkFactory { + @Override + public boolean canHandle(DataFlowRequest request) { + try { + SftpDataAddress.fromDataAddress(request.getDestinationDataAddress()); + return true; + } catch (EdcSftpException e) { + return false; + } } - return VALID; - } + @Override + public @NotNull Result validate(DataFlowRequest request) { + if (!canHandle(request)) { + return Result.failure(String.format("Invalid DataFlowRequest: %s", request.getId())); + } - @Override - public DataSink createSink(DataFlowRequest request) { - if (!canHandle(request)) { - return null; + return VALID; } - SftpDataAddress destination = - SftpDataAddress.fromDataAddress(request.getDestinationDataAddress()); + @Override + public DataSink createSink(DataFlowRequest request) { + if (!canHandle(request)) { + return null; + } + + SftpDataAddress destination = + SftpDataAddress.fromDataAddress(request.getDestinationDataAddress()); - SftpClientConfig sftpClientConfig = - SftpClientConfig.builder() - .writeOpenModes(List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Append)) - .sftpUser(destination.getSftpUser()) - .sftpLocation(destination.getSftpLocation()) - .build(); + SftpClientConfig sftpClientConfig = + SftpClientConfig.Builder.newInstance() + .writeOpenModes(List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Append)) + .sftpUser(destination.getSftpUser()) + .sftpLocation(destination.getSftpLocation()) + .build(); - SftpClientWrapper sftpClientWrapper = new SftpClientWrapperImpl(sftpClientConfig); + SftpClientWrapper sftpClientWrapper = new SftpClientWrapperImpl(sftpClientConfig); - return new SftpDataSink(sftpClientWrapper); - } + return new SftpDataSink(sftpClientWrapper); + } } diff --git a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSource.java b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSource.java index 9020fb905..d670bce04 100644 --- a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSource.java +++ b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSource.java @@ -14,21 +14,23 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import lombok.Builder; -import lombok.NonNull; import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSource; import org.eclipse.edc.connector.dataplane.spi.pipeline.StreamResult; import java.util.stream.Stream; -@Builder public class SftpDataSource implements DataSource { - @NonNull private final SftpClientWrapper sftpClientWrapper; + public SftpDataSource(SftpClientWrapper sftpClientWrapper) { + this.sftpClientWrapper = sftpClientWrapper; + } + + @Override public StreamResult> openPartStream() { Part sftpPart = new SftpPart(sftpClientWrapper); return StreamResult.success(Stream.of(sftpPart)); } + } 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 076afc72d..eca6e5302 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 @@ -14,7 +14,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import lombok.RequiredArgsConstructor; import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSource; import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSourceFactory; import org.eclipse.edc.spi.result.Result; @@ -23,42 +22,41 @@ import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; import org.jetbrains.annotations.NotNull; -@RequiredArgsConstructor public class SftpDataSourceFactory implements DataSourceFactory { - @Override - public boolean canHandle(DataFlowRequest request) { - try { - SftpDataAddress.fromDataAddress(request.getSourceDataAddress()); - return true; - } catch (EdcSftpException e) { - return false; + @Override + public boolean canHandle(DataFlowRequest request) { + try { + SftpDataAddress.fromDataAddress(request.getSourceDataAddress()); + return true; + } catch (EdcSftpException e) { + return false; + } } - } - @Override - public @NotNull Result validate(DataFlowRequest request) { - if (!canHandle(request)) { - return Result.failure(String.format("Invalid DataFlowRequest: %s", request.getId())); - } - - return VALID; - } + @Override + public @NotNull Result validate(DataFlowRequest request) { + if (!canHandle(request)) { + return Result.failure(String.format("Invalid DataFlowRequest: %s", request.getId())); + } - @Override - public DataSource createSource(DataFlowRequest request) { - if (!canHandle(request)) { - return null; + return VALID; } - SftpDataAddress source = SftpDataAddress.fromDataAddress(request.getSourceDataAddress()); + @Override + public DataSource createSource(DataFlowRequest request) { + if (!canHandle(request)) { + return null; + } - SftpClientConfig sftpClientConfig = - SftpClientConfig.builder() - .sftpUser(source.getSftpUser()) - .sftpLocation(source.getSftpLocation()) - .build(); + SftpDataAddress source = SftpDataAddress.fromDataAddress(request.getSourceDataAddress()); - SftpClientWrapper sftpClientWrapper = new SftpClientWrapperImpl(sftpClientConfig); - return new SftpDataSource(sftpClientWrapper); - } + SftpClientConfig sftpClientConfig = + SftpClientConfig.Builder.newInstance() + .sftpUser(source.getSftpUser()) + .sftpLocation(source.getSftpLocation()) + .build(); + + SftpClientWrapper sftpClientWrapper = new SftpClientWrapperImpl(sftpClientConfig); + return new SftpDataSource(sftpClientWrapper); + } } diff --git a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpPart.java b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpPart.java index 2b4e9e7e3..923f56810 100644 --- a/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpPart.java +++ b/edc-extensions/transferprocess-sftp-client/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpPart.java @@ -14,24 +14,30 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import java.io.InputStream; -import lombok.Builder; -import lombok.NonNull; -import lombok.SneakyThrows; import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSource; +import org.eclipse.tractusx.edc.transferprocess.sftp.common.EdcSftpException; + +import java.io.IOException; +import java.io.InputStream; -@Builder public class SftpPart implements DataSource.Part { - @NonNull private final SftpClientWrapper sftpClientWrapper; + private final SftpClientWrapper sftpClientWrapper; + + public SftpPart(SftpClientWrapper sftpClientWrapper) { + this.sftpClientWrapper = sftpClientWrapper; + } - @Override - public String name() { - return ((SftpClientWrapperImpl) sftpClientWrapper).getConfig().getSftpLocation().getPath(); - } + @Override + public String name() { + return ((SftpClientWrapperImpl) sftpClientWrapper).getConfig().getSftpLocation().getPath(); + } - @Override - @SneakyThrows - public InputStream openStream() { - return sftpClientWrapper.downloadFile(); - } + @Override + public InputStream openStream() { + try { + return sftpClientWrapper.downloadFile(); + } catch (IOException e) { + throw new EdcSftpException(e); + } + } } diff --git a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/AbstractSftpClientWrapperIT.java b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/AbstractSftpClientWrapperIntegrationTest.java similarity index 57% rename from edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/AbstractSftpClientWrapperIT.java rename to edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/AbstractSftpClientWrapperIntegrationTest.java index d1d1c1ce5..830cbae21 100644 --- a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/AbstractSftpClientWrapperIT.java +++ b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/AbstractSftpClientWrapperIntegrationTest.java @@ -20,8 +20,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import lombok.NoArgsConstructor; -import lombok.SneakyThrows; import org.bouncycastle.crypto.params.RSAKeyParameters; import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; @@ -41,13 +39,13 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.io.OutputStreamWriter; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.PosixFilePermission; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPublicKey; import java.util.Base64; import java.util.Comparator; @@ -59,26 +57,26 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static org.testcontainers.shaded.org.awaitility.Awaitility.await; -abstract class AbstractSftpClientWrapperIT { +abstract class AbstractSftpClientWrapperIntegrationTest { static final String DOCKER_IMAGE_NAME = "atmoz/sftp:alpine-3.6"; - static final String sftpPathPrefix = "transfer"; + static final String SFTP_PATH_PREFIX = "transfer"; static final Map DOCKER_ENV = - Map.of("SFTP_USERS", String.format("user:password:::%s", sftpPathPrefix)); - static final Path dockerVolumeDirectory; - static final Path remotePasswordUploadDirectory; - static final Path remotePasswordDownloadDirectory; - static final Path remoteKeypairUploadDirectory; - static final Path remoteKeypairDownloadDirectory; - static final Path localUploadAndGeneratorDirectory; - static final Path keyDirectory; - static final Path publicKeyPath; - static final KeyPair keyPair; + Map.of("SFTP_USERS", String.format("user:password:::%s", SFTP_PATH_PREFIX)); + static final Path DOCKER_VOLUME_DIRECTORY; + static final Path REMOTE_PASSWORD_UPLOAD_DIRECTORY; + static final Path REMOTE_PASSWORD_DOWNLOAD_DIRECTORY; + static final Path REMOTE_KEYPAIR_UPLOAD_DIRECTORY; + static final Path REMOTE_KEYPAIR_DOWNLOAD_DIRECTORY; + static final Path LOCAL_UPLOAD_AND_GENERATOR_DIRECTORY; + static final Path KEY_DIRECTORY; + static final Path PUBLIC_KEY_PATH; + static final KeyPair KEY_PAIR; @Container @ClassRule - private static final GenericContainer sftpContainer; + private static final GenericContainer SFTP_CONTAINER; static { - keyPair = generateKeyPair(); + KEY_PAIR = generateKeyPair(); try { Set fullPermission = new HashSet(); @@ -92,23 +90,23 @@ abstract class AbstractSftpClientWrapperIT { fullPermission.add(PosixFilePermission.OTHERS_READ); fullPermission.add(PosixFilePermission.OTHERS_WRITE); - dockerVolumeDirectory = Files.createTempDirectory(SftpClientWrapperIT.class.getName()); - localUploadAndGeneratorDirectory = - Files.createTempDirectory(SftpClientWrapperIT.class.getName()); - remotePasswordUploadDirectory = - Files.createDirectory(dockerVolumeDirectory.resolve("passwordUpload")); - remotePasswordDownloadDirectory = - Files.createDirectory(dockerVolumeDirectory.resolve("passwordDownload")); - remoteKeypairUploadDirectory = - Files.createDirectory(dockerVolumeDirectory.resolve("keypairUpload")); - remoteKeypairDownloadDirectory = - Files.createDirectory(dockerVolumeDirectory.resolve("keypairDownload")); - keyDirectory = Files.createTempDirectory(SftpClientWrapperIT.class.getName()); - publicKeyPath = keyDirectory.resolve("public"); - - try (final OutputStreamWriter fileWriter = - new OutputStreamWriter(new FileOutputStream(publicKeyPath.toString()))) { - final RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); + DOCKER_VOLUME_DIRECTORY = Files.createTempDirectory(SftpClientWrapperIntegrationTest.class.getName()); + LOCAL_UPLOAD_AND_GENERATOR_DIRECTORY = + Files.createTempDirectory(SftpClientWrapperIntegrationTest.class.getName()); + REMOTE_PASSWORD_UPLOAD_DIRECTORY = + Files.createDirectory(DOCKER_VOLUME_DIRECTORY.resolve("passwordUpload")); + REMOTE_PASSWORD_DOWNLOAD_DIRECTORY = + Files.createDirectory(DOCKER_VOLUME_DIRECTORY.resolve("passwordDownload")); + REMOTE_KEYPAIR_UPLOAD_DIRECTORY = + Files.createDirectory(DOCKER_VOLUME_DIRECTORY.resolve("keypairUpload")); + REMOTE_KEYPAIR_DOWNLOAD_DIRECTORY = + Files.createDirectory(DOCKER_VOLUME_DIRECTORY.resolve("keypairDownload")); + KEY_DIRECTORY = Files.createTempDirectory(SftpClientWrapperIntegrationTest.class.getName()); + PUBLIC_KEY_PATH = KEY_DIRECTORY.resolve("public"); + + try (var fileWriter = + new OutputStreamWriter(new FileOutputStream(PUBLIC_KEY_PATH.toString()))) { + final RSAPublicKey publicKey = (RSAPublicKey) KEY_PAIR.getPublic(); final RSAKeyParameters publicKeyParameters = new RSAKeyParameters(false, publicKey.getModulus(), publicKey.getPublicExponent()); byte[] encodedKey = OpenSSHPublicKeyUtil.encodePublicKey(publicKeyParameters); @@ -117,91 +115,93 @@ abstract class AbstractSftpClientWrapperIT { fileWriter.write(authKeysEntry); } - Files.setPosixFilePermissions(dockerVolumeDirectory, fullPermission); - Files.setPosixFilePermissions(remotePasswordUploadDirectory, fullPermission); - Files.setPosixFilePermissions(remotePasswordDownloadDirectory, fullPermission); - Files.setPosixFilePermissions(remoteKeypairUploadDirectory, fullPermission); - Files.setPosixFilePermissions(remoteKeypairDownloadDirectory, fullPermission); - Files.setPosixFilePermissions(keyDirectory, fullPermission); + Files.setPosixFilePermissions(DOCKER_VOLUME_DIRECTORY, fullPermission); + Files.setPosixFilePermissions(REMOTE_PASSWORD_UPLOAD_DIRECTORY, fullPermission); + Files.setPosixFilePermissions(REMOTE_PASSWORD_DOWNLOAD_DIRECTORY, fullPermission); + Files.setPosixFilePermissions(REMOTE_KEYPAIR_UPLOAD_DIRECTORY, fullPermission); + Files.setPosixFilePermissions(REMOTE_KEYPAIR_DOWNLOAD_DIRECTORY, fullPermission); + Files.setPosixFilePermissions(KEY_DIRECTORY, fullPermission); } catch (IOException e) { throw new RuntimeException(); } - sftpContainer = + SFTP_CONTAINER = new GenericContainer<>(DockerImageName.parse(DOCKER_IMAGE_NAME)) .withEnv(DOCKER_ENV) .withExposedPorts(22) .waitingFor(Wait.forListeningPort()) .withFileSystemBind( - dockerVolumeDirectory.toAbsolutePath().toString(), - String.format("/home/user/%s", sftpPathPrefix)) - .withFileSystemBind(keyDirectory.toAbsolutePath().toString(), "/home/user/keys"); - sftpContainer.start(); + DOCKER_VOLUME_DIRECTORY.toAbsolutePath().toString(), + String.format("/home/user/%s", SFTP_PATH_PREFIX)) + .withFileSystemBind(KEY_DIRECTORY.toAbsolutePath().toString(), "/home/user/keys"); + SFTP_CONTAINER.start(); - await().atMost(10, SECONDS).until(sftpContainer::isRunning); + await().atMost(10, SECONDS).until(SFTP_CONTAINER::isRunning); try { - sftpContainer.execInContainer("mkdir", "-p", "/home/user/.ssh"); - sftpContainer.execInContainer("chmod", "700", "/home/user/.ssh"); - sftpContainer.execInContainer("chown", "user", "/home/user/.ssh/"); - sftpContainer.execInContainer( + SFTP_CONTAINER.execInContainer("mkdir", "-p", "/home/user/.ssh"); + SFTP_CONTAINER.execInContainer("chmod", "700", "/home/user/.ssh"); + SFTP_CONTAINER.execInContainer("chown", "user", "/home/user/.ssh/"); + SFTP_CONTAINER.execInContainer( "cp", "-f", "/home/user/keys/public", "/home/user/.ssh/authorized_keys"); - sftpContainer.execInContainer("chown", "user", "/home/user/.ssh/authorized_keys"); - sftpContainer.execInContainer("chmod", "600", "/home/user/.ssh/authorized_keys"); + SFTP_CONTAINER.execInContainer("chown", "user", "/home/user/.ssh/authorized_keys"); + SFTP_CONTAINER.execInContainer("chmod", "600", "/home/user/.ssh/authorized_keys"); } catch (IOException | InterruptedException e) { throw new RuntimeException(); } } @AfterAll - @SneakyThrows - static void tearDown() { - if (Files.exists(dockerVolumeDirectory)) { - Files.walk(dockerVolumeDirectory) + static void tearDown() throws IOException { + if (Files.exists(DOCKER_VOLUME_DIRECTORY)) { + Files.walk(DOCKER_VOLUME_DIRECTORY) .sorted(Comparator.reverseOrder()) .map(Path::toFile) .forEach(File::delete); } - if (Files.exists(localUploadAndGeneratorDirectory)) { - Files.walk(localUploadAndGeneratorDirectory) + if (Files.exists(LOCAL_UPLOAD_AND_GENERATOR_DIRECTORY)) { + Files.walk(LOCAL_UPLOAD_AND_GENERATOR_DIRECTORY) .sorted(Comparator.reverseOrder()) .map(Path::toFile) .forEach(File::delete); } - if (Files.exists(keyDirectory)) { - Files.walk(keyDirectory) + if (Files.exists(KEY_DIRECTORY)) { + Files.walk(KEY_DIRECTORY) .sorted(Comparator.reverseOrder()) .map(Path::toFile) .forEach(File::delete); } } - @SneakyThrows private static KeyPair generateKeyPair() { - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(2048); - return keyPairGenerator.generateKeyPair(); + try { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + return keyPairGenerator.generateKeyPair(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } } protected SftpUser getPasswordUser() { - return SftpUser.builder().name("user").password("password").build(); + return SftpUser.Builder.newInstance().name("user").password("password").build(); } protected SftpUser getKeyPairUser() { - return SftpUser.builder().name("user").keyPair(keyPair).build(); + return SftpUser.Builder.newInstance().name("user").keyPair(KEY_PAIR).build(); } protected SftpLocation getSftpLocation(String path) { - return SftpLocation.builder() + return SftpLocation.Builder.newInstance() .host("127.0.0.1") - .port(sftpContainer.getFirstMappedPort()) + .port(SFTP_CONTAINER.getFirstMappedPort()) .path(path) .build(); } protected SftpClientWrapper getSftpClient(SftpLocation location, SftpUser sftpUser) { SftpClientConfig config = - SftpClientConfig.builder() + SftpClientConfig.Builder.newInstance() .sftpLocation(location) .sftpUser(sftpUser) .hostVerification(false) @@ -209,34 +209,36 @@ protected SftpClientWrapper getSftpClient(SftpLocation location, SftpUser sftpUs return new SftpClientWrapperImpl(config); } - @NoArgsConstructor protected static class FilesProvider implements ArgumentsProvider { private static final int BUFFER_SIZE = 4 * 1024 * 1024; @Override public Stream provideArguments(ExtensionContext context) { return Stream.of( - Arguments.of(get1KBFile()), Arguments.of(get1MBFile()), Arguments.of(get2MBFile())); + Arguments.of(get1KbFile()), Arguments.of(get1MbFile()), Arguments.of(get2MbFile())); } - public File get1KBFile() { + public File get1KbFile() { return generateFile(1024); } - public File get1MBFile() { + public File get1MbFile() { return generateFile(1024 * 1024); } - public File get2MBFile() { + public File get2MbFile() { return generateFile(2 * 1024 * 1024); } - @SneakyThrows private File generateFile(final int byteSize) { - Path path = localUploadAndGeneratorDirectory.resolve(String.format("%s.bin", byteSize)); + Path path = LOCAL_UPLOAD_AND_GENERATOR_DIRECTORY.resolve(String.format("%s.bin", byteSize)); if (!Files.exists(path)) { - Files.createFile(path); - try (final OutputStream outputStream = Files.newOutputStream(path)) { + try { + Files.createFile(path); + } catch (IOException e) { + throw new RuntimeException(e); + } + try (var outputStream = Files.newOutputStream(path)) { int bufferSize; int remaining = byteSize; do { @@ -245,6 +247,8 @@ private File generateFile(final int byteSize) { IOUtils.write(chunk, outputStream); remaining = remaining - bufferSize; } while (remaining > 0); + } catch (IOException e) { + throw new RuntimeException(e); } } return path.toFile(); diff --git a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperIT.java b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperIT.java deleted file mode 100644 index 368db5bf8..000000000 --- a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperIT.java +++ /dev/null @@ -1,153 +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 Test - * - */ - -package org.eclipse.tractusx.edc.transferprocess.sftp.client; - -import java.io.File; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import lombok.Cleanup; -import lombok.SneakyThrows; -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; -import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ArgumentsSource; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.shaded.org.apache.commons.io.IOUtils; - -@Disabled("Does not work") -@Testcontainers -@ExtendWith(EdcExtension.class) -class SftpClientWrapperIT extends AbstractSftpClientWrapperIT { - - @ParameterizedTest - @SneakyThrows - @ArgumentsSource(FilesProvider.class) - void uploadFileWithPassword(File file) { - final SftpUser sftpUser = getPasswordUser(); - final SftpLocation sftpLocation = - getSftpLocation( - String.format( - "%s/%s/%s", - sftpPathPrefix, - remotePasswordUploadDirectory.getFileName().toString(), - file.getName())); - - @Cleanup final InputStream fileStream = Files.newInputStream(file.toPath()); - - getSftpClient(sftpLocation, sftpUser).uploadFile(fileStream); - - final Path uploadedFilePath = remotePasswordUploadDirectory.resolve(file.getName()); - Assertions.assertTrue(Files.exists(uploadedFilePath)); - - @Cleanup final InputStream source = Files.newInputStream(file.toPath()); - @Cleanup final InputStream target = Files.newInputStream(uploadedFilePath); - - Assertions.assertTrue( - IOUtils.contentEquals(source, target), - String.format( - "File %s should have same content as file %s", file.toPath(), uploadedFilePath)); - } - - @ParameterizedTest - @SneakyThrows - @ArgumentsSource(FilesProvider.class) - void uploadFileWithKeyPair(File file) { - final SftpUser sftpUser = getKeyPairUser(); - final SftpLocation sftpLocation = - getSftpLocation( - String.format( - "%s/%s/%s", - sftpPathPrefix, - remoteKeypairUploadDirectory.getFileName().toString(), - file.getName())); - - @Cleanup final InputStream fileStream = Files.newInputStream(file.toPath()); - - getSftpClient(sftpLocation, sftpUser).uploadFile(fileStream); - - final Path uploadedFilePath = remoteKeypairUploadDirectory.resolve(file.getName()); - Assertions.assertTrue(Files.exists(uploadedFilePath)); - - @Cleanup final InputStream source = Files.newInputStream(file.toPath()); - @Cleanup final InputStream target = Files.newInputStream(uploadedFilePath); - - Assertions.assertTrue( - IOUtils.contentEquals(source, target), - String.format( - "File %s should have same content as file %s", file.toPath(), uploadedFilePath)); - } - - @ParameterizedTest - @SneakyThrows - @ArgumentsSource(FilesProvider.class) - void downloadFileWithPassword(File file) { - final SftpUser sftpUser = getPasswordUser(); - final SftpLocation sftpLocation = - getSftpLocation( - String.format( - "%s/%s/%s", - sftpPathPrefix, - remotePasswordDownloadDirectory.getFileName().toString(), - file.getName())); - - @Cleanup final InputStream fileToUpload = Files.newInputStream(file.toPath()); - Files.copy( - fileToUpload, - remotePasswordDownloadDirectory.resolve(file.getName()), - StandardCopyOption.REPLACE_EXISTING); - - @Cleanup final InputStream source = Files.newInputStream(file.toPath()); - @Cleanup final InputStream target = getSftpClient(sftpLocation, sftpUser).downloadFile(); - - Assertions.assertTrue( - IOUtils.contentEquals(source, target), - String.format( - "File %s should have same content as file %s", file.toPath(), sftpLocation.getPath())); - } - - @ParameterizedTest - @SneakyThrows - @ArgumentsSource(FilesProvider.class) - void downloadFileWithKeyPair(File file) { - final SftpUser sftpUser = getKeyPairUser(); - final SftpLocation sftpLocation = - getSftpLocation( - String.format( - "%s/%s/%s", - sftpPathPrefix, - remoteKeypairDownloadDirectory.getFileName().toString(), - file.getName())); - - @Cleanup final InputStream fileToUpload = Files.newInputStream(file.toPath()); - Files.copy( - fileToUpload, - remoteKeypairDownloadDirectory.resolve(file.getName()), - StandardCopyOption.REPLACE_EXISTING); - - @Cleanup final InputStream source = Files.newInputStream(file.toPath()); - @Cleanup final InputStream target = getSftpClient(sftpLocation, sftpUser).downloadFile(); - - Assertions.assertTrue( - IOUtils.contentEquals(source, target), - String.format( - "File %s should have same content as file %s", file.toPath(), sftpLocation.getPath())); - } -} diff --git a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperIntegrationTest.java b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperIntegrationTest.java new file mode 100644 index 000000000..ec3b5c027 --- /dev/null +++ b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperIntegrationTest.java @@ -0,0 +1,119 @@ +/* + * 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 Test + * + */ + +package org.eclipse.tractusx.edc.transferprocess.sftp.client; + +import org.eclipse.edc.junit.extensions.EdcExtension; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.shaded.org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; + +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Disabled("Does not work") +@Testcontainers +@ExtendWith(EdcExtension.class) +class SftpClientWrapperIntegrationTest extends AbstractSftpClientWrapperIntegrationTest { + + @ParameterizedTest + @ArgumentsSource(FilesProvider.class) + void uploadFileWithPassword(File file) throws IOException { + var sftpUser = getPasswordUser(); + var sftpLocation = getSftpLocation(format("%s/%s/%s", SFTP_PATH_PREFIX, REMOTE_PASSWORD_UPLOAD_DIRECTORY.getFileName().toString(), file.getName())); + + var fileStream = Files.newInputStream(file.toPath()); + + getSftpClient(sftpLocation, sftpUser).uploadFile(fileStream); + + var uploadedFilePath = REMOTE_PASSWORD_UPLOAD_DIRECTORY.resolve(file.getName()); + assertTrue(Files.exists(uploadedFilePath)); + + var source = Files.newInputStream(file.toPath()); + var target = Files.newInputStream(uploadedFilePath); + + assertTrue(IOUtils.contentEquals(source, target), + format("File %s should have same content as file %s", file.toPath(), uploadedFilePath)); + } + + @ParameterizedTest + @ArgumentsSource(FilesProvider.class) + void uploadFileWithKeyPair(File file) throws IOException { + var sftpUser = getKeyPairUser(); + var sftpLocation = + getSftpLocation( + format( + "%s/%s/%s", + SFTP_PATH_PREFIX, + REMOTE_KEYPAIR_UPLOAD_DIRECTORY.getFileName().toString(), + file.getName())); + + var fileStream = Files.newInputStream(file.toPath()); + + getSftpClient(sftpLocation, sftpUser).uploadFile(fileStream); + + var uploadedFilePath = REMOTE_KEYPAIR_UPLOAD_DIRECTORY.resolve(file.getName()); + assertTrue(Files.exists(uploadedFilePath)); + + var source = Files.newInputStream(file.toPath()); + var target = Files.newInputStream(uploadedFilePath); + + assertTrue(IOUtils.contentEquals(source, target), + format("File %s should have same content as file %s", file.toPath(), uploadedFilePath)); + } + + @ParameterizedTest + @ArgumentsSource(FilesProvider.class) + void downloadFileWithPassword(File file) throws IOException { + var sftpUser = getPasswordUser(); + var sftpLocation = getSftpLocation( + format("%s/%s/%s", SFTP_PATH_PREFIX, + REMOTE_PASSWORD_DOWNLOAD_DIRECTORY.getFileName().toString(), file.getName())); + + var fileToUpload = Files.newInputStream(file.toPath()); + Files.copy(fileToUpload, + REMOTE_PASSWORD_DOWNLOAD_DIRECTORY, + StandardCopyOption.REPLACE_EXISTING); + + var source = Files.newInputStream(file.toPath()); + var target = getSftpClient(sftpLocation, sftpUser).downloadFile(); + + assertTrue(IOUtils.contentEquals(source, target), + format("File %s should have same content as file %s", file.toPath(), sftpLocation.getPath())); + } + + @ParameterizedTest + @ArgumentsSource(FilesProvider.class) + void downloadFileWithKeyPair(File file) throws IOException { + var sftpUser = getKeyPairUser(); + var sftpLocation = getSftpLocation(format("%s/%s/%s", SFTP_PATH_PREFIX, REMOTE_KEYPAIR_DOWNLOAD_DIRECTORY.getFileName().toString(), file.getName())); + + var fileToUpload = Files.newInputStream(file.toPath()); + Files.copy(fileToUpload, REMOTE_KEYPAIR_DOWNLOAD_DIRECTORY.resolve(file.getName()), StandardCopyOption.REPLACE_EXISTING); + + var source = Files.newInputStream(file.toPath()); + var target = getSftpClient(sftpLocation, sftpUser).downloadFile(); + + assertTrue(IOUtils.contentEquals(source, target), + format("File %s should have same content as file %s", file.toPath(), sftpLocation.getPath())); + } +} diff --git a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperTest.java b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperTest.java index 7fcb0196c..9827dfc00 100644 --- a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperTest.java +++ b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpClientWrapperTest.java @@ -20,11 +20,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.util.List; -import lombok.SneakyThrows; import org.apache.sshd.sftp.client.SftpClient; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; @@ -32,54 +27,60 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + class SftpClientWrapperTest { - @Test - @SneakyThrows - void uploadFile() { - SftpUser userMock = Mockito.mock(SftpUser.class); - SftpLocation locationMock = Mockito.mock(SftpLocation.class); - SftpClientConfig sftpClientConfig = - SftpClientConfig.builder().sftpUser(userMock).sftpLocation(locationMock).build(); - SftpClient sftpClientMock = Mockito.mock(SftpClient.class); - SftpClientWrapperImpl sftpClientWrapper = - Mockito.spy(new SftpClientWrapperImpl(sftpClientConfig, sftpClientMock)); - byte[] content = new byte[] {0, 1, 2}; - InputStream inputStream = new ByteArrayInputStream(content); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + @Test + void uploadFile() throws IOException { + SftpUser userMock = Mockito.mock(SftpUser.class); + SftpLocation locationMock = Mockito.mock(SftpLocation.class); + SftpClientConfig sftpClientConfig = + SftpClientConfig.Builder.newInstance().sftpUser(userMock).sftpLocation(locationMock).build(); + SftpClient sftpClientMock = Mockito.mock(SftpClient.class); + SftpClientWrapperImpl sftpClientWrapper = + Mockito.spy(new SftpClientWrapperImpl(sftpClientConfig, sftpClientMock)); + byte[] content = new byte[]{0, 1, 2}; + InputStream inputStream = new ByteArrayInputStream(content); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - Mockito.when(locationMock.getPath()).thenReturn("path"); - Mockito.when( - sftpClientMock.write(Mockito.anyString(), Mockito.anyInt(), Mockito.anyCollection())) - .thenReturn(outputStream); - sftpClientWrapper.uploadFile(inputStream); + Mockito.when(locationMock.getPath()).thenReturn("path"); + Mockito.when( + sftpClientMock.write(Mockito.anyString(), Mockito.anyInt(), Mockito.anyCollection())) + .thenReturn(outputStream); + sftpClientWrapper.uploadFile(inputStream); - Assertions.assertArrayEquals(content, outputStream.toByteArray()); - Mockito.verify(sftpClientMock, Mockito.times(1)) - .write("path", 4096, List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Write)); - } + Assertions.assertArrayEquals(content, outputStream.toByteArray()); + Mockito.verify(sftpClientMock, Mockito.times(1)) + .write("path", 4096, List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Write)); + } - @Test - @SneakyThrows - void downloadFile() { - SftpUser userMock = Mockito.mock(SftpUser.class); - SftpLocation locationMock = Mockito.mock(SftpLocation.class); - SftpClientConfig sftpClientConfig = - SftpClientConfig.builder().sftpUser(userMock).sftpLocation(locationMock).build(); - SftpClient sftpClientMock = Mockito.mock(SftpClient.class); - SftpClientWrapperImpl sftpClientWrapper = - Mockito.spy(new SftpClientWrapperImpl(sftpClientConfig, sftpClientMock)); - byte[] content = new byte[] {0, 1, 2}; - InputStream inputStream = new ByteArrayInputStream(content); + @Test + void downloadFile() throws IOException { + SftpUser userMock = Mockito.mock(SftpUser.class); + SftpLocation locationMock = Mockito.mock(SftpLocation.class); + SftpClientConfig sftpClientConfig = + SftpClientConfig.Builder.newInstance().sftpUser(userMock).sftpLocation(locationMock).build(); + SftpClient sftpClientMock = Mockito.mock(SftpClient.class); + SftpClientWrapperImpl sftpClientWrapper = + Mockito.spy(new SftpClientWrapperImpl(sftpClientConfig, sftpClientMock)); + byte[] content = new byte[]{0, 1, 2}; + InputStream inputStream = new ByteArrayInputStream(content); - Mockito.when(locationMock.getPath()).thenReturn("path"); - Mockito.when( - sftpClientMock.read(Mockito.anyString(), Mockito.anyInt(), Mockito.anyCollection())) - .thenReturn(inputStream); + Mockito.when(locationMock.getPath()).thenReturn("path"); + Mockito.when( + sftpClientMock.read(Mockito.anyString(), Mockito.anyInt(), Mockito.anyCollection())) + .thenReturn(inputStream); - try (InputStream resultStream = sftpClientWrapper.downloadFile()) { - Assertions.assertArrayEquals(content, resultStream.readAllBytes()); - Mockito.verify(sftpClientMock, Mockito.times(1)) - .read("path", 4096, List.of(SftpClient.OpenMode.Read)); + try (InputStream resultStream = sftpClientWrapper.downloadFile()) { + Assertions.assertArrayEquals(content, resultStream.readAllBytes()); + Mockito.verify(sftpClientMock, Mockito.times(1)) + .read("path", 4096, List.of(SftpClient.OpenMode.Read)); + } catch (IOException e) { + throw new RuntimeException(e); + } } - } } 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 835c764cc..e254353a5 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 @@ -20,9 +20,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import java.util.List; -import java.util.Map; -import lombok.SneakyThrows; import org.apache.sshd.sftp.client.SftpClient; import org.eclipse.edc.spi.types.domain.DataAddress; import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; @@ -34,91 +31,92 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; +import java.util.List; +import java.util.Map; + class SftpDataSinkFactoryTest { - @Test - void validate__valid() { - SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); - SftpUser sftpUser = SftpUser.builder().name("name").build(); - SftpLocation sftpLocation = SftpLocation.builder().host("host").port(22).path("path").build(); - - SftpDataAddress sftpDataAddress = - SftpDataAddress.builder().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getDestinationDataAddress()).thenReturn(sftpDataAddress); - - Assertions.assertTrue(dataSinkFactory.validate(request).succeeded()); - } - - @Test - void validate__invalidDataAddressType() { - SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); - DataAddress dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getDestinationDataAddress()).thenReturn(dataAddress); - - Assertions.assertTrue(dataSinkFactory.validate(request).failed()); - } - - @Test - void validate__invalidDataAddressParameters() { - SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); - final Map properties = - Map.of( - "type", "sftp", - "locationHost", "localhost", - "locationPort", "notANumber", - "locationPath", "path", - "userName", "name", - "userPassword", "password"); - - final DataAddress dataAddress = - DataAddress.Builder.newInstance().properties(properties).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getDestinationDataAddress()).thenReturn(dataAddress); - - Assertions.assertTrue(dataSinkFactory.validate(request).failed()); - } - - @Test - @SneakyThrows - void createSink__successful() { - SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); - SftpUser sftpUser = SftpUser.builder().name("name").build(); - SftpLocation sftpLocation = - SftpLocation.builder().host("127.0.0.1").port(22).path("path").build(); - SftpClientConfig sftpClientConfig = - SftpClientConfig.builder() - .writeOpenModes(List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Append)) - .sftpUser(sftpUser) - .sftpLocation(sftpLocation) - .build(); - - SftpDataAddress sftpDataAddress = - SftpDataAddress.builder().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getDestinationDataAddress()).thenReturn(sftpDataAddress); - - try (MockedStatic staticWrapper = - Mockito.mockStatic(SftpClientWrapperImpl.class)) { - staticWrapper - .when(() -> SftpClientWrapperImpl.getSftpClient(sftpClientConfig)) - .thenReturn(Mockito.mock(SftpClient.class)); - Assertions.assertNotNull(dataSinkFactory.createSink(request)); - - staticWrapper.verify( - () -> SftpClientWrapperImpl.getSftpClient(Mockito.any()), Mockito.times(1)); + @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(); + + SftpDataAddress sftpDataAddress = + SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + Mockito.when(request.getDestinationDataAddress()).thenReturn(sftpDataAddress); + + Assertions.assertTrue(dataSinkFactory.validate(request).succeeded()); + } + + @Test + void validate_invalidDataAddressType() { + SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); + DataAddress dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); + DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + Mockito.when(request.getDestinationDataAddress()).thenReturn(dataAddress); + + Assertions.assertTrue(dataSinkFactory.validate(request).failed()); + } + + @Test + void validate_invalidDataAddressParameters() { + SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); + final Map properties = + Map.of( + "type", "sftp", + "locationHost", "localhost", + "locationPort", "notANumber", + "locationPath", "path", + "userName", "name", + "userPassword", "password"); + + final DataAddress dataAddress = + DataAddress.Builder.newInstance().properties(properties).build(); + DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + Mockito.when(request.getDestinationDataAddress()).thenReturn(dataAddress); + + Assertions.assertTrue(dataSinkFactory.validate(request).failed()); + } + + @Test + void createSink_successful() { + SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); + SftpUser sftpUser = SftpUser.Builder.newInstance().name("name").build(); + SftpLocation sftpLocation = + SftpLocation.Builder.newInstance().host("127.0.0.1").port(22).path("path").build(); + SftpClientConfig sftpClientConfig = + SftpClientConfig.Builder.newInstance() + .writeOpenModes(List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Append)) + .sftpUser(sftpUser) + .sftpLocation(sftpLocation) + .build(); + + SftpDataAddress sftpDataAddress = + SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + Mockito.when(request.getDestinationDataAddress()).thenReturn(sftpDataAddress); + + try (MockedStatic staticWrapper = + Mockito.mockStatic(SftpClientWrapperImpl.class)) { + staticWrapper + .when(() -> SftpClientWrapperImpl.getSftpClient(sftpClientConfig)) + .thenReturn(Mockito.mock(SftpClient.class)); + Assertions.assertNotNull(dataSinkFactory.createSink(request)); + + staticWrapper.verify( + () -> SftpClientWrapperImpl.getSftpClient(Mockito.any()), Mockito.times(1)); + } + } + + @Test + void createSink_invalidDataAddressType() { + SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); + DataAddress dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); + DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + Mockito.when(request.getDestinationDataAddress()).thenReturn(dataAddress); + + Assertions.assertNull(dataSinkFactory.createSink(request)); } - } - - @Test - @SneakyThrows - void createSink__invalidDataAddressType() { - SftpDataSinkFactory dataSinkFactory = new SftpDataSinkFactory(); - DataAddress dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); - DataFlowRequest 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/SftpDataSinkTest.java b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSinkTest.java index 7af9eea1f..bce5f70c5 100644 --- a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSinkTest.java +++ b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSinkTest.java @@ -20,13 +20,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.util.Arrays; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.SneakyThrows; import org.apache.sshd.sftp.client.SftpClient; import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSource; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; @@ -35,52 +28,61 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; + class SftpDataSinkTest { - @Test - @SneakyThrows - void transferParts() { - final SftpUser userMock = Mockito.mock(SftpUser.class); - final SftpLocation locationMock = Mockito.mock(SftpLocation.class); - final SftpClientConfig sftpClientConfig = - SftpClientConfig.builder() - .sftpUser(userMock) - .sftpLocation(locationMock) - .writeOpenModes(List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Append)) - .build(); - final SftpClient sftpClientMock = Mockito.mock(SftpClient.class); - final SftpClientWrapperImpl sftpClientWrapper = - Mockito.spy(new SftpClientWrapperImpl(sftpClientConfig, sftpClientMock)); - final SftpDataSink sftpDataSink = Mockito.spy(new SftpDataSink(sftpClientWrapper)); - final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + @Test + void transferParts() throws IOException { + var userMock = Mockito.mock(SftpUser.class); + var locationMock = Mockito.mock(SftpLocation.class); + var sftpClientConfig = + SftpClientConfig.Builder.newInstance() + .sftpUser(userMock) + .sftpLocation(locationMock) + .writeOpenModes(List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Append)) + .build(); + var sftpClientMock = Mockito.mock(SftpClient.class); + var sftpClientWrapper = + Mockito.spy(new SftpClientWrapperImpl(sftpClientConfig, sftpClientMock)); + var sftpDataSink = Mockito.spy(new SftpDataSink(sftpClientWrapper)); + var outputStream = new ByteArrayOutputStream(); - Mockito.when(sftpClientMock.write(Mockito.any(), Mockito.anyInt(), Mockito.anyCollection())) - .thenReturn(outputStream); - Mockito.when(locationMock.getPath()).thenReturn("path"); + Mockito.when(sftpClientMock.write(Mockito.any(), Mockito.anyInt(), Mockito.anyCollection())) + .thenReturn(outputStream); + Mockito.when(locationMock.getPath()).thenReturn("path"); - List parts = - Arrays.asList(new SftpTestPart(new byte[] {0, 1}), new SftpTestPart(new byte[] {2, 3})); - byte[] expected = {0, 1, 2, 3}; + List parts = + Arrays.asList(new SftpTestPart(new byte[]{0, 1}), new SftpTestPart(new byte[]{2, 3})); + byte[] expected = {0, 1, 2, 3}; - sftpDataSink.transferParts(parts); + sftpDataSink.transferParts(parts); - Assertions.assertArrayEquals(expected, outputStream.toByteArray()); - Mockito.verify(sftpClientMock, Mockito.times(2)) - .write("path", 4096, List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Append)); - } + Assertions.assertArrayEquals(expected, outputStream.toByteArray()); + Mockito.verify(sftpClientMock, Mockito.times(2)) + .write("path", 4096, List.of(SftpClient.OpenMode.Create, SftpClient.OpenMode.Append)); + } - @AllArgsConstructor - private static class SftpTestPart implements DataSource.Part { + private static class SftpTestPart implements DataSource.Part { - final byte[] content; + final byte[] content; - @Override - public String name() { - return null; - } + private SftpTestPart(byte[] content) { + this.content = content; + } + + @Override + public String name() { + return null; + } - @Override - public InputStream openStream() { - return new ByteArrayInputStream(content); + @Override + public InputStream openStream() { + return new ByteArrayInputStream(content); + } } - } } 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 5c8a63f4f..297059ddc 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 @@ -20,8 +20,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import java.util.Map; -import lombok.SneakyThrows; import org.apache.sshd.sftp.client.SftpClient; import org.eclipse.edc.spi.types.domain.DataAddress; import org.eclipse.edc.spi.types.domain.transfer.DataFlowRequest; @@ -33,86 +31,86 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; +import java.util.Map; + class SftpDataSourceFactoryTest { - @Test - void validate__valid() { - SftpDataSourceFactory dataSourceFactory = new SftpDataSourceFactory(); - SftpUser sftpUser = SftpUser.builder().name("name").build(); - SftpLocation sftpLocation = SftpLocation.builder().host("host").port(22).path("path").build(); - - SftpDataAddress sftpDataAddress = - SftpDataAddress.builder().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getSourceDataAddress()).thenReturn(sftpDataAddress); - - Assertions.assertTrue(dataSourceFactory.validate(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); - - Assertions.assertTrue(dataSourceFactory.validate(request).failed()); - } - - @Test - void validate__invalidDataAddressParameters() { - SftpDataSourceFactory dataSourceFactory = new SftpDataSourceFactory(); - final Map properties = - Map.of( - "type", "sftp", - "locationHost", "localhost", - "locationPort", "notANumber", - "locationPath", "path", - "userName", "name", - "userPassword", "password"); - - DataAddress dataAddress = DataAddress.Builder.newInstance().properties(properties).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getSourceDataAddress()).thenReturn(dataAddress); - - Assertions.assertTrue(dataSourceFactory.validate(request).failed()); - } - - @Test - @SneakyThrows - void createSink__successful() { - SftpDataSourceFactory dataSourceFactory = new SftpDataSourceFactory(); - SftpUser sftpUser = SftpUser.builder().name("name").build(); - SftpLocation sftpLocation = - SftpLocation.builder().host("127.0.0.1").port(22).path("path").build(); - SftpClientConfig sftpClientConfig = - SftpClientConfig.builder().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - - SftpDataAddress sftpDataAddress = - SftpDataAddress.builder().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - DataFlowRequest request = Mockito.mock(DataFlowRequest.class); - Mockito.when(request.getSourceDataAddress()).thenReturn(sftpDataAddress); - - try (MockedStatic staticWrapper = - Mockito.mockStatic(SftpClientWrapperImpl.class)) { - staticWrapper - .when(() -> SftpClientWrapperImpl.getSftpClient(sftpClientConfig)) - .thenReturn(Mockito.mock(SftpClient.class)); - Assertions.assertNotNull(dataSourceFactory.createSource(request)); - - staticWrapper.verify( - () -> SftpClientWrapperImpl.getSftpClient(Mockito.any()), Mockito.times(1)); + @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(); + + SftpDataAddress sftpDataAddress = + SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + Mockito.when(request.getSourceDataAddress()).thenReturn(sftpDataAddress); + + Assertions.assertTrue(dataSourceFactory.validate(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); + + Assertions.assertTrue(dataSourceFactory.validate(request).failed()); + } + + @Test + void validate_invalidDataAddressParameters() { + SftpDataSourceFactory dataSourceFactory = new SftpDataSourceFactory(); + final Map properties = + Map.of( + "type", "sftp", + "locationHost", "localhost", + "locationPort", "notANumber", + "locationPath", "path", + "userName", "name", + "userPassword", "password"); + + DataAddress dataAddress = DataAddress.Builder.newInstance().properties(properties).build(); + DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + Mockito.when(request.getSourceDataAddress()).thenReturn(dataAddress); + + Assertions.assertTrue(dataSourceFactory.validate(request).failed()); + } + + @Test + void createSink_successful() { + SftpDataSourceFactory dataSourceFactory = new SftpDataSourceFactory(); + SftpUser sftpUser = SftpUser.Builder.newInstance().name("name").build(); + SftpLocation sftpLocation = + SftpLocation.Builder.newInstance().host("127.0.0.1").port(22).path("path").build(); + SftpClientConfig sftpClientConfig = + SftpClientConfig.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + + SftpDataAddress sftpDataAddress = + SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + DataFlowRequest request = Mockito.mock(DataFlowRequest.class); + Mockito.when(request.getSourceDataAddress()).thenReturn(sftpDataAddress); + + try (MockedStatic staticWrapper = + Mockito.mockStatic(SftpClientWrapperImpl.class)) { + staticWrapper + .when(() -> SftpClientWrapperImpl.getSftpClient(sftpClientConfig)) + .thenReturn(Mockito.mock(SftpClient.class)); + Assertions.assertNotNull(dataSourceFactory.createSource(request)); + + staticWrapper.verify( + () -> SftpClientWrapperImpl.getSftpClient(Mockito.any()), Mockito.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); + + Assertions.assertNull(dataSourceFactory.createSource(request)); } - } - - @Test - @SneakyThrows - 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); - - Assertions.assertNull(dataSourceFactory.createSource(request)); - } } diff --git a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceTest.java b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceTest.java index 100961640..d25921c46 100644 --- a/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceTest.java +++ b/edc-extensions/transferprocess-sftp-client/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/client/SftpDataSourceTest.java @@ -20,7 +20,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.client; -import lombok.SneakyThrows; import org.apache.sshd.sftp.client.SftpClient; import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSource; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; @@ -30,23 +29,23 @@ import org.mockito.Mockito; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; class SftpDataSourceTest { @Test - @SneakyThrows - void openPartStream() { + void openPartStream() throws IOException { final SftpUser userMock = Mockito.mock(SftpUser.class); final SftpLocation locationMock = Mockito.mock(SftpLocation.class); final SftpClientConfig sftpClientConfig = - SftpClientConfig.builder().sftpUser(userMock).sftpLocation(locationMock).build(); + SftpClientConfig.Builder.newInstance().sftpUser(userMock).sftpLocation(locationMock).build(); final SftpClient sftpClientMock = Mockito.mock(SftpClient.class); final SftpClientWrapperImpl sftpClientWrapper = Mockito.spy(new SftpClientWrapperImpl(sftpClientConfig, sftpClientMock)); SftpDataSource sftpDataSource = Mockito.spy(new SftpDataSource(sftpClientWrapper)); - byte[] expected = new byte[]{ 0, 1, 2, 3 }; + byte[] expected = new byte[]{0, 1, 2, 3}; ByteArrayInputStream outputStream = new ByteArrayInputStream(expected); Mockito.when(locationMock.getPath()).thenReturn("path"); diff --git a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/EdcSftpException.java b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/EdcSftpException.java index aa6151eec..a830b1ea9 100644 --- a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/EdcSftpException.java +++ b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/EdcSftpException.java @@ -17,20 +17,20 @@ import org.eclipse.edc.spi.EdcException; public class EdcSftpException extends EdcException { - public EdcSftpException(String message) { - super(message); - } + public EdcSftpException(String message) { + super(message); + } - public EdcSftpException(String message, Throwable cause) { - super(message, cause); - } + public EdcSftpException(String message, Throwable cause) { + super(message, cause); + } - public EdcSftpException(Throwable cause) { - super(cause); - } + public EdcSftpException(Throwable cause) { + super(cause); + } - public EdcSftpException( - String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } + public EdcSftpException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } } 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 0a9b8ef03..a9ae68638 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 @@ -14,66 +14,112 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.common; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NonNull; import org.eclipse.edc.spi.types.domain.DataAddress; -@AllArgsConstructor -@Builder -@Getter +import java.util.Base64; + +import static java.lang.String.format; + public class SftpDataAddress extends DataAddress { - private static final String LOCATION_HOST = "locationHost"; - private static final String LOCATION_PORT = "locationPort"; - private static final String LOCATION_PATH = "locationPath"; - private static final String USER_NAME = "userName"; - private static final String USER_PASSWORD = "userPassword"; - private static final String USER_PRIVATE_KEY = "userPrivateKey"; - - @Getter private static final String CONNECTION_TYPE = "sftp"; - @NonNull private final SftpUser sftpUser; - @NonNull private final SftpLocation sftpLocation; - - public static SftpDataAddress fromDataAddress(DataAddress dataAddress) throws EdcSftpException { - if (dataAddress instanceof SftpDataAddress) { - return (SftpDataAddress) dataAddress; + private static final String LOCATION_HOST = "locationHost"; + private static final String LOCATION_PORT = "locationPort"; + private static final String LOCATION_PATH = "locationPath"; + private static final String USER_NAME = "userName"; + private static final String USER_PASSWORD = "userPassword"; + private static final String USER_PRIVATE_KEY = "userPrivateKey"; + + + private static final String CONNECTION_TYPE = "sftp"; + + private SftpDataAddress() { + + } + + public static SftpDataAddress fromDataAddress(DataAddress dataAddress) throws EdcSftpException { + if (dataAddress instanceof SftpDataAddress) { + return (SftpDataAddress) dataAddress; + } + + if (!dataAddress.getType().equalsIgnoreCase("sftp")) { + throw new EdcSftpException(format("Invalid DataAddress type: %s. Expected %s.", + dataAddress.getType(), CONNECTION_TYPE)); + } + + try { + var sftpUser = createSftpUser(dataAddress); + + var sftpLocation = createSftpLocation(dataAddress); + + return SftpDataAddress.Builder.newInstance() + .sftpUser(sftpUser) + .sftpLocation(sftpLocation) + .build(); + } 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); + } } - if (!dataAddress.getType().equalsIgnoreCase("sftp")) { - throw new EdcSftpException( - String.format( - "Invalid DataAddress type: %s. Expected %s.", - dataAddress.getType(), CONNECTION_TYPE)); + 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)) + .build(); } - try { - SftpUser sftpUser = - SftpUser.builder() - .name(dataAddress.getProperty(USER_NAME)) - .password(dataAddress.getProperty(USER_PASSWORD)) - .keyPair( - SftpUserKeyPairGenerator.getKeyPairFromPrivateKey( - dataAddress.getProperty(USER_PRIVATE_KEY), - dataAddress.getProperty(USER_NAME))) - .build(); - - SftpLocation sftpLocation = - SftpLocation.builder() - .host(dataAddress.getProperty(LOCATION_HOST)) - .port(Integer.valueOf(dataAddress.getProperty(LOCATION_PORT, "22"))) - .path(dataAddress.getProperty(LOCATION_PATH)) - .build(); - - return SftpDataAddress.builder().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); - } catch (NullPointerException e) { - throw new EdcSftpException(e.getMessage(), e); - } catch (NumberFormatException e) { - throw new EdcSftpException( - String.format( - "Port for SftpLocation %s/%s not a number", - dataAddress.getProperty(LOCATION_HOST), dataAddress.getProperty(LOCATION_PATH)), - e); + private static SftpUser createSftpUser(DataAddress dataAddress) { + return SftpUser.Builder.newInstance() + .name(dataAddress.getProperty(USER_NAME)) + .password(dataAddress.getProperty(USER_PASSWORD)) + .keyPair(SftpUserKeyPairGenerator.getKeyPairFromPrivateKey( + dataAddress.getProperty(USER_PRIVATE_KEY), + dataAddress.getProperty(USER_NAME))) + .build(); } - } + + public SftpUser getSftpUser() { + return createSftpUser(this); + } + + public SftpLocation getSftpLocation() { + return createSftpLocation(this); + } + + public static class Builder extends DataAddress.Builder { + protected Builder(SftpDataAddress address) { + super(address); + } + + public static Builder newInstance() { + return new Builder(new SftpDataAddress()); + } + + public Builder sftpUser(SftpUser user) { + this.address.getProperties().put(USER_NAME, user.getName()); + this.address.getProperties().put(USER_PASSWORD, user.getPassword()); + if (user.getKeyPair() != null) { + this.address.getProperties().put(USER_PRIVATE_KEY, Base64.getEncoder().encodeToString(user.getKeyPair().getPrivate().getEncoded())); + this.address.getProperties().put(KEY_NAME, user.getName()); + } + return this; + } + + public Builder sftpLocation(SftpLocation location) { + this.address.getProperties().put(LOCATION_HOST, location.getHost()); + this.address.getProperties().put(LOCATION_PORT, String.valueOf(location.getPort())); + this.address.getProperties().put(LOCATION_PATH, location.getPath()); + return this; + } + + @Override + public SftpDataAddress build() { + address.setType(CONNECTION_TYPE); + + super.build(); //for validation + return address; + } + } + } diff --git a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpLocation.java b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpLocation.java index b9af1aecb..0b554c8b2 100644 --- a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpLocation.java +++ b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpLocation.java @@ -14,21 +14,64 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.common; -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NonNull; - -@Builder -@Getter -@EqualsAndHashCode +import java.util.Objects; + public class SftpLocation { - @NonNull private final String host; - @NonNull private final Integer port; - @NonNull private final String path; - - @Override - public String toString() { - return String.format("%s:%d/%s", host, port, path); - } + private String host; + private int port; + private String path; + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getPath() { + return path; + } + + @Override + public String toString() { + return String.format("%s:%d/%s", host, port, path); + } + + + public static class Builder { + private final SftpLocation location; + + private Builder() { + location = new SftpLocation(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder host(String host) { + location.host = host; + return this; + } + + public Builder port(int port) { + location.port = port; + return this; + } + + public Builder path(String path) { + location.path = path; + return this; + } + + public SftpLocation build() { + Objects.requireNonNull(location.host, "host"); + Objects.requireNonNull(location.path, "path"); + if (location.port <= 0) { + throw new IllegalArgumentException("port must be > 0 but was " + location.port); + } + return location; + } + } } diff --git a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpLocationFactory.java b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpLocationFactory.java index 8c229d39f..ee5d002e3 100644 --- a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpLocationFactory.java +++ b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpLocationFactory.java @@ -15,5 +15,5 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.common; public interface SftpLocationFactory { - SftpLocation createSftpLocation(String sftpHost, Integer sftpPort, String sftpPath); + SftpLocation createSftpLocation(String sftpHost, Integer sftpPort, String sftpPath); } diff --git a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpProvider.java b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpProvider.java index 4ddd29396..c5d1f7608 100644 --- a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpProvider.java +++ b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpProvider.java @@ -15,11 +15,11 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.common; public interface SftpProvider { - void createUser(SftpUser user); + void createUser(SftpUser user); - void deleteUser(SftpUser user); + void deleteUser(SftpUser user); - void createLocation(SftpLocation location); + void createLocation(SftpLocation location); - void deleteLocation(SftpLocation location); + void deleteLocation(SftpLocation location); } diff --git a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUser.java b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUser.java index b66dfc3e0..3bcd9cff3 100644 --- a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUser.java +++ b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUser.java @@ -15,18 +15,52 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.common; import java.security.KeyPair; -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NonNull; -import lombok.ToString; - -@Builder -@Getter -@ToString(of = "name") -@EqualsAndHashCode + public class SftpUser { - @NonNull private final String name; - private final String password; - private final KeyPair keyPair; + private String name; + private String password; + private KeyPair keyPair; + + public String getName() { + return name; + } + + public String getPassword() { + return password; + } + + public KeyPair getKeyPair() { + return keyPair; + } + + public static class Builder { + private final SftpUser user; + + private Builder() { + user = new SftpUser(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder name(String name) { + user.name = name; + return this; + } + + public Builder password(String password) { + user.password = password; + return this; + } + + public Builder keyPair(KeyPair keyPair) { + user.keyPair = keyPair; + return this; + } + + public SftpUser build() { + return user; + } + } } diff --git a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUserFactory.java b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUserFactory.java index ea04a125e..53a399cf9 100644 --- a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUserFactory.java +++ b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUserFactory.java @@ -15,5 +15,5 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.common; public interface SftpUserFactory { - SftpUser createSftpUser(String sftpUserName, String sftpUserPassword, byte[] sftpUserKeypair); + SftpUser createSftpUser(String sftpUserName, String sftpUserPassword, byte[] sftpUserKeypair); } diff --git a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUserKeyPairGenerator.java b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUserKeyPairGenerator.java index 55d2984ce..abbaff3dc 100644 --- a/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUserKeyPairGenerator.java +++ b/edc-extensions/transferprocess-sftp-common/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/common/SftpUserKeyPairGenerator.java @@ -17,56 +17,45 @@ import java.security.KeyFactory; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; import java.security.interfaces.RSAPrivateCrtKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.Base64; -import lombok.AllArgsConstructor; -import lombok.NonNull; +import java.util.Objects; -@AllArgsConstructor public class SftpUserKeyPairGenerator { - public static KeyPair getKeyPairFromPrivateKey( - byte[] privateKeyBytes, @NonNull String sftpUserName) { - if (privateKeyBytes == null) { - return null; - } + public static KeyPair getKeyPairFromPrivateKey(byte[] privateKeyBytes, String sftpUserName) { + Objects.requireNonNull(sftpUserName, "sftpUsername"); + if (privateKeyBytes == null) { + return null; + } - try { - final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - final PrivateKey privateKey = - keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes)); + try { + var keyFactory = KeyFactory.getInstance("RSA"); + var privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes)); - final RSAPrivateCrtKey privateKeySpec = (RSAPrivateCrtKey) privateKey; + var privateKeySpec = (RSAPrivateCrtKey) privateKey; - final PublicKey publicKey = - keyFactory.generatePublic( - new RSAPublicKeySpec( - privateKeySpec.getModulus(), privateKeySpec.getPublicExponent())); - return new KeyPair(publicKey, privateKey); + var publicKey = keyFactory.generatePublic(new RSAPublicKeySpec(privateKeySpec.getModulus(), privateKeySpec.getPublicExponent())); + return new KeyPair(publicKey, privateKey); - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new EdcSftpException( - String.format("Unable to parse provided keypair for Sftp user %s", sftpUserName), e); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new EdcSftpException(String.format("Unable to parse provided keypair for Sftp user %s", sftpUserName), e); + } } - } - public static KeyPair getKeyPairFromPrivateKey(String privateKey, @NonNull String sftpUserName) { - if (privateKey == null) { - return null; - } + public static KeyPair getKeyPairFromPrivateKey(String privateKeyBase64, String sftpUserName) { + if (privateKeyBase64 == null) { + return null; + } - byte[] publicBytes; - try { - publicBytes = Base64.getDecoder().decode(privateKey); - } catch (IllegalArgumentException e) { - throw new EdcSftpException( - String.format("Cannot decode base64 private key for user %s", sftpUserName), e); - } + try { + var publicBytes = Base64.getDecoder().decode(privateKeyBase64); + return getKeyPairFromPrivateKey(publicBytes, sftpUserName); + } catch (IllegalArgumentException e) { + throw new EdcSftpException(String.format("Cannot decode base64 private key for user %s", sftpUserName), e); + } - return getKeyPairFromPrivateKey(publicBytes, sftpUserName); - } + } } 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 d29cdcd0e..07f60e406 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 @@ -14,198 +14,190 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.common; -import java.security.KeyPair; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.junit.jupiter.api.Test; + import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Map; -import lombok.SneakyThrows; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; class SftpDataAddressTest { - @Test - void fromDataAddress__password() { - final Map properties = - Map.of( - "type", "sftp", - "locationHost", "localhost", - "locationPort", "22", - "locationPath", "path", - "userName", "name", - "userPassword", "password"); - - final DataAddress dataAddress = - DataAddress.Builder.newInstance().properties(properties).build(); - - final SftpDataAddress sftpDataAddress = SftpDataAddress.fromDataAddress(dataAddress); - - Assertions.assertEquals("localhost", sftpDataAddress.getSftpLocation().getHost()); - Assertions.assertEquals(22, sftpDataAddress.getSftpLocation().getPort()); - Assertions.assertEquals("path", sftpDataAddress.getSftpLocation().getPath()); - Assertions.assertEquals("name", sftpDataAddress.getSftpUser().getName()); - Assertions.assertEquals("password", sftpDataAddress.getSftpUser().getPassword()); - Assertions.assertNull(sftpDataAddress.getSftpUser().getKeyPair()); - } - - @Test - @SneakyThrows - void fromDataAddress__keyPair() { - final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(2048); - final KeyPair keyPair = keyPairGenerator.generateKeyPair(); - final byte[] privateKeyBytes = keyPair.getPrivate().getEncoded(); - - final Map properties = - Map.of( - "type", "sftp", - "locationHost", "localhost", - "locationPort", "22", - "locationPath", "path", - "userName", "name", - "userPrivateKey", Base64.getEncoder().encodeToString(privateKeyBytes)); - - final DataAddress dataAddress = - DataAddress.Builder.newInstance().properties(properties).build(); - - final SftpDataAddress sftpDataAddress = SftpDataAddress.fromDataAddress(dataAddress); - - Assertions.assertEquals("localhost", sftpDataAddress.getSftpLocation().getHost()); - Assertions.assertEquals(22, sftpDataAddress.getSftpLocation().getPort()); - Assertions.assertEquals("path", sftpDataAddress.getSftpLocation().getPath()); - Assertions.assertEquals("name", sftpDataAddress.getSftpUser().getName()); - Assertions.assertArrayEquals( - privateKeyBytes, sftpDataAddress.getSftpUser().getKeyPair().getPrivate().getEncoded()); - Assertions.assertNull(sftpDataAddress.getSftpUser().getPassword()); - } - - @Test - @SneakyThrows - void fromDataAddress__noAuth() { - final Map properties = - Map.of( - "type", "sftp", - "locationHost", "localhost", - "locationPort", "22", - "locationPath", "path", - "userName", "name"); - - final DataAddress dataAddress = - DataAddress.Builder.newInstance().properties(properties).build(); - - final SftpDataAddress sftpDataAddress = SftpDataAddress.fromDataAddress(dataAddress); - - Assertions.assertEquals("localhost", sftpDataAddress.getSftpLocation().getHost()); - Assertions.assertEquals(22, sftpDataAddress.getSftpLocation().getPort()); - Assertions.assertEquals("path", sftpDataAddress.getSftpLocation().getPath()); - Assertions.assertEquals("name", sftpDataAddress.getSftpUser().getName()); - Assertions.assertNull(sftpDataAddress.getSftpUser().getPassword()); - } - - @Test - @SneakyThrows - void fromDataAddress__invalidKeyPairBrokenBase64() { - final Map properties = - Map.of( - "type", "sftp", - "locationHost", "localhost", - "locationPort", "22", - "locationPath", "path", - "userName", "name", - "userPrivateKey", "clearlyNotAPrivateKey"); - - final DataAddress dataAddress = - DataAddress.Builder.newInstance().properties(properties).build(); - - EdcSftpException edcSftpException = - Assertions.assertThrows( - EdcSftpException.class, () -> SftpDataAddress.fromDataAddress(dataAddress)); - Assertions.assertEquals( - "Cannot decode base64 private key for user name", edcSftpException.getMessage()); - } - - @Test - @SneakyThrows - void fromDataAddress__invalidKeyPairButCorrectBase64() { - final Map properties = - Map.of( - "type", "sftp", - "locationHost", "localhost", - "locationPort", "22", - "locationPath", "path", - "userName", "name", - "userPrivateKey", "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu"); - - final DataAddress dataAddress = - DataAddress.Builder.newInstance().properties(properties).build(); - - EdcSftpException edcSftpException = - Assertions.assertThrows( - EdcSftpException.class, () -> SftpDataAddress.fromDataAddress(dataAddress)); - Assertions.assertEquals( - "Unable to parse provided keypair for Sftp user name", edcSftpException.getMessage()); - } - - @Test - void fromDataAddress__portNaN() { - final Map properties = - Map.of( - "type", "sftp", - "locationHost", "localhost", - "locationPort", "notANumber", - "locationPath", "path", - "userName", "name", - "userPassword", "password"); - - final DataAddress dataAddress = - DataAddress.Builder.newInstance().properties(properties).build(); - - EdcSftpException edcSftpException = - Assertions.assertThrows( - EdcSftpException.class, () -> SftpDataAddress.fromDataAddress(dataAddress)); - Assertions.assertEquals( - "Port for SftpLocation localhost/path not a number", edcSftpException.getMessage()); - } - - @Test - void fromDataAddress__missingParameter() { - final Map properties = - Map.of( - "type", "sftp", - "locationPort", "22", - "locationPath", "path", - "userName", "name", - "userPassword", "password"); - - final DataAddress dataAddress = - DataAddress.Builder.newInstance().properties(properties).build(); - - EdcSftpException edcSftpException = - Assertions.assertThrows( - EdcSftpException.class, () -> SftpDataAddress.fromDataAddress(dataAddress)); - Assertions.assertEquals("host is marked non-null but is null", edcSftpException.getMessage()); - } - - @Test - void fromDataAddress__notSftp() { - final Map properties = - Map.of( - "type", "somethingOtherThanSftp", - "locationHost", "localhost", - "locationPort", "22", - "locationPath", "path", - "userName", "name", - "userPassword", "password"); - - final DataAddress dataAddress = - DataAddress.Builder.newInstance().properties(properties).build(); - - EdcSftpException edcSftpException = - Assertions.assertThrows( - EdcSftpException.class, () -> SftpDataAddress.fromDataAddress(dataAddress)); - Assertions.assertEquals( - "Invalid DataAddress type: somethingOtherThanSftp. Expected sftp.", - edcSftpException.getMessage()); - } + @Test + void fromDataAddress_password() { + var properties = Map.of( + "type", "sftp", + "locationHost", "localhost", + "locationPort", "22", + "locationPath", "path", + "userName", "name", + "userPassword", "password"); + + var dataAddress = DataAddress.Builder.newInstance().properties(properties).build(); + + var sftpDataAddress = SftpDataAddress.fromDataAddress(dataAddress); + + assertEquals("localhost", sftpDataAddress.getSftpLocation().getHost()); + assertEquals(22, sftpDataAddress.getSftpLocation().getPort()); + assertEquals("path", sftpDataAddress.getSftpLocation().getPath()); + assertEquals("name", sftpDataAddress.getSftpUser().getName()); + assertEquals("password", sftpDataAddress.getSftpUser().getPassword()); + assertNull(sftpDataAddress.getSftpUser().getKeyPair()); + } + + @Test + void fromDataAddress_keyPair() throws NoSuchAlgorithmException { + var keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + var keyPair = keyPairGenerator.generateKeyPair(); + var privateKeyBytes = keyPair.getPrivate().getEncoded(); + + var properties = + Map.of( + "type", "sftp", + "locationHost", "localhost", + "locationPort", "22", + "locationPath", "path", + "userName", "name", + "userPrivateKey", Base64.getEncoder().encodeToString(privateKeyBytes)); + + var dataAddress = + DataAddress.Builder.newInstance().properties(properties).build(); + + var sftpDataAddress = SftpDataAddress.fromDataAddress(dataAddress); + + assertEquals("localhost", sftpDataAddress.getSftpLocation().getHost()); + assertEquals(22, sftpDataAddress.getSftpLocation().getPort()); + assertEquals("path", sftpDataAddress.getSftpLocation().getPath()); + assertEquals("name", sftpDataAddress.getSftpUser().getName()); + assertArrayEquals( + privateKeyBytes, sftpDataAddress.getSftpUser().getKeyPair().getPrivate().getEncoded()); + assertNull(sftpDataAddress.getSftpUser().getPassword()); + } + + @Test + void fromDataAddress_noAuth() { + var properties = + Map.of( + "type", "sftp", + "locationHost", "localhost", + "locationPort", "22", + "locationPath", "path", + "userName", "name"); + + var dataAddress = + DataAddress.Builder.newInstance().properties(properties).build(); + + var sftpDataAddress = SftpDataAddress.fromDataAddress(dataAddress); + + assertEquals("localhost", sftpDataAddress.getSftpLocation().getHost()); + assertEquals(22, sftpDataAddress.getSftpLocation().getPort()); + assertEquals("path", sftpDataAddress.getSftpLocation().getPath()); + assertEquals("name", sftpDataAddress.getSftpUser().getName()); + assertNull(sftpDataAddress.getSftpUser().getPassword()); + } + + @Test + void fromDataAddress_invalidKeyPairBrokenBase64() { + var properties = + Map.of( + "type", "sftp", + "locationHost", "localhost", + "locationPort", "22", + "locationPath", "path", + "userName", "name", + "userPrivateKey", "clearlyNotAPrivateKey"); + + var dataAddress = + DataAddress.Builder.newInstance().properties(properties).build(); + + var edcSftpException = + assertThrows( + EdcSftpException.class, () -> SftpDataAddress.fromDataAddress(dataAddress)); + assertEquals( + "Cannot decode base64 private key for user name", edcSftpException.getMessage()); + } + + @Test + void fromDataAddress_invalidKeyPairButCorrectBase64() { + var properties = + Map.of( + "type", "sftp", + "locationHost", "localhost", + "locationPort", "22", + "locationPath", "path", + "userName", "name", + "userPrivateKey", "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu"); + + var dataAddress = + DataAddress.Builder.newInstance().properties(properties).build(); + + var edcSftpException = + assertThrows( + EdcSftpException.class, () -> SftpDataAddress.fromDataAddress(dataAddress)); + assertEquals( + "Unable to parse provided keypair for Sftp user name", edcSftpException.getMessage()); + } + + @Test + void fromDataAddress_portNaN() { + var properties = + Map.of( + "type", "sftp", + "locationHost", "localhost", + "locationPort", "notANumber", + "locationPath", "path", + "userName", "name", + "userPassword", "password"); + + var dataAddress = DataAddress.Builder.newInstance().properties(properties).build(); + + var edcSftpException = + assertThrows( + EdcSftpException.class, () -> SftpDataAddress.fromDataAddress(dataAddress)); + assertEquals( + "Port for SftpLocation localhost/path not a number", edcSftpException.getMessage()); + } + + @Test + void fromDataAddress_missingParameter() { + var properties = + Map.of( + "type", "sftp", + "locationPort", "22", + "locationPath", "path", + "userName", "name", + "userPassword", "password"); + + var dataAddress = DataAddress.Builder.newInstance().properties(properties).build(); + + assertThatThrownBy(() -> SftpDataAddress.fromDataAddress(dataAddress)) + .isInstanceOf(EdcSftpException.class) + .hasMessageStartingWith("host"); + } + + @Test + void fromDataAddress_notSftp() { + var properties = + Map.of( + "type", "somethingOtherThanSftp", + "locationHost", "localhost", + "locationPort", "22", + "locationPath", "path", + "userName", "name", + "userPassword", "password"); + + var dataAddress = DataAddress.Builder.newInstance().properties(properties).build(); + + var edcSftpException = assertThrows(EdcSftpException.class, () -> SftpDataAddress.fromDataAddress(dataAddress)); + assertEquals("Invalid DataAddress type: somethingOtherThanSftp. Expected sftp.", + edcSftpException.getMessage()); + } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvider.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvider.java index d12015d7d..daacc9927 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvider.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvider.java @@ -20,47 +20,47 @@ public class NoOpSftpProvider implements SftpProvider { - /** - * This provisioner does not create cloud resources. The assumption is that users and locations - * already exist cloud-side. Thus, this method has no functionality. - * - * @param user The user whose credentials should be deployed. - */ - @Override - public void createUser(SftpUser user) { - // do nothing - } + /** + * This provisioner does not create cloud resources. The assumption is that users and locations + * already exist cloud-side. Thus, this method has no functionality. + * + * @param user The user whose credentials should be deployed. + */ + @Override + public void createUser(SftpUser user) { + // do nothing + } - /** - * This provisioner does not create cloud resources. The assumption is that users and locations - * already exist cloud-side. Thus, this method has no functionality. - * - * @param user The user whose credentials should be deleted. - */ - @Override - public void deleteUser(SftpUser user) { - // do nothing - } + /** + * This provisioner does not create cloud resources. The assumption is that users and locations + * already exist cloud-side. Thus, this method has no functionality. + * + * @param user The user whose credentials should be deleted. + */ + @Override + public void deleteUser(SftpUser user) { + // do nothing + } - /** - * This provisioner does not create cloud resources. The assumption is that users and locations - * already exist cloud-side. Thus, this method has no functionality. - * - * @param location The location of the cloud resource that should be made available. - */ - @Override - public void createLocation(SftpLocation location) { - // do nothing - } + /** + * This provisioner does not create cloud resources. The assumption is that users and locations + * already exist cloud-side. Thus, this method has no functionality. + * + * @param location The location of the cloud resource that should be made available. + */ + @Override + public void createLocation(SftpLocation location) { + // do nothing + } - /** - * This provisioner does not create cloud resources. The assumption is that users and locations - * already exist cloud-side. Thus, this method has no functionality. - * - * @param location The location of the cloud resource that should be made unavailable. - */ - @Override - public void deleteLocation(SftpLocation location) { - // do nothing - } + /** + * This provisioner does not create cloud resources. The assumption is that users and locations + * already exist cloud-side. Thus, this method has no functionality. + * + * @param location The location of the cloud resource that should be made unavailable. + */ + @Override + public void deleteLocation(SftpLocation location) { + // do nothing + } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisioner.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisioner.java index 978f4bc94..cf89412ca 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisioner.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisioner.java @@ -14,9 +14,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import java.util.concurrent.CompletableFuture; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; import org.eclipse.edc.connector.transfer.spi.provision.Provisioner; import org.eclipse.edc.connector.transfer.spi.types.DeprovisionedResource; import org.eclipse.edc.connector.transfer.spi.types.ProvisionResponse; @@ -31,120 +28,124 @@ import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; -@RequiredArgsConstructor -public class NoOpSftpProvisioner - implements Provisioner { - static final String DATA_ADDRESS_TYPE = "sftp"; - static final String PROVIDER_TYPE = "NoOp"; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class NoOpSftpProvisioner implements Provisioner { + public static final String DATA_ADDRESS_TYPE = "sftp"; + public static final String PROVIDER_TYPE = "NoOp"; - @NonNull private final String policyScope; - @NonNull private final PolicyEngine policyEngine; - @NonNull private final NoOpSftpProvider sftpProvider; + private final String policyScope; + private final PolicyEngine policyEngine; + private final NoOpSftpProvider sftpProvider; - @Override - public boolean canProvision(@NonNull ResourceDefinition resourceDefinition) { - if (!(resourceDefinition instanceof SftpProviderResourceDefinition)) { - return false; + public NoOpSftpProvisioner(String policyScope, PolicyEngine policyEngine, NoOpSftpProvider sftpProvider) { + this.policyScope = policyScope; + this.policyEngine = policyEngine; + this.sftpProvider = sftpProvider; } - if (!(((SftpProviderResourceDefinition) resourceDefinition) - .getProviderType() - .equals(PROVIDER_TYPE))) { - return false; + + @Override + public boolean canProvision(ResourceDefinition resourceDefinition) { + Objects.requireNonNull(resourceDefinition, "resourceDefinition"); + if (!(resourceDefinition instanceof SftpProviderResourceDefinition)) { + return false; + } + if (!(((SftpProviderResourceDefinition) resourceDefinition) + .getProviderType() + .equals(PROVIDER_TYPE))) { + return false; + } + try { + SftpDataAddress.fromDataAddress(((SftpProviderResourceDefinition) resourceDefinition).getSftpDataAddress()); + } catch (EdcSftpException e) { + return false; + } + return true; } - try { - SftpDataAddress.fromDataAddress( - ((SftpProviderResourceDefinition) resourceDefinition).getSftpDataAddress()); - } catch (EdcSftpException e) { - return false; + + @Override + public boolean canDeprovision(ProvisionedResource provisionedResource) { + Objects.requireNonNull(provisionedResource, "provisionedResource"); + if (!(provisionedResource instanceof SftpProvisionedContentResource)) { + return false; + } + + if (!(((SftpProvisionedContentResource) provisionedResource) + .getProviderType() + .equals(PROVIDER_TYPE))) { + return false; + } + + try { + SftpDataAddress.fromDataAddress(((SftpProvisionedContentResource) provisionedResource).getDataAddress()); + } catch (EdcSftpException e) { + return false; + } + return true; } - return true; - } - @Override - public boolean canDeprovision(@NonNull ProvisionedResource provisionedResource) { - if (!(provisionedResource instanceof SftpProvisionedContentResource)) { - return false; + @Override + public CompletableFuture> provision(SftpProviderResourceDefinition sftpProviderResourceDefinition, Policy policy) { + + return CompletableFuture.supplyAsync( + () -> { + if (!this.canProvision(sftpProviderResourceDefinition)) { + return StatusResult.failure(ResponseStatus.FATAL_ERROR); + } + // As of the time of writing, policies don't actually do anything in this context. + // They are included here in case EDC wants to use them eventually. + Policy scopedPolicy; + scopedPolicy = policyEngine.filter(policy, policyScope); + sftpProvider.createLocation(sftpProviderResourceDefinition.getSftpDataAddress().getSftpLocation()); + sftpProvider.createUser(sftpProviderResourceDefinition.getSftpDataAddress().getSftpUser()); + + var randomId = UUID.randomUUID().toString(); + var sftpProvisionedContentResource = SftpProvisionedContentResource.Builder.newInstance() + .sftpDataAddress(sftpProviderResourceDefinition.getSftpDataAddress()) + .providerType(PROVIDER_TYPE) + .scopedPolicy(scopedPolicy) + .provisionedResourceId(randomId) + .resourceDefinitionId(sftpProviderResourceDefinition.getId()) + .provisionedResourceId( + generateResourceId( + sftpProviderResourceDefinition.getSftpDataAddress().getSftpUser(), + sftpProviderResourceDefinition.getSftpDataAddress().getSftpLocation())) + .build(); + + return StatusResult.success(ProvisionResponse.Builder.newInstance() + .resource(sftpProvisionedContentResource) + .build()); + }); } - if (!(((SftpProvisionedContentResource) provisionedResource) - .getProviderType() - .equals(PROVIDER_TYPE))) { - return false; + @Override + public CompletableFuture> deprovision(SftpProvisionedContentResource sftpProvisionedContentResource, Policy policy) { + return CompletableFuture.supplyAsync( + () -> { + if (!this.canDeprovision(sftpProvisionedContentResource)) { + return StatusResult.failure(ResponseStatus.FATAL_ERROR); + } + // As of the time of writing, policies don't actually do anything in this context. + // They are included here in case EDC wants to use them eventually. + var dataAddress = sftpProvisionedContentResource.getSftpDataAddress(); + sftpProvider.deleteLocation(dataAddress.getSftpLocation()); + sftpProvider.deleteUser(dataAddress.getSftpUser()); + + DeprovisionedResource deprovisionedResource = + DeprovisionedResource.Builder.newInstance() + .provisionedResourceId(sftpProvisionedContentResource.getProvisionedResourceId()) + .inProcess(true) + .build(); + + return StatusResult.success(deprovisionedResource); + }); } - try { - SftpDataAddress.fromDataAddress( - ((SftpProvisionedContentResource) provisionedResource).getSftpDataAddress()); - } catch (EdcSftpException e) { - return false; + private String generateResourceId(SftpUser sftpUser, SftpLocation sftpLocation) { + return String.format( + "%s@%s:%d/%s", + sftpUser.getName(), sftpLocation.getHost(), sftpLocation.getPort(), sftpLocation.getPath()); } - return true; - } - - @Override - public CompletableFuture> provision( - SftpProviderResourceDefinition sftpProviderResourceDefinition, Policy policy) { - - return CompletableFuture.supplyAsync( - () -> { - if (!this.canProvision(sftpProviderResourceDefinition)) { - return StatusResult.failure(ResponseStatus.FATAL_ERROR); - } - // As of the time of writing, policies don't actually do anything in this context. - // They are included here in case EDC wants to use them eventually. - Policy scopedPolicy; - scopedPolicy = policyEngine.filter(policy, policyScope); - sftpProvider.createLocation( - sftpProviderResourceDefinition.getSftpDataAddress().getSftpLocation()); - sftpProvider.createUser( - sftpProviderResourceDefinition.getSftpDataAddress().getSftpUser()); - - SftpProvisionedContentResource sftpProvisionedContentResource = - SftpProvisionedContentResource.builder() - .sftpDataAddress(sftpProviderResourceDefinition.getSftpDataAddress()) - .providerType(PROVIDER_TYPE) - .scopedPolicy(scopedPolicy) - .provisionedResourceId( - generateResourceId( - sftpProviderResourceDefinition.getSftpDataAddress().getSftpUser(), - sftpProviderResourceDefinition.getSftpDataAddress().getSftpLocation())) - .build(); - - return StatusResult.success( - ProvisionResponse.Builder.newInstance() - .resource(sftpProvisionedContentResource) - .build()); - }); - } - - @Override - public CompletableFuture> deprovision( - SftpProvisionedContentResource sftpProvisionedContentResource, Policy policy) { - return CompletableFuture.supplyAsync( - () -> { - if (!this.canDeprovision(sftpProvisionedContentResource)) { - return StatusResult.failure(ResponseStatus.FATAL_ERROR); - } - // As of the time of writing, policies don't actually do anything in this context. - // They are included here in case EDC wants to use them eventually. - sftpProvider.deleteLocation( - sftpProvisionedContentResource.getSftpDataAddress().getSftpLocation()); - sftpProvider.deleteUser( - sftpProvisionedContentResource.getSftpDataAddress().getSftpUser()); - - DeprovisionedResource deprovisionedResource = - DeprovisionedResource.Builder.newInstance() - .provisionedResourceId(sftpProvisionedContentResource.getProvisionedResourceId()) - .inProcess(true) - .build(); - - return StatusResult.success(deprovisionedResource); - }); - } - - private String generateResourceId(SftpUser sftpUser, SftpLocation sftpLocation) { - return String.format( - "%s@%s:%d/%s", - sftpUser.getName(), sftpLocation.getHost(), sftpLocation.getPort(), sftpLocation.getPath()); - } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpLocationFactoryImpl.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpLocationFactoryImpl.java index 413c98335..8891d1a14 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpLocationFactoryImpl.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpLocationFactoryImpl.java @@ -14,14 +14,12 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import lombok.RequiredArgsConstructor; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocationFactory; -@RequiredArgsConstructor public class SftpLocationFactoryImpl implements SftpLocationFactory { - @Override - public SftpLocation createSftpLocation(String sftpHost, Integer sftpPort, String sftpPath) { - return SftpLocation.builder().host(sftpHost).port(sftpPort).path(sftpPath).build(); - } + @Override + public SftpLocation createSftpLocation(String sftpHost, Integer sftpPort, String sftpPath) { + return SftpLocation.Builder.newInstance().host(sftpHost).port(sftpPort).path(sftpPath).build(); + } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinition.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinition.java index a15c97cca..70ce2d86b 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinition.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinition.java @@ -14,20 +14,29 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; -@Getter -@RequiredArgsConstructor public class SftpProviderResourceDefinition extends ResourceDefinition { - @NonNull private String providerType; - @NonNull private SftpDataAddress sftpDataAddress; + private final String providerType; + private final SftpDataAddress sftpDataAddress; - @Override - public > B toBuilder() { - return null; - } + public SftpProviderResourceDefinition(String providerType, SftpDataAddress sftpDataAddress) { + this.providerType = providerType; + this.sftpDataAddress = sftpDataAddress; + } + + + public String getProviderType() { + return providerType; + } + + public SftpDataAddress getSftpDataAddress() { + return sftpDataAddress; + } + + @Override + public > B toBuilder() { + return null; + } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGenerator.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGenerator.java index ed9c137e5..42476dc03 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGenerator.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGenerator.java @@ -14,9 +14,6 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import static org.eclipse.tractusx.edc.transferprocess.sftp.provisioner.NoOpSftpProvisioner.PROVIDER_TYPE; - -import lombok.RequiredArgsConstructor; import org.eclipse.edc.connector.transfer.spi.provision.ProviderResourceDefinitionGenerator; import org.eclipse.edc.connector.transfer.spi.types.DataRequest; import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; @@ -26,29 +23,26 @@ import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; import org.jetbrains.annotations.Nullable; -@RequiredArgsConstructor -public class SftpProviderResourceDefinitionGenerator - implements ProviderResourceDefinitionGenerator { - @Override - public @Nullable ResourceDefinition generate( - DataRequest dataRequest, DataAddress assetAddress, Policy policy) { - SftpDataAddress sftpDataAddress; - try { - sftpDataAddress = SftpDataAddress.fromDataAddress(assetAddress); - } catch (EdcSftpException e) { - return null; +public class SftpProviderResourceDefinitionGenerator implements ProviderResourceDefinitionGenerator { + + @Override + public @Nullable ResourceDefinition generate(DataRequest dataRequest, DataAddress assetAddress, Policy policy) { + try { + var sftpDataAddress = SftpDataAddress.fromDataAddress(assetAddress); + return new SftpProviderResourceDefinition(NoOpSftpProvisioner.PROVIDER_TYPE, sftpDataAddress); + } catch (EdcSftpException e) { + return null; + } } - return new SftpProviderResourceDefinition(PROVIDER_TYPE, sftpDataAddress); - } - @Override - public boolean canGenerate(DataRequest dataRequest, DataAddress dataAddress, Policy policy) { - try { - SftpDataAddress.fromDataAddress(dataAddress); - } catch (EdcSftpException e) { - return false; + @Override + public boolean canGenerate(DataRequest dataRequest, DataAddress dataAddress, Policy policy) { + try { + SftpDataAddress.fromDataAddress(dataAddress); + } catch (EdcSftpException e) { + return false; + } + return true; } - return true; - } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionedContentResource.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionedContentResource.java index 5f7f364ab..cdeba389f 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionedContentResource.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionedContentResource.java @@ -11,22 +11,80 @@ * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation * */ + package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import lombok.Builder; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; import org.eclipse.edc.connector.transfer.spi.types.ProvisionedContentResource; import org.eclipse.edc.policy.model.Policy; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; -@Getter -@RequiredArgsConstructor -@Builder +import java.util.Objects; + public class SftpProvisionedContentResource extends ProvisionedContentResource { - @NonNull private String providerType; - @NonNull private Policy scopedPolicy; - @NonNull private SftpDataAddress sftpDataAddress; - @NonNull private String provisionedResourceId; + private String providerType; + private Policy scopedPolicy; + private SftpDataAddress sftpDataAddress; + + private SftpProvisionedContentResource() { + + } + + public String getProviderType() { + return providerType; + } + + public Policy getScopedPolicy() { + return scopedPolicy; + } + + public String getProvisionedResourceId() { + return id; + } + + public SftpDataAddress getSftpDataAddress() { + return sftpDataAddress; + } + + + public static class Builder extends ProvisionedContentResource.Builder { + + protected Builder(SftpProvisionedContentResource resource) { + super(resource); + } + + public static Builder newInstance() { + return new Builder(new SftpProvisionedContentResource()); + } + + public Builder providerType(String providerType) { + this.provisionedResource.providerType = providerType; + return this; + } + + public Builder scopedPolicy(Policy scopedPolicy) { + provisionedResource.scopedPolicy = scopedPolicy; + return this; + } + + public Builder sftpDataAddress(SftpDataAddress dataAddress) { + dataAddress(dataAddress); + provisionedResource.sftpDataAddress = dataAddress; + return this; + } + + public Builder provisionedResourceId(String resourceId) { + id(resourceId); + + return this; + } + + public SftpProvisionedContentResource build() { + provisionedResource.dataAddress = dataAddressBuilder.build(); + Objects.requireNonNull(provisionedResource.providerType, "providerType"); + Objects.requireNonNull(provisionedResource.scopedPolicy, "scopedPolicy"); + Objects.requireNonNull(provisionedResource.sftpDataAddress, "dataAddress"); + return provisionedResource; + } + + } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerConfiguration.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerConfiguration.java index acf67ac29..414de3422 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerConfiguration.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerConfiguration.java @@ -16,26 +16,22 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; import java.net.URL; -import lombok.Builder; -import lombok.Getter; -import lombok.NonNull; -/** Configuration to create a resource definition and provisioner pair for sftp data transfer. */ -@Getter -@Builder +/** + * Configuration to create a resource definition and provisioner pair for sftp data transfer. + */ public class SftpProvisionerConfiguration { - @NonNull private final String name; + private String name; - @NonNull @Builder.Default - private final ProvisionerType provisionerType = ProvisionerType.PROVIDER; + private final ProvisionerType provisionerType = ProvisionerType.PROVIDER; - @NonNull private final String dataAddressType; - @NonNull private final String policyScope; - @NonNull private final URL endpoint; + private String dataAddressType; + private String policyScope; + private URL endpoint; - public enum ProvisionerType { - CONSUMER, - PROVIDER - } + public enum ProvisionerType { + CONSUMER, + PROVIDER + } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerExtension.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerExtension.java index eb3e3f80f..8a7081e89 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerExtension.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProvisionerExtension.java @@ -26,31 +26,29 @@ @Provides(NoOpSftpProvisioner.class) public class SftpProvisionerExtension implements ServiceExtension { - @Inject ProvisionManager provisionManager; - @Inject Monitor monitor; - @Inject PolicyEngine policyEngine; - - private static final String POLICY_SCOPE_CONFIG_PATH = "provisioner.sftp.policy.scope"; - private static final String DEFAULT_POLICY_SCOPE = "sftp.provisioner"; - - @Override - public String name() { - return "Sftp Provisioner"; - } - - @Override - public void initialize(ServiceExtensionContext context) { - final String policyScope = - context.getConfig().getString(POLICY_SCOPE_CONFIG_PATH, DEFAULT_POLICY_SCOPE); - - final NoOpSftpProvider sftpProvider = new NoOpSftpProvider(); - final NoOpSftpProvisioner noOpSftpProvisioner = - new NoOpSftpProvisioner(policyScope, policyEngine, sftpProvider); - final SftpProviderResourceDefinitionGenerator generator = - new SftpProviderResourceDefinitionGenerator(); - provisionManager.register(noOpSftpProvisioner); - context.registerService(ProviderResourceDefinitionGenerator.class, generator); - - monitor.info("SftpProvisionerExtension: authentication/initialization complete."); - } + private static final String POLICY_SCOPE_CONFIG_PATH = "provisioner.sftp.policy.scope"; + private static final String DEFAULT_POLICY_SCOPE = "sftp.provisioner"; + @Inject + ProvisionManager provisionManager; + @Inject + Monitor monitor; + @Inject + PolicyEngine policyEngine; + + @Override + public String name() { + return "Sftp Provisioner"; + } + + @Override + public void initialize(ServiceExtensionContext context) { + var policyScope = context.getConfig().getString(POLICY_SCOPE_CONFIG_PATH, DEFAULT_POLICY_SCOPE); + + var sftpProvider = new NoOpSftpProvider(); + var noOpSftpProvisioner = new NoOpSftpProvisioner(policyScope, policyEngine, sftpProvider); + var generator = new SftpProviderResourceDefinitionGenerator(); + provisionManager.register(noOpSftpProvisioner); + context.registerService(ProviderResourceDefinitionGenerator.class, generator); + monitor.info("SftpProvisionerExtension: authentication/initialization complete."); + } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpUserFactoryImpl.java b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpUserFactoryImpl.java index 2bb921fdf..d55f33734 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpUserFactoryImpl.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/main/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpUserFactoryImpl.java @@ -14,25 +14,20 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import java.security.KeyPair; -import lombok.Builder; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUserFactory; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUserKeyPairGenerator; -@Builder public class SftpUserFactoryImpl implements SftpUserFactory { - @Override - public SftpUser createSftpUser( - String sftpUserName, String sftpUserPassword, byte[] sftpUserPrivateKey) { - KeyPair sftpUserKeyPair = - SftpUserKeyPairGenerator.getKeyPairFromPrivateKey(sftpUserPrivateKey, sftpUserName); + @Override + public SftpUser createSftpUser(String sftpUserName, String sftpUserPassword, byte[] sftpUserPrivateKey) { + var sftpUserKeyPair = SftpUserKeyPairGenerator.getKeyPairFromPrivateKey(sftpUserPrivateKey, sftpUserName); - return SftpUser.builder() - .name(sftpUserName) - .password(sftpUserPassword) - .keyPair(sftpUserKeyPair) - .build(); - } + return SftpUser.Builder.newInstance() + .name(sftpUserName) + .password(sftpUserPassword) + .keyPair(sftpUserKeyPair) + .build(); + } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisionerTest.java b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisionerTest.java index c5657191f..4a47abcfc 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisionerTest.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/NoOpSftpProvisionerTest.java @@ -20,187 +20,207 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import java.util.concurrent.CompletableFuture; -import lombok.NoArgsConstructor; -import lombok.SneakyThrows; -import org.eclipse.edc.connector.transfer.spi.types.DeprovisionedResource; -import org.eclipse.edc.connector.transfer.spi.types.ProvisionResponse; import org.eclipse.edc.connector.transfer.spi.types.ProvisionedContentResource; import org.eclipse.edc.connector.transfer.spi.types.ResourceDefinition; import org.eclipse.edc.policy.engine.spi.PolicyEngine; import org.eclipse.edc.policy.model.Policy; -import org.eclipse.edc.spi.response.StatusResult; +import org.eclipse.edc.spi.result.AbstractResult; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static java.time.Duration.ofSeconds; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; class NoOpSftpProvisionerTest { - private final String policyScope = "scope"; - private final PolicyEngine policyEngine = Mockito.mock(PolicyEngine.class); - private final NoOpSftpProvider sftpProvider = new NoOpSftpProvider(); - - private final NoOpSftpProvisioner provisioner = - new NoOpSftpProvisioner(policyScope, policyEngine, sftpProvider); - - @Test - void canProvision__true() { - String provisionType = "NoOp"; - SftpUser sftpUser = SftpUser.builder().name("name").password("password").build(); - SftpLocation sftpLocation = SftpLocation.builder().host("host").port(22).path("path").build(); - SftpDataAddress dataAddress = new SftpDataAddress(sftpUser, sftpLocation); - SftpProviderResourceDefinition resourceDefinition = - new SftpProviderResourceDefinition(provisionType, dataAddress); - - Assertions.assertTrue(provisioner.canProvision(resourceDefinition)); - } - - @Test - void canProvision__falseProvisionType() { - String provisionType = "AmazonS3"; - SftpUser sftpUser = SftpUser.builder().name("name").password("password").build(); - SftpLocation sftpLocation = SftpLocation.builder().host("host").port(22).path("path").build(); - SftpDataAddress dataAddress = new SftpDataAddress(sftpUser, sftpLocation); - SftpProviderResourceDefinition resourceDefinition = - new SftpProviderResourceDefinition(provisionType, dataAddress); - - Assertions.assertFalse(provisioner.canProvision(resourceDefinition)); - } - - @Test - void canProvision__falseDefinitionType() { - ResourceDefinition resourceDefinition = new WrongResourceDefinition(); - - Assertions.assertFalse(provisioner.canProvision(resourceDefinition)); - } - - @Test - void canDeprovision__true() { - String provisionType = "NoOp"; - SftpUser sftpUser = SftpUser.builder().name("name").password("password").build(); - SftpLocation sftpLocation = SftpLocation.builder().host("host").port(22).path("path").build(); - Policy scopedPolicy = Mockito.mock(Policy.class); - SftpDataAddress dataAddress = new SftpDataAddress(sftpUser, sftpLocation); - String provisionedResourceID = "resource"; - SftpProvisionedContentResource provisionedContentResource = - new SftpProvisionedContentResource( - provisionType, scopedPolicy, dataAddress, provisionedResourceID); - - Assertions.assertTrue(provisioner.canDeprovision(provisionedContentResource)); - } - - @Test - void canDeprovision__falseProvisionType() { - String provisionType = "AmazonS3"; - SftpUser sftpUser = SftpUser.builder().name("name").password("password").build(); - SftpLocation sftpLocation = SftpLocation.builder().host("host").port(22).path("path").build(); - Policy scopedPolicy = Mockito.mock(Policy.class); - SftpDataAddress dataAddress = new SftpDataAddress(sftpUser, sftpLocation); - String provisionedResourceID = "resource"; - SftpProvisionedContentResource provisionedContentResource = - new SftpProvisionedContentResource( - provisionType, scopedPolicy, dataAddress, provisionedResourceID); - - Assertions.assertFalse(provisioner.canDeprovision(provisionedContentResource)); - } - - @Test - void canDeprovision__falseDefinitionType() { - ProvisionedContentResource provisionedContentResource = new WrongProvisionedContentResource(); - - Assertions.assertFalse(provisioner.canDeprovision(provisionedContentResource)); - } - - @Test - @SneakyThrows - void provision__successful() { - String provisionType = "NoOp"; - SftpUser sftpUser = SftpUser.builder().name("name").password("password").build(); - SftpLocation sftpLocation = SftpLocation.builder().host("host").port(22).path("path").build(); - SftpDataAddress dataAddress = new SftpDataAddress(sftpUser, sftpLocation); - SftpProviderResourceDefinition resourceDefinition = - new SftpProviderResourceDefinition(provisionType, dataAddress); - Policy policy = Mockito.mock(Policy.class); - - Mockito.when(policyEngine.filter(policy, policyScope)).thenReturn(policy); - - CompletableFuture> future = - provisioner.provision(resourceDefinition, policy); - StatusResult result = future.get(); - - Assertions.assertTrue(result.succeeded()); - } - - @Test - @SneakyThrows - void provision__failedWrongProvisionType() { - String provisionType = "AmazonS3"; - SftpUser sftpUser = SftpUser.builder().name("name").password("password").build(); - SftpLocation sftpLocation = SftpLocation.builder().host("host").port(22).path("path").build(); - SftpDataAddress dataAddress = new SftpDataAddress(sftpUser, sftpLocation); - SftpProviderResourceDefinition resourceDefinition = - new SftpProviderResourceDefinition(provisionType, dataAddress); - Policy policy = Mockito.mock(Policy.class); - - Mockito.when(policyEngine.filter(policy, policyScope)).thenReturn(policy); - - CompletableFuture> future = - provisioner.provision(resourceDefinition, policy); - StatusResult result = future.get(); - - Assertions.assertTrue(result.failed()); - } - - @Test - @SneakyThrows - void deprovision__successful() { - String provisionType = "NoOp"; - SftpUser sftpUser = SftpUser.builder().name("name").password("password").build(); - SftpLocation sftpLocation = SftpLocation.builder().host("host").port(22).path("path").build(); - SftpDataAddress dataAddress = new SftpDataAddress(sftpUser, sftpLocation); - Policy policy = Mockito.mock(Policy.class); - String provisionedResourceID = "resource"; - SftpProvisionedContentResource provisionedContentResource = - new SftpProvisionedContentResource( - provisionType, policy, dataAddress, provisionedResourceID); - - CompletableFuture> future = - provisioner.deprovision(provisionedContentResource, policy); - StatusResult result = future.get(); - - Assertions.assertTrue(result.succeeded()); - } - - @Test - @SneakyThrows - void deprovision__failedWrongProvisionType() { - String provisionType = "AmazonS3"; - SftpUser sftpUser = SftpUser.builder().name("name").password("password").build(); - SftpLocation sftpLocation = SftpLocation.builder().host("host").port(22).path("path").build(); - SftpDataAddress dataAddress = new SftpDataAddress(sftpUser, sftpLocation); - Policy policy = Mockito.mock(Policy.class); - String provisionedResourceID = "resource"; - SftpProvisionedContentResource provisionedContentResource = - new SftpProvisionedContentResource( - provisionType, policy, dataAddress, provisionedResourceID); - - CompletableFuture> future = - provisioner.deprovision(provisionedContentResource, policy); - StatusResult result = future.get(); - - Assertions.assertTrue(result.failed()); - } - - @NoArgsConstructor - private static class WrongResourceDefinition extends ResourceDefinition { - @Override - public > B toBuilder() { - return null; + private final String policyScope = "scope"; + private final PolicyEngine policyEngine = mock(PolicyEngine.class); + private final NoOpSftpProvider sftpProvider = new NoOpSftpProvider(); + + private final NoOpSftpProvisioner provisioner = + new NoOpSftpProvisioner(policyScope, policyEngine, sftpProvider); + + @Test + void canProvision_true() { + var provisionType = "NoOp"; + var sftpUser = SftpUser.Builder.newInstance().name("name").password("password").build(); + var sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); + var dataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + var resourceDefinition = + new SftpProviderResourceDefinition(provisionType, dataAddress); + + Assertions.assertTrue(provisioner.canProvision(resourceDefinition)); + } + + @Test + void canProvision_falseProvisionType() { + var provisionType = "AmazonS3"; + SftpUser sftpUser = SftpUser.Builder.newInstance().name("name").password("password").build(); + SftpLocation sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); + var dataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + var resourceDefinition = + new SftpProviderResourceDefinition(provisionType, dataAddress); + + Assertions.assertFalse(provisioner.canProvision(resourceDefinition)); + } + + @Test + void canProvision_falseDefinitionType() { + ResourceDefinition resourceDefinition = new WrongResourceDefinition(); + + Assertions.assertFalse(provisioner.canProvision(resourceDefinition)); + } + + @Test + void canDeprovision_true() { + var provisionType = "NoOp"; + var sftpUser = SftpUser.Builder.newInstance().name("name").password("password").build(); + var sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); + var scopedPolicy = mock(Policy.class); + var dataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + var provisionedResourceId = "resource"; + var provisionedContentResource = + SftpProvisionedContentResource.Builder.newInstance() + .providerType(provisionType) + .scopedPolicy(scopedPolicy) + .transferProcessId(UUID.randomUUID().toString()) + .resourceDefinitionId(UUID.randomUUID().toString()) + .resourceName("test-resource") + .provisionedResourceId("test-resdef-id") + .sftpDataAddress(dataAddress) + .provisionedResourceId(provisionedResourceId) + .build(); + + assertThat(provisioner.canDeprovision(provisionedContentResource)).isTrue(); + } + + @Test + void canDeprovision_falseProvisionType() { + var provisionType = "AmazonS3"; + SftpUser sftpUser = SftpUser.Builder.newInstance().name("name").password("password").build(); + SftpLocation sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); + var scopedPolicy = mock(Policy.class); + var dataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + var provisionedResourceId = "resource"; + var provisionedContentResource = + SftpProvisionedContentResource.Builder.newInstance() + .providerType(provisionType) + .scopedPolicy(scopedPolicy) + .transferProcessId(UUID.randomUUID().toString()) + .resourceDefinitionId(UUID.randomUUID().toString()) + .resourceName("test-resource") + .provisionedResourceId("test-resdef-id") + .sftpDataAddress(dataAddress).provisionedResourceId(provisionedResourceId) + .build(); + + Assertions.assertFalse(provisioner.canDeprovision(provisionedContentResource)); + } + + @Test + void canDeprovision_falseDefinitionType() { + ProvisionedContentResource provisionedContentResource = new WrongProvisionedContentResource(); + + Assertions.assertFalse(provisioner.canDeprovision(provisionedContentResource)); + } + + @Test + void provision_successful() throws ExecutionException, InterruptedException { + var provisionType = "NoOp"; + SftpUser sftpUser = SftpUser.Builder.newInstance().name("name").password("password").build(); + SftpLocation sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); + var dataAddress = SftpDataAddress.Builder.newInstance().type("sftp").sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + var resourceDefinition = new SftpProviderResourceDefinition(provisionType, dataAddress); + var policy = mock(Policy.class); + + when(policyEngine.filter(policy, policyScope)).thenReturn(policy); + + var future = provisioner.provision(resourceDefinition, policy); + assertThat(future).succeedsWithin(ofSeconds(5)); + assertThat(future).isCompletedWithValueMatching(AbstractResult::succeeded); + } + + @Test + void provision_failedWrongProvisionType() { + var provisionType = "AmazonS3"; + SftpUser sftpUser = SftpUser.Builder.newInstance().name("name").password("password").build(); + SftpLocation sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); + var dataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + var resourceDefinition = + new SftpProviderResourceDefinition(provisionType, dataAddress); + var policy = mock(Policy.class); + + when(policyEngine.filter(policy, policyScope)).thenReturn(policy); + + var future = + provisioner.provision(resourceDefinition, policy); + + assertThat(future).succeedsWithin(ofSeconds(5)); + assertThat(future).isCompletedWithValueMatching(AbstractResult::failed); } - } - @NoArgsConstructor - private static class WrongProvisionedContentResource extends ProvisionedContentResource {} + @Test + void deprovision_successful() { + var provisionType = "NoOp"; + var sftpUser = SftpUser.Builder.newInstance().name("name").password("password").build(); + var sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); + var dataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + var policy = mock(Policy.class); + var provisionedResourceId = "resource"; + var provisionedContentResource = SftpProvisionedContentResource.Builder.newInstance() + .providerType(provisionType) + .scopedPolicy(policy) + .sftpDataAddress(dataAddress) + .provisionedResourceId(provisionedResourceId) + .transferProcessId(UUID.randomUUID().toString()) + .resourceDefinitionId(UUID.randomUUID().toString()) + .resourceName("test-resource") + .provisionedResourceId("test-resdef-id") + .build(); + var future = provisioner.deprovision(provisionedContentResource, policy); + + assertThat(future).succeedsWithin(ofSeconds(5)); + assertThat(future).isCompletedWithValueMatching(AbstractResult::succeeded); + + } + + @Test + void deprovision_failedWrongProvisionType() { + var provisionType = "AmazonS3"; + var sftpUser = SftpUser.Builder.newInstance().name("name").password("password").build(); + var sftpLocation = SftpLocation.Builder.newInstance().host("host").port(22).path("path").build(); + var dataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + var policy = mock(Policy.class); + var provisionedResourceId = "resource"; + var provisionedContentResource = + SftpProvisionedContentResource.Builder.newInstance() + .providerType(provisionType) + .scopedPolicy(policy) + .sftpDataAddress(dataAddress).provisionedResourceId(provisionedResourceId) + .transferProcessId(UUID.randomUUID().toString()) + .resourceDefinitionId(UUID.randomUUID().toString()) + .resourceName("test-resource") + .provisionedResourceId("test-resdef-id") + .build(); + + var future = + provisioner.deprovision(provisionedContentResource, policy); + assertThat(future).isCompletedWithValueMatching(AbstractResult::failed); + } + + private static class WrongResourceDefinition extends ResourceDefinition { + @Override + public > B toBuilder() { + return null; + } + } + + private static class WrongProvisionedContentResource extends ProvisionedContentResource { + } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpLocationFactoryImplTest.java b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpLocationFactoryImplTest.java index afca5d5ac..2b4a11bfe 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpLocationFactoryImplTest.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpLocationFactoryImplTest.java @@ -25,18 +25,18 @@ import org.junit.jupiter.api.Test; class SftpLocationFactoryImplTest { - private final SftpLocationFactoryImpl sftpLocationFactoryImpl = new SftpLocationFactoryImpl(); + private final SftpLocationFactoryImpl sftpLocationFactoryImpl = new SftpLocationFactoryImpl(); - @Test - void generateSftpLocation() { - final String host = "host"; - final Integer port = 22; - final String path = "path"; + @Test + void generateSftpLocation() { + final String host = "host"; + final Integer port = 22; + final String path = "path"; - final SftpLocation location = sftpLocationFactoryImpl.createSftpLocation(host, port, path); + final SftpLocation location = sftpLocationFactoryImpl.createSftpLocation(host, port, path); - Assertions.assertEquals(host, location.getHost()); - Assertions.assertEquals(port, location.getPort()); - Assertions.assertEquals(path, location.getPath()); - } + Assertions.assertEquals(host, location.getHost()); + Assertions.assertEquals(port, location.getPort()); + Assertions.assertEquals(path, location.getPath()); + } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGeneratorTest.java b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGeneratorTest.java index d81252705..02366eef2 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGeneratorTest.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpProviderResourceDefinitionGeneratorTest.java @@ -20,75 +20,81 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import static org.eclipse.tractusx.edc.transferprocess.sftp.provisioner.NoOpSftpProvisioner.DATA_ADDRESS_TYPE; -import static org.eclipse.tractusx.edc.transferprocess.sftp.provisioner.NoOpSftpProvisioner.PROVIDER_TYPE; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import lombok.SneakyThrows; import org.eclipse.edc.connector.transfer.spi.types.DataRequest; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.types.domain.DataAddress; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpDataAddress; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpLocation; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; + +import static org.eclipse.tractusx.edc.transferprocess.sftp.provisioner.NoOpSftpProvisioner.DATA_ADDRESS_TYPE; +import static org.eclipse.tractusx.edc.transferprocess.sftp.provisioner.NoOpSftpProvisioner.PROVIDER_TYPE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + + class SftpProviderResourceDefinitionGeneratorTest { - private final SftpProviderResourceDefinitionGenerator generator = - new SftpProviderResourceDefinitionGenerator(); - - @Test - void generate__successful() { - final String name = "name"; - final String password = "password"; - final KeyPair keyPair = generateKeyPair(); - final String host = "host"; - final Integer port = 22; - final String path = "path"; - - final DataRequest dataRequest = - DataRequest.Builder.newInstance().destinationType(DATA_ADDRESS_TYPE).build(); - final SftpUser sftpUser = - SftpUser.builder().name(name).password(password).keyPair(keyPair).build(); - final SftpLocation sftpLocation = - SftpLocation.builder().host(host).port(port).path(path).build(); - final DataAddress dataAddress = new SftpDataAddress(sftpUser, sftpLocation); - final Policy policy = Policy.Builder.newInstance().build(); - - final SftpProviderResourceDefinition resourceDefinition = - (SftpProviderResourceDefinition) generator.generate(dataRequest, dataAddress, policy); - - Assertions.assertNotNull(resourceDefinition); - final SftpDataAddress sftpDataAddress = resourceDefinition.getSftpDataAddress(); - - Assertions.assertEquals(PROVIDER_TYPE, resourceDefinition.getProviderType()); - Assertions.assertEquals(host, sftpDataAddress.getSftpLocation().getHost()); - Assertions.assertEquals(port, sftpDataAddress.getSftpLocation().getPort()); - Assertions.assertEquals(path, sftpDataAddress.getSftpLocation().getPath()); - Assertions.assertEquals(name, sftpDataAddress.getSftpUser().getName()); - Assertions.assertEquals(password, sftpDataAddress.getSftpUser().getPassword()); - Assertions.assertEquals(keyPair, sftpDataAddress.getSftpUser().getKeyPair()); - } - - @Test - void generate__wrongDataAddressType() { - final DataRequest dataRequest = - DataRequest.Builder.newInstance().destinationType(DATA_ADDRESS_TYPE).build(); - final DataAddress dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); - final Policy policy = Policy.Builder.newInstance().build(); - - final SftpProviderResourceDefinition resourceDefinition = - (SftpProviderResourceDefinition) generator.generate(dataRequest, dataAddress, policy); - - Assertions.assertNull(resourceDefinition); - } - - @SneakyThrows - static KeyPair generateKeyPair() { - final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(2048); - return keyPairGenerator.generateKeyPair(); - } + private final SftpProviderResourceDefinitionGenerator generator = + new SftpProviderResourceDefinitionGenerator(); + + static KeyPair generateKeyPair() { + try { + var keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + return keyPairGenerator.generateKeyPair(); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + @Test + void generate_successful() throws NoSuchAlgorithmException { + var name = "name"; + var password = "password"; + var keyPair = generateKeyPair(); + var host = "host"; + var port = 22; + var path = "path"; + + var dataRequest = DataRequest.Builder.newInstance().destinationType(DATA_ADDRESS_TYPE).build(); + var sftpUser = SftpUser.Builder.newInstance().name(name).password(password).keyPair(keyPair).build(); + var sftpLocation = + SftpLocation.Builder.newInstance().host(host).port(port).path(path).build(); + final DataAddress dataAddress = SftpDataAddress.Builder.newInstance().sftpUser(sftpUser).sftpLocation(sftpLocation).build(); + var policy = Policy.Builder.newInstance().build(); + + var resourceDefinition = + (SftpProviderResourceDefinition) generator.generate(dataRequest, dataAddress, policy); + + assertNotNull(resourceDefinition); + var sftpDataAddress = resourceDefinition.getSftpDataAddress(); + + assertEquals(PROVIDER_TYPE, resourceDefinition.getProviderType()); + assertEquals(host, sftpDataAddress.getSftpLocation().getHost()); + assertEquals(port, sftpDataAddress.getSftpLocation().getPort()); + assertEquals(path, sftpDataAddress.getSftpLocation().getPath()); + assertEquals(name, sftpDataAddress.getSftpUser().getName()); + assertEquals(password, sftpDataAddress.getSftpUser().getPassword()); + + } + + @Test + void generate_wrongDataAddressType() { + var dataRequest = + DataRequest.Builder.newInstance().destinationType(DATA_ADDRESS_TYPE).build(); + var dataAddress = DataAddress.Builder.newInstance().type("wrong").build(); + var policy = Policy.Builder.newInstance().build(); + + var resourceDefinition = + (SftpProviderResourceDefinition) generator.generate(dataRequest, dataAddress, policy); + + assertNull(resourceDefinition); + } } diff --git a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpUserFactoryImplTest.java b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpUserFactoryImplTest.java index 29a456176..a4fe07398 100644 --- a/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpUserFactoryImplTest.java +++ b/edc-extensions/transferprocess-sftp-provisioner/src/test/java/org/eclipse/tractusx/edc/transferprocess/sftp/provisioner/SftpUserFactoryImplTest.java @@ -20,32 +20,32 @@ package org.eclipse.tractusx.edc.transferprocess.sftp.provisioner; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import lombok.SneakyThrows; import org.eclipse.tractusx.edc.transferprocess.sftp.common.SftpUser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; + class SftpUserFactoryImplTest { - private final SftpUserFactoryImpl sftpUserFactoryImpl = new SftpUserFactoryImpl(); + private final SftpUserFactoryImpl sftpUserFactoryImpl = new SftpUserFactoryImpl(); - @Test - @SneakyThrows - void generateSftpLocation() { - final String name = "name"; - final String password = "password"; + @Test + void generateSftpLocation() throws NoSuchAlgorithmException { + final String name = "name"; + final String password = "password"; - final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(2048); - final KeyPair keyPair = keyPairGenerator.generateKeyPair(); - final byte[] privateKeyBytes = keyPair.getPrivate().getEncoded(); + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + final KeyPair keyPair = keyPairGenerator.generateKeyPair(); + final byte[] privateKeyBytes = keyPair.getPrivate().getEncoded(); - final SftpUser sftpUser = sftpUserFactoryImpl.createSftpUser(name, password, privateKeyBytes); + final SftpUser sftpUser = sftpUserFactoryImpl.createSftpUser(name, password, privateKeyBytes); - Assertions.assertEquals(name, sftpUser.getName()); - Assertions.assertEquals(password, sftpUser.getPassword()); + Assertions.assertEquals(name, sftpUser.getName()); + Assertions.assertEquals(password, sftpUser.getPassword()); - Assertions.assertArrayEquals(privateKeyBytes, sftpUser.getKeyPair().getPrivate().getEncoded()); - } + Assertions.assertArrayEquals(privateKeyBytes, sftpUser.getKeyPair().getPrivate().getEncoded()); + } } diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/README.md b/edc-tests/deployment/src/main/resources/helm/omejdn/README.md deleted file mode 100644 index f85a94889..000000000 --- a/edc-tests/deployment/src/main/resources/helm/omejdn/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Omejdn DAPS - -This chart deployes an [IDS Omejdn DAPS](https://github.com/Fraunhofer-AISEC/omejdn-server). - -Two Eclipse Dataspace Connectors need to be registered at the same DAPS instance, to be able to talk to each other. Each connector is registered in the DAPS by an unique client ID and a correpsonding client certificate. - -New connectors are configured in the omejdn _values.yaml_. - -In each Eclipse Dataspace Connector configure the following properties to use the DAPS. - -```properties - edc.oauth.client.id= - - edc.oauth.provider.jwks.url="http://:4567/.well-known/jwks.json" - edc.oauth.token.url="http://:4567/token" - - edc.oauth.private.key.alias= - edc.oauth.public.key.alias= - - edc.oauth.provider.audience=idsc:IDS_CONNECTORS_ALL -``` diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.gitignore b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.gitignore deleted file mode 100644 index 8681aba50..000000000 --- a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# ignore downloaded helm depdencies -charts/ - -Chart.lock diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/README.md b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/README.md deleted file mode 100644 index e927d7bc5..000000000 --- a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Supporting Infrastructure Deployment - -The Supporting Infrastructure Deployment creates a complete, independent and already configured EDC test environment. -During the automated business tests, these infrastructure components are deployed together with two connectors (Plato & Sokrates). - -This deployment could also be used as - -- reference setup for teams, that want to create their own connector -- standalone infrastructure to try things out - -This deployment should **never** be used - -- in **any** production or near production environments -- in **any** long living internet facing connector setups - -## Omejdn DAPS - -The Dynamic Attribute Provisioning Service (DAPS) is a component of the IDS Ecosystem. -The Fraunhofer Institute has created a DAPS reference implementation, the Omejdn -DAPS ([link](https://github.com/Fraunhofer-AISEC/omejdn-server)). This deplyoment configures and deployes a instance of -this reference implementation. - -Definition of DAPS from the IDS Reference architecture v3.0: - -> The Identity Provider acts as an agent for the International -> Data Spaces Association. It is responsible for issuing technical identities to parties that have been approved to become -> Participants in the International Data Spaces. The Identity -> Provider is instructed to issue identities based on approved -> roles (e.g., App Store or App Provider). Only if equipped with -> such an identity, an entity is allowed to participate in the International Data Spaces - -Also, please note, that the Omejdn DAPS is meant as research sandbox and should not be used in anq -productive environment. - -> **IMPORTANT:** Omejdn is meant to be a research sandbox in which we can (re)implement standard protocols and -> potentially extend and modify functionality under the hood to support research projects. Use at your own -> risk! ([source](https://github.com/Fraunhofer-AISEC/omejdn-server)) - -## HashiCorp Vault - -The Control- and Data Plane persist confidential in the vault and persist and communicate using only the secret -names. Hence, it is not possible to run a connector without an instance of a vault. - -## PostgreSQL - -This database is used to persist the state of the Control Plane. - -## Setup - -Simply execute the following comment in a shell: - -```shell -helm install infra edc-tests/deployment/src/main/resources/helm/test-infrastructure --update-dependencies -``` diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml deleted file mode 100644 index 30e89ee38..000000000 --- a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml +++ /dev/null @@ -1,553 +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 -# - ---- - -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -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: {} -runtime: - 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/ids - # -- 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 - # -- observability api with unsecured access, must not be internet facing - observability: - # -- port for incoming API calls - port: 8085 - # -- observability api, provides /health /readiness and /liveness endpoints - path: /observability - # -- allow or disallow insecure access, i.e. access without authentication - insecure: 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: - - ids - # -- 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 - observability: - # -- port for incoming API calls - port: 8085 - # -- observability api, provides /health /readiness and /liveness endpoints - path: /observability - # -- allow or disallow insecure access, i.e. access without authentication - insecure: true - 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: - enabled: false - jdbcUrl: "" - username: "" - password: "" - vault: - hashicorp: - enabled: true - url: "" - 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: "" - clientId: "" - paths: - jwks: /jwks.json - token: /token - backendService: - httpProxyTokenReceiverUrl: "" - 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: [] -######## -# DAPS # -######## -idsdaps: - fullnameOverride: "ids-daps" - 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! -############## -# POSTGRESQL # -############## -postgresql: - fullnameOverride: "postgresql" - primary: - persistence: - enabled: false - readReplicas: - persistence: - enabled: false - auth: - database: "edc" - username: "user" - password: "password" -######### -# VAULT # -######### -vault: - fullnameOverride: "vault" - injector: - enabled: false - server: - dev: - enabled: true - devRootToken: "root" - # Must be the same certificate that is configured in section 'ids-daps' - postStart: # must be set externally! diff --git a/edc-tests/deployment/src/main/resources/helm/tractusx-connector-azure-vault-test.yaml b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-azure-vault-test.yaml index bc247f91e..b2c0c3db9 100644 --- a/edc-tests/deployment/src/main/resources/helm/tractusx-connector-azure-vault-test.yaml +++ b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-azure-vault-test.yaml @@ -38,6 +38,9 @@ fullnameOverride: tx-prod ################################ # EDC ControlPlane + DataPlane # ################################ +participant: + id: "test-participant" + controlplane: service: type: NodePort @@ -92,7 +95,7 @@ vault: secrets: daps: - url: "http://ids-daps:4567" + url: "http://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: diff --git a/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml index 05ba6cef2..5f2300a53 100644 --- a/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml +++ b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml @@ -22,6 +22,10 @@ fullnameOverride: tx-prod ################################ # EDC ControlPlane + DataPlane # ################################ + +participant: + id: "test-participant" + controlplane: service: type: NodePort @@ -73,7 +77,7 @@ vault: secrets: daps: - url: "http://ids-daps:4567" + url: "http://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: diff --git a/edc-tests/e2e-tests/build.gradle.kts b/edc-tests/e2e-tests/build.gradle.kts index 237c134d9..63181a7ca 100644 --- a/edc-tests/e2e-tests/build.gradle.kts +++ b/edc-tests/e2e-tests/build.gradle.kts @@ -17,6 +17,7 @@ plugins { } dependencies { + testImplementation(project(":spi:edr-cache-spi")) testImplementation(project(":edc-extensions:control-plane-adapter-api")) testImplementation(libs.okhttp.mockwebserver) testImplementation(libs.restAssured) @@ -35,6 +36,11 @@ dependencies { testImplementation(libs.edc.spi.dataplane.selector) testImplementation(libs.edc.ext.jsonld) testImplementation(libs.edc.dsp) + testImplementation(testFixtures(libs.edc.sql.core)) + + testCompileOnly(project(":edc-tests:runtime:extensions")) + testCompileOnly(project(":edc-tests:runtime:runtime-memory")) + testCompileOnly(project(":edc-tests:runtime:runtime-postgresql")) } // do not publish diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractDefinitionHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractDefinitionHelperFunctions.java index 4377949ce..bdf80342b 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractDefinitionHelperFunctions.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractDefinitionHelperFunctions.java @@ -22,14 +22,14 @@ import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; public class ContractDefinitionHelperFunctions { - + public static JsonObject createContractDefinition(String assetId, String definitionId, String accessPolicyId, String contractPolicyId) { return Json.createObjectBuilder() .add(ID, definitionId) .add(TYPE, EDC_NAMESPACE + "ContractDefinition") .add(EDC_NAMESPACE + "accessPolicyId", accessPolicyId) .add(EDC_NAMESPACE + "contractPolicyId", contractPolicyId) - .add(EDC_NAMESPACE + "criteria", Json.createArrayBuilder() + .add(EDC_NAMESPACE + "assetsSelector", Json.createArrayBuilder() .add(Json.createObjectBuilder() .add(TYPE, "CriterionDto") .add(EDC_NAMESPACE + "operandLeft", EDC_NAMESPACE + "id") diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractNegotiationHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractNegotiationHelperFunctions.java index d11e176e7..1665173a7 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractNegotiationHelperFunctions.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ContractNegotiationHelperFunctions.java @@ -27,7 +27,7 @@ public class ContractNegotiationHelperFunctions { - private final static JsonLd jsonLd = new TitaniumJsonLd(mock(Monitor.class)); + private static final JsonLd JSON_LD = new TitaniumJsonLd(mock(Monitor.class)); public static JsonObject createNegotiationRequest(String connectorAddress, String providerId, String offerId, String assetId, JsonObject policy) { return Json.createObjectBuilder() @@ -39,7 +39,7 @@ public static JsonObject createNegotiationRequest(String connectorAddress, Strin .add(EDC_NAMESPACE + "offer", Json.createObjectBuilder() .add(EDC_NAMESPACE + "offerId", offerId) .add(EDC_NAMESPACE + "assetId", assetId) - .add(EDC_NAMESPACE + "policy", jsonLd.compact(policy).getContent()) + .add(EDC_NAMESPACE + "policy", JSON_LD.compact(policy).getContent()) ) .build(); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java index 7b0f85d3a..bc4bb22a7 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 @@ -20,7 +20,9 @@ import jakarta.json.JsonObject; import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.spi.event.Event; import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto; import java.util.Set; @@ -31,11 +33,11 @@ public class EdrNegotiationHelperFunctions { - private final static JsonLd jsonLd = new TitaniumJsonLd(mock(Monitor.class)); + private static final JsonLd JSON_LD = new TitaniumJsonLd(mock(Monitor.class)); public static JsonObject createEdrNegotiationRequest(String connectorAddress, String providerId, String offerId, String assetId, JsonObject policy, JsonArray callbacks) { return Json.createObjectBuilder() - .add(TYPE, EDC_NAMESPACE + "NegotiateEdrRequestDto") + .add(TYPE, NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE) .add(EDC_NAMESPACE + "connectorId", providerId) .add(EDC_NAMESPACE + "providerId", providerId) .add(EDC_NAMESPACE + "connectorAddress", connectorAddress) @@ -43,7 +45,7 @@ public static JsonObject createEdrNegotiationRequest(String connectorAddress, St .add(EDC_NAMESPACE + "offer", Json.createObjectBuilder() .add(EDC_NAMESPACE + "offerId", offerId) .add(EDC_NAMESPACE + "assetId", assetId) - .add(EDC_NAMESPACE + "policy", jsonLd.compact(policy).getContent()) + .add(EDC_NAMESPACE + "policy", JSON_LD.compact(policy).getContent()) ) .add(EDC_NAMESPACE + "callbackAddresses", callbacks) .build(); @@ -61,4 +63,7 @@ public static JsonObject createCallback(String url, boolean transactional, Set ReceivedEvent createEvent(Class klass) { + return ReceivedEvent.Builder.newInstance().type(klass.getSimpleName()).build(); + } } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java index 7ff41e964..180900841 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 @@ -40,7 +40,7 @@ public class PolicyHelperFunctions { - private static final String BUSINESS_PARTNER_EVALUATION_KEY = EDC_NAMESPACE + "BusinessPartnerNumber"; + private static final String BUSINESS_PARTNER_EVALUATION_KEY = "BusinessPartnerNumber"; /** * Creates a {@link PolicyDefinition} using the given ID, that contains equality constraints for each of the given BusinessPartnerNumbers: 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 df70cbab5..74bce1f90 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,13 +17,13 @@ import jakarta.json.Json; import jakarta.json.JsonObject; -import static org.eclipse.edc.api.query.QuerySpecDto.EDC_QUERY_SPEC_LIMIT; -import static org.eclipse.edc.api.query.QuerySpecDto.EDC_QUERY_SPEC_OFFSET; -import static org.eclipse.edc.api.query.QuerySpecDto.EDC_QUERY_SPEC_TYPE; +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; public class QueryHelperFunctions { - + public static JsonObject createQuery(int limit, int offset) { return Json.createObjectBuilder() .add(TYPE, EDC_QUERY_SPEC_TYPE) diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ReceivedEvent.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ReceivedEvent.java new file mode 100644 index 000000000..55d012cc2 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/ReceivedEvent.java @@ -0,0 +1,56 @@ +/* + * 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.helpers; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + + +@JsonIgnoreProperties(ignoreUnknown = true) +public class ReceivedEvent { + private String type; + + public String getType() { + return type; + } + + @Override + public String toString() { + return "ReceivedEvent{" + + "type='" + type + '\'' + + '}'; + } + + public static class Builder { + private final ReceivedEvent event; + + private Builder(ReceivedEvent event) { + this.event = event; + } + + public static Builder newInstance() { + return new Builder(new ReceivedEvent()); + } + + public Builder type(String type) { + this.event.type = type; + return this; + } + + public ReceivedEvent build() { + return event; + } + } +} + 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 45bfba659..6910eeda6 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,6 +19,7 @@ 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 java.util.stream.Collectors; @@ -37,6 +38,7 @@ public void clearPersistence() { clearAssetIndex(); clearPolicies(); clearContractDefinitions(); + clearEdrCache(); } public void clearContractDefinitions() { @@ -54,4 +56,9 @@ public void clearAssetIndex() { var index = context.getService(AssetIndex.class); index.queryAssets(QuerySpec.max()).forEach(asset -> index.deleteById(asset.getId())); } + + public void clearEdrCache() { + var edrCache = context.getService(EndpointDataReferenceCache.class); + edrCache.queryForEntries(QuerySpec.max()).forEach(entry -> edrCache.deleteByTransferProcessId(entry.getTransferProcessId())); + } } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java deleted file mode 100644 index 404bfbe1d..000000000 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.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.lifecycle; - - -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.util.HashMap; - -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.DSP_PATH; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_CONNECTOR_PATH; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_CONNECTOR_PORT; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_DATAPLANE_CONTROL_PORT; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_DSP_API_PORT; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_DSP_CALLBACK; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_MANAGEMENT_PATH; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_MANAGEMENT_PORT; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_PUBLIC_API_PORT; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_CONNECTOR_PATH; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_CONNECTOR_PORT; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_DATAPLANE_CONTROL_PORT; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_DSP_API_PORT; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_DSP_CALLBACK; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_MANAGEMENT_PATH; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_MANAGEMENT_PORT; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_PUBLIC_API_PORT; - - -public class MultiRuntimeTest { - public static final String BPN_SUFFIX = "-BPN"; - public static final String SOKRATES_NAME = "SOKRATES"; - public static final String SOKRATES_BPN = SOKRATES_NAME + BPN_SUFFIX; - - @RegisterExtension - protected static Participant sokrates = new Participant( - ":edc-tests:runtime", - SOKRATES_NAME, - SOKRATES_BPN, - new HashMap<>() { - { - put("edc.connector.name", "sokrates"); - put("edc.participant.id", SOKRATES_BPN); - put("web.http.port", String.valueOf(SOKRATES_CONNECTOR_PORT)); - put("web.http.path", SOKRATES_CONNECTOR_PATH); - put("web.http.management.port", String.valueOf(SOKRATES_MANAGEMENT_PORT)); - put("web.http.management.path", SOKRATES_MANAGEMENT_PATH); - put("web.http.protocol.port", String.valueOf(SOKRATES_DSP_API_PORT)); - put("web.http.protocol.path", DSP_PATH); - put("edc.dsp.callback.address", SOKRATES_DSP_CALLBACK); - put("edc.api.auth.key", "testkey"); - put("web.http.public.path", "/api/public"); - put("web.http.public.port", SOKRATES_PUBLIC_API_PORT); - - // embedded dataplane config - put("web.http.control.path", "/api/dataplane/control"); - put("web.http.control.port", SOKRATES_DATAPLANE_CONTROL_PORT); - put("edc.dataplane.token.validation.endpoint", "http://localhost:" + SOKRATES_DATAPLANE_CONTROL_PORT + "/api/dataplane/control/token"); - put("edc.dataplane.selector.httpplane.url", "http://localhost:" + SOKRATES_DATAPLANE_CONTROL_PORT + "/api/dataplane/control"); - put("edc.dataplane.selector.httpplane.sourcetypes", "HttpData"); - put("edc.dataplane.selector.httpplane.destinationtypes", "HttpProxy"); - put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + SOKRATES_PUBLIC_API_PORT + "/api/public\"}"); - put("edc.receiver.http.dynamic.endpoint", "http://localhost:" + SOKRATES_CONNECTOR_PORT + "/api/consumer/datareference"); - put("tractusx.businesspartnervalidation.log.agreement.validation", "true"); - put("edc.agent.identity.key", "BusinessPartnerNumber"); - } - }); - public static final String PLATO_NAME = "PLATO"; - public static final String PLATO_BPN = PLATO_NAME + BPN_SUFFIX; - - @RegisterExtension - protected static Participant plato = new Participant( - ":edc-tests:runtime", - PLATO_NAME, - PLATO_BPN, - new HashMap<>() { - { - put("edc.connector.name", "plato"); - put("edc.participant.id", PLATO_BPN); - put("web.http.default.port", String.valueOf(PLATO_CONNECTOR_PORT)); - put("web.http.default.path", PLATO_CONNECTOR_PATH); - put("web.http.management.port", String.valueOf(PLATO_MANAGEMENT_PORT)); - put("web.http.management.path", PLATO_MANAGEMENT_PATH); - put("web.http.protocol.port", String.valueOf(PLATO_DSP_API_PORT)); - put("web.http.protocol.path", DSP_PATH); - put("edc.dsp.callback.address", PLATO_DSP_CALLBACK); - put("edc.api.auth.key", "testkey"); - put("web.http.public.port", PLATO_PUBLIC_API_PORT); - put("web.http.public.path", "/api/public"); - // embedded dataplane config - put("web.http.control.path", "/api/dataplane/control"); - put("web.http.control.port", PLATO_DATAPLANE_CONTROL_PORT); - put("edc.dataplane.token.validation.endpoint", "http://localhost:" + PLATO_DATAPLANE_CONTROL_PORT + "/api/dataplane/control/token"); - put("edc.dataplane.selector.httpplane.url", "http://localhost:" + PLATO_DATAPLANE_CONTROL_PORT + "/api/dataplane/control"); - put("edc.dataplane.selector.httpplane.sourcetypes", "HttpData"); - put("edc.dataplane.selector.httpplane.destinationtypes", "HttpProxy"); - put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + PLATO_PUBLIC_API_PORT + "/api/public\"}"); - put("tractusx.businesspartnervalidation.log.agreement.validation", "true"); - put("edc.agent.identity.key", "BusinessPartnerNumber"); - } - }); -} 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 e2546035f..7f77f1726 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 @@ -15,6 +15,7 @@ package org.eclipse.tractusx.edc.lifecycle; import com.fasterxml.jackson.databind.ObjectMapper; +import io.restassured.response.Response; import io.restassured.specification.RequestSpecification; import jakarta.json.Json; import jakarta.json.JsonArray; @@ -23,26 +24,14 @@ 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.extensions.EdcRuntimeExtension; -import org.eclipse.edc.policy.model.PolicyRegistrationTypes; import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.injection.InjectionContainer; -import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.eclipse.tractusx.edc.helpers.AssetHelperFunctions; import org.eclipse.tractusx.edc.helpers.ContractDefinitionHelperFunctions; -import org.eclipse.tractusx.edc.token.MockDapsService; -import org.junit.jupiter.api.extension.AfterAllCallback; -import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.ExtensionContext; import java.net.URI; import java.time.Duration; -import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; @@ -63,12 +52,16 @@ import static org.eclipse.tractusx.edc.helpers.TransferProcessHelperFunctions.createTransferRequest; import static org.mockito.Mockito.mock; -public class Participant extends EdcRuntimeExtension implements BeforeAllCallback, AfterAllCallback { +public class Participant { + + private static final String PROXY_SUBPATH = "proxy/aas/request"; private final String managementUrl; private final String apiKey; private final String dspEndpoint; - private final TypeManager typeManager = new TypeManager(); + + private final String gatewayEndpoint; + private final String runtimeName; private final String bpn; private final String backend; @@ -76,42 +69,18 @@ public class Participant extends EdcRuntimeExtension implements BeforeAllCallbac private final Duration timeout = Duration.ofSeconds(30); private final ObjectMapper objectMapper = JacksonJsonLd.createObjectMapper(); + private final String proxyUrl; - private DataWiper wiper; - - public Participant(String moduleName, String runtimeName, String bpn, Map properties) { - super(moduleName, runtimeName, properties); + public Participant(String runtimeName, String bpn, Map properties) { this.managementUrl = URI.create(format("http://localhost:%s%s", properties.get("web.http.management.port"), properties.get("web.http.management.path"))).toString(); this.dspEndpoint = URI.create(format("http://localhost:%s%s", properties.get("web.http.protocol.port"), properties.get("web.http.protocol.path"))).toString(); this.apiKey = properties.get("edc.api.auth.key"); + this.gatewayEndpoint = URI.create(format("http://localhost:%s/api/gateway", properties.get("web.http.port"))).toString(); + this.proxyUrl = URI.create(format("http://localhost:%s", properties.get("tx.dpf.consumer.proxy.port"))).toString(); this.bpn = bpn; this.runtimeName = runtimeName; this.backend = properties.get("edc.receiver.http.dynamic.endpoint"); - this.registerServiceMock(IdentityService.class, new MockDapsService(getBpn())); jsonLd = new TitaniumJsonLd(mock(Monitor.class)); - typeManager.registerTypes(PolicyRegistrationTypes.TYPES.toArray(Class[]::new)); - - } - - @Override - public void beforeTestExecution(ExtensionContext extensionContext) { - //do nothing - we only want to start the runtime once - wiper.clearPersistence(); - } - - @Override - public void afterTestExecution(ExtensionContext context) { - } - - @Override - public void beforeAll(ExtensionContext context) throws Exception { - //only run this once - super.beforeTestExecution(context); - } - - @Override - public void afterAll(ExtensionContext context) throws Exception { - super.afterTestExecution(context); } /** @@ -189,7 +158,7 @@ public String negotiateContract(Participant other, String assetId) { return response.extract().jsonPath().getString(ID); } - public void negotiateEdr(Participant other, String assetId, JsonArray callbacks) { + public String negotiateEdr(Participant other, String assetId, JsonArray callbacks) { var dataset = getDatasetForAsset(other, assetId); assertThat(dataset).withFailMessage("Catalog received from " + other.runtimeName + " was empty!").isNotEmpty(); @@ -208,6 +177,7 @@ public void negotiateEdr(Participant other, String assetId, JsonArray callbacks) var body = response.extract().body().asString(); assertThat(response.extract().statusCode()).withFailMessage(body).isBetween(200, 299); + return response.extract().jsonPath().getString(ID); } public String getNegotiationState(String negotiationId) { @@ -233,6 +203,39 @@ private String getContractNegotiationField(String negotiationId, String fieldNam .getString(format("'edc:%s'", fieldName)); } + public JsonObject getEdr(String transferProcessId) { + return baseRequest() + .when() + .get("/adapter/edrs/{id}", transferProcessId) + .then() + .statusCode(200) + .extract() + .body() + .as(JsonObject.class); + } + + public JsonArray getEdrEntriesByAssetId(String assetId) { + return baseRequest() + .when() + .get("/adapter/edrs?assetId={assetId}", assetId) + .then() + .statusCode(200) + .extract() + .body() + .as(JsonArray.class); + } + + public JsonArray getEdrEntriesByAgreementId(String agreementId) { + return baseRequest() + .when() + .get("/adapter/edrs?agreementId={agreementId}", agreementId) + .then() + .statusCode(200) + .extract() + .body() + .as(JsonArray.class); + } + /** * Returns this participant's BusinessPartnerNumber (=BPN). This is constructed of the runtime name plus "-BPN" @@ -325,6 +328,38 @@ public JsonArray getCatalogDatasets(Participant provider, JsonObject querySpec) return datasetReference.get(); } + public String pullProxyDataByAssetId(Participant provider, String assetId) { + var body = Map.of("assetId", assetId, "endpointUrl", format("%s/aas/test", provider.gatewayEndpoint)); + return getProxyData(body); + } + + public Response pullProxyDataResponseByAssetId(Participant provider, String assetId) { + var body = Map.of("assetId", assetId, "endpointUrl", format("%s/aas/test", provider.gatewayEndpoint)); + return proxyRequest(body); + } + + public String pullProxyDataByTransferProcessId(Participant provider, String transferProcessId) { + var body = Map.of("transferProcessId", transferProcessId, + "endpointUrl", format("%s/aas/test", provider.gatewayEndpoint)); + return getProxyData(body); + + } + + private String getProxyData(Map body) { + return proxyRequest(body) + .then() + .assertThat().statusCode(200) + .extract().body().asString(); + } + + private Response proxyRequest(Map body) { + return given() + .baseUri(proxyUrl) + .contentType("application/json") + .body(body) + .post(PROXY_SUBPATH); + } + public JsonObject getDatasetForAsset(Participant provider, String assetId) { var datasets = getCatalogDatasets(provider); return datasets.stream() @@ -334,13 +369,6 @@ public JsonObject getDatasetForAsset(Participant provider, String assetId) { .orElseThrow(() -> new EdcException(format("No dataset for asset %s in the catalog", assetId))); } - - @Override - protected void bootExtensions(ServiceExtensionContext context, List> serviceExtensions) { - super.bootExtensions(context, serviceExtensions); - wiper = new DataWiper(context); - } - private RequestSpecification baseRequest() { return given() .baseUri(managementUrl) diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/ParticipantRuntime.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/ParticipantRuntime.java new file mode 100644 index 000000000..4f6c9f0ff --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/ParticipantRuntime.java @@ -0,0 +1,68 @@ +/* + * 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.junit.extensions.EdcRuntimeExtension; +import org.eclipse.edc.spi.iam.IdentityService; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.injection.InjectionContainer; +import org.eclipse.tractusx.edc.token.MockDapsService; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.util.List; +import java.util.Map; + +public class ParticipantRuntime extends EdcRuntimeExtension implements BeforeAllCallback, AfterAllCallback { + + + private DataWiper wiper; + + public ParticipantRuntime(String moduleName, String runtimeName, String bpn, Map properties) { + super(moduleName, runtimeName, properties); + this.registerServiceMock(IdentityService.class, new MockDapsService(bpn)); + } + + @Override + public void beforeTestExecution(ExtensionContext extensionContext) { + //do nothing - we only want to start the runtime once + wiper.clearPersistence(); + } + + @Override + public void afterTestExecution(ExtensionContext context) { + } + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + //only run this once + super.beforeTestExecution(context); + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + super.afterTestExecution(context); + } + + + @Override + protected void bootExtensions(ServiceExtensionContext context, List> serviceExtensions) { + super.bootExtensions(context, serviceExtensions); + wiper = new DataWiper(context); + } + +} 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 new file mode 100644 index 000000000..f66b01bf3 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/PgParticipantRuntime.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.lifecycle; + +import org.eclipse.edc.connector.core.vault.InMemoryVault; +import org.eclipse.edc.spi.iam.IdentityService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.injection.InjectionContainer; +import org.eclipse.edc.sql.testfixtures.PostgresqlLocalInstance; +import org.eclipse.tractusx.edc.token.MockDapsService; + +import java.util.List; +import java.util.Map; + +import static org.mockito.Mockito.mock; + +public class PgParticipantRuntime extends ParticipantRuntime { + + private final String dbName; + + 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))); + } + + @Override + protected void bootExtensions(ServiceExtensionContext context, List> serviceExtensions) { + PostgresqlLocalInstance.createDatabase(dbName); + super.bootExtensions(context, serviceExtensions); + } + + + private static class InMemoryVaultOverride extends InMemoryVault { + + InMemoryVaultOverride(Monitor monitor) { + super(monitor); + } + + @Override + public Result deleteSecret(String s) { + super.deleteSecret(s); + return Result.success(); + } + } + +} 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 234de574e..ba0a1b20a 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,21 +14,30 @@ 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; + import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; -class TestRuntimeConfiguration { +public class TestRuntimeConfiguration { + public static final String BPN_SUFFIX = "-BPN"; + public static final String SOKRATES_NAME = "SOKRATES"; + public static final String SOKRATES_BPN = SOKRATES_NAME + BPN_SUFFIX; + public static final String PLATO_NAME = "PLATO"; + public static final String PLATO_BPN = PLATO_NAME + BPN_SUFFIX; + public static final Integer PLATO_PROXIED_AAS_BACKEND_PORT = getFreePort(); static final String DSP_PATH = "/api/v1/dsp"; static final int PLATO_CONNECTOR_PORT = getFreePort(); static final int PLATO_MANAGEMENT_PORT = getFreePort(); static final String PLATO_CONNECTOR_PATH = "/api"; static final String PLATO_MANAGEMENT_PATH = "/api/v1/management"; - static final int PLATO_DSP_API_PORT = getFreePort(); - static final String PLATO_DSP_CALLBACK = "http://localhost:" + PLATO_DSP_API_PORT + DSP_PATH; - static final int SOKRATES_CONNECTOR_PORT = getFreePort(); static final int SOKRATES_MANAGEMENT_PORT = getFreePort(); static final String SOKRATES_CONNECTOR_PATH = "/api"; @@ -37,9 +46,124 @@ class TestRuntimeConfiguration { static final String SOKRATES_DSP_CALLBACK = "http://localhost:" + SOKRATES_DSP_API_PORT + DSP_PATH; static final String SOKRATES_PUBLIC_API_PORT = String.valueOf(getFreePort()); static final String PLATO_PUBLIC_API_PORT = String.valueOf(getFreePort()); - static final String PLATO_DATAPLANE_CONTROL_PORT = String.valueOf(getFreePort()); + static final String PLATO_DATAPLANE_PROXY_PORT = String.valueOf(getFreePort()); static final String SOKRATES_DATAPLANE_CONTROL_PORT = String.valueOf(getFreePort()); + static final String SOKRATES_DATAPLANE_PROXY_PORT = String.valueOf(getFreePort()); + + 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); + } + }; + } + + public static Map sokratesConfiguration() { + return new HashMap<>() { + { + put("edc.connector.name", "sokrates"); + put("edc.participant.id", SOKRATES_BPN); + put("web.http.port", String.valueOf(SOKRATES_CONNECTOR_PORT)); + put("web.http.path", SOKRATES_CONNECTOR_PATH); + put("web.http.management.port", String.valueOf(SOKRATES_MANAGEMENT_PORT)); + put("web.http.management.path", SOKRATES_MANAGEMENT_PATH); + put("web.http.protocol.port", String.valueOf(SOKRATES_DSP_API_PORT)); + put("web.http.protocol.path", DSP_PATH); + put("edc.dsp.callback.address", SOKRATES_DSP_CALLBACK); + put("edc.api.auth.key", "testkey"); + put("web.http.public.path", "/api/public"); + put("web.http.public.port", SOKRATES_PUBLIC_API_PORT); + + // embedded dataplane config + put("web.http.control.path", "/api/dataplane/control"); + put("web.http.control.port", SOKRATES_DATAPLANE_CONTROL_PORT); + put("tx.dpf.consumer.proxy.port", SOKRATES_DATAPLANE_PROXY_PORT); + put("edc.dataplane.token.validation.endpoint", "http://localhost:" + SOKRATES_DATAPLANE_CONTROL_PORT + "/api/dataplane/control/token"); + put("edc.dataplane.selector.httpplane.url", "http://localhost:" + SOKRATES_DATAPLANE_CONTROL_PORT + "/api/dataplane/control"); + put("edc.dataplane.selector.httpplane.sourcetypes", "HttpData"); + put("edc.dataplane.selector.httpplane.destinationtypes", "HttpProxy"); + put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + SOKRATES_PUBLIC_API_PORT + "/api/public\"}"); + put("edc.receiver.http.dynamic.endpoint", "http://localhost:" + SOKRATES_CONNECTOR_PORT + "/api/consumer/datareference"); + put("tractusx.businesspartnervalidation.log.agreement.validation", "true"); + put("edc.agent.identity.key", "BusinessPartnerNumber"); + } + }; + } + + public static Map platoConfiguration() { + return new HashMap<>() { + { + put("edc.connector.name", "plato"); + put("edc.participant.id", PLATO_BPN); + put("web.http.port", String.valueOf(PLATO_CONNECTOR_PORT)); + put("web.http.path", PLATO_CONNECTOR_PATH); + put("web.http.management.port", String.valueOf(PLATO_MANAGEMENT_PORT)); + put("web.http.management.path", PLATO_MANAGEMENT_PATH); + put("web.http.protocol.port", String.valueOf(PLATO_DSP_API_PORT)); + put("web.http.protocol.path", DSP_PATH); + put("edc.dsp.callback.address", PLATO_DSP_CALLBACK); + put("edc.api.auth.key", "testkey"); + put("web.http.public.port", PLATO_PUBLIC_API_PORT); + put("web.http.public.path", "/api/public"); + // embedded dataplane config + put("web.http.control.path", "/api/dataplane/control"); + put("web.http.control.port", PLATO_DATAPLANE_CONTROL_PORT); + put("tx.dpf.consumer.proxy.port", PLATO_DATAPLANE_PROXY_PORT); + put("edc.dataplane.token.validation.endpoint", "http://localhost:" + PLATO_DATAPLANE_CONTROL_PORT + "/api/dataplane/control/token"); + put("edc.dataplane.selector.httpplane.url", "http://localhost:" + PLATO_DATAPLANE_CONTROL_PORT + "/api/dataplane/control"); + put("edc.dataplane.selector.httpplane.sourcetypes", "HttpData"); + put("edc.dataplane.selector.httpplane.destinationtypes", "HttpProxy"); + put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + PLATO_PUBLIC_API_PORT + "/api/public\"}"); + put("tractusx.businesspartnervalidation.log.agreement.validation", "true"); + put("edc.agent.identity.key", "BusinessPartnerNumber"); + put("tx.dpf.proxy.gateway.aas.proxied.path", "http://localhost:" + PLATO_PROXIED_AAS_BACKEND_PORT); + put("tx.dpf.proxy.gateway.aas.authorization.type", "none"); + } + }; + } + @NotNull + public static String jdbcUrl(String name) { + return PostgresqlLocalInstance.JDBC_URL_PREFIX + name; + } } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/AbstractCatalogTest.java similarity index 55% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogTest.java rename to edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/AbstractCatalogTest.java index 1a9149099..741e198ed 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/AbstractCatalogTest.java @@ -12,11 +12,10 @@ * */ -package org.eclipse.tractusx.edc.tests; +package org.eclipse.tractusx.edc.tests.catalog; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.tractusx.edc.lifecycle.MultiRuntimeTest; +import org.eclipse.tractusx.edc.lifecycle.Participant; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -27,22 +26,30 @@ 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; +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; -@EndToEndTest -public class CatalogTest extends MultiRuntimeTest { +public abstract class AbstractCatalogTest { + + protected static final Participant SOKRATES = new Participant(SOKRATES_NAME, SOKRATES_BPN, sokratesConfiguration()); + protected static final Participant PLATO = new Participant(PLATO_NAME, PLATO_BPN, platoConfiguration()); @Test void requestCatalog_fulfillsPolicy_shouldReturnOffer() { // arrange - sokrates.createAsset("test-asset"); + SOKRATES.createAsset("test-asset"); var accessPolicy = noConstraintPolicyDefinition("test-ap1"); var contractPolicy = noConstraintPolicyDefinition("test-cp1"); - sokrates.createPolicy(accessPolicy); - sokrates.createPolicy(contractPolicy); - sokrates.createContractDefinition("test-asset", "test-def", "test-ap1", "test-cp1"); + SOKRATES.createPolicy(accessPolicy); + SOKRATES.createPolicy(contractPolicy); + SOKRATES.createContractDefinition("test-asset", "test-def", "test-ap1", "test-cp1"); // act - var catalog = plato.getCatalogDatasets(sokrates); + var catalog = PLATO.getCatalogDatasets(SOKRATES); // assert assertThat(catalog).isNotEmpty() @@ -59,39 +66,39 @@ void requestCatalog_filteredByBpn_shouldReject() { var onlyPlatoId = "ap"; var onlyDiogenesId = "db"; - var onlyPlatoPolicy = businessPartnerNumberPolicy(onlyPlatoId, "BPN1", "BPN2", plato.getBpn()); + var onlyPlatoPolicy = businessPartnerNumberPolicy(onlyPlatoId, "BPN1", "BPN2", PLATO.getBpn()); var onlyDiogenesPolicy = businessPartnerNumberPolicy(onlyDiogenesId, "ARISTOTELES-BPN"); var noConstraintPolicyId = "no-constraint"; - sokrates.createPolicy(onlyPlatoPolicy); - sokrates.createPolicy(onlyDiogenesPolicy); - sokrates.createPolicy(noConstraintPolicyDefinition(noConstraintPolicyId)); + SOKRATES.createPolicy(onlyPlatoPolicy); + SOKRATES.createPolicy(onlyDiogenesPolicy); + SOKRATES.createPolicy(noConstraintPolicyDefinition(noConstraintPolicyId)); - sokrates.createAsset("test-asset1"); - sokrates.createAsset("test-asset2"); - sokrates.createAsset("test-asset3"); + SOKRATES.createAsset("test-asset1"); + SOKRATES.createAsset("test-asset2"); + SOKRATES.createAsset("test-asset3"); - sokrates.createContractDefinition("test-asset1", "def1", noConstraintPolicyId, noConstraintPolicyId); - sokrates.createContractDefinition("test-asset2", "def2", onlyPlatoId, noConstraintPolicyId); - sokrates.createContractDefinition("test-asset3", "def3", onlyDiogenesId, noConstraintPolicyId); + SOKRATES.createContractDefinition("test-asset1", "def1", noConstraintPolicyId, noConstraintPolicyId); + SOKRATES.createContractDefinition("test-asset2", "def2", onlyPlatoId, noConstraintPolicyId); + SOKRATES.createContractDefinition("test-asset3", "def3", onlyDiogenesId, noConstraintPolicyId); // act - var catalog = plato.getCatalogDatasets(sokrates); + var catalog = PLATO.getCatalogDatasets(SOKRATES); assertThat(catalog).hasSize(2); } @Test @DisplayName("Multiple ContractDefinitions exist for one Asset") void requestCatalog_multipleOffersForAsset() { - sokrates.createAsset("asset-1"); - sokrates.createPolicy(noConstraintPolicyDefinition("policy-1")); - sokrates.createPolicy(businessPartnerNumberPolicy("policy-2", plato.getBpn())); + SOKRATES.createAsset("asset-1"); + SOKRATES.createPolicy(noConstraintPolicyDefinition("policy-1")); + SOKRATES.createPolicy(businessPartnerNumberPolicy("policy-2", PLATO.getBpn())); - sokrates.createContractDefinition("asset-1", "def1", "policy-1", "policy-1"); - sokrates.createContractDefinition("asset-1", "def2", "policy-2", "policy-1"); + SOKRATES.createContractDefinition("asset-1", "def1", "policy-1", "policy-1"); + SOKRATES.createContractDefinition("asset-1", "def2", "policy-2", "policy-1"); - var catalog = plato.getCatalogDatasets(sokrates); + var catalog = PLATO.getCatalogDatasets(SOKRATES); assertThat(catalog).hasSize(1) .allSatisfy(cd -> { assertThat(getDatasetAssetId(cd)).isEqualTo("asset-1"); @@ -103,24 +110,24 @@ void requestCatalog_multipleOffersForAsset() { @DisplayName("Catalog with 1000 offers") void requestCatalog_of1000Assets_shouldContainAll() { var policyId = "policy-1"; - var policy = businessPartnerNumberPolicy(policyId, plato.getBpn()); - sokrates.createPolicy(policy); - sokrates.createPolicy(noConstraintPolicyDefinition("noconstraint")); + var policy = businessPartnerNumberPolicy(policyId, PLATO.getBpn()); + SOKRATES.createPolicy(policy); + SOKRATES.createPolicy(noConstraintPolicyDefinition("noconstraint")); range(0, 1000) .forEach(i -> { var assetId = "asset-" + i; - sokrates.createAsset(assetId); - sokrates.createContractDefinition(assetId, "def-" + i, policyId, "noconstraint"); + SOKRATES.createAsset(assetId); + SOKRATES.createContractDefinition(assetId, "def-" + i, policyId, "noconstraint"); }); // request all at once - var dataset = plato.getCatalogDatasets(sokrates, createQuery(1000, 0)); + var dataset = PLATO.getCatalogDatasets(SOKRATES, createQuery(1000, 0)); assertThat(dataset).hasSize(1000); // request in chunks - var o2 = plato.getCatalogDatasets(sokrates, createQuery(500, 0)); - var o3 = plato.getCatalogDatasets(sokrates, createQuery(500, 500)); + var o2 = PLATO.getCatalogDatasets(SOKRATES, createQuery(500, 0)); + var o3 = PLATO.getCatalogDatasets(SOKRATES, createQuery(500, 500)); assertThat(o2).doesNotContainAnyElementsOf(o3); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogInMemoryTest.java new file mode 100644 index 000000000..7cbb8b2e4 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogInMemoryTest.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.tests.catalog; + +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; + +@EndToEndTest +public class CatalogInMemoryTest extends AbstractCatalogTest { + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + SOKRATES_NAME, + SOKRATES_BPN, + sokratesConfiguration() + ); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + PLATO_NAME, + PLATO_BPN, + platoConfiguration() + ); +} 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 new file mode 100644 index 000000000..5af78043d --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/CatalogPostgresqlTest.java @@ -0,0 +1,45 @@ +/* + * 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.catalog; + +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.platoPostgresqlConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesPostgresqlConfiguration; + +@PostgresqlDbIntegrationTest +public class CatalogPostgresqlTest extends AbstractCatalogTest { + + @RegisterExtension + protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( + ":edc-tests:runtime:runtime-postgresql", + SOKRATES_NAME, + SOKRATES_BPN, + sokratesPostgresqlConfiguration() + ); + @RegisterExtension + protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( + ":edc-tests:runtime:runtime-postgresql", + PLATO_NAME, + PLATO_BPN, + platoPostgresqlConfiguration() + ); +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/NegotiateEdrTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java similarity index 66% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/NegotiateEdrTest.java rename to edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java index b10b4fa8a..30ebb4bee 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/NegotiateEdrTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java @@ -12,9 +12,8 @@ * */ -package org.eclipse.tractusx.edc.tests; +package org.eclipse.tractusx.edc.tests.edr; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.json.Json; import okhttp3.mockwebserver.MockResponse; @@ -29,9 +28,8 @@ import org.eclipse.edc.connector.transfer.spi.event.TransferProcessProvisioned; import org.eclipse.edc.connector.transfer.spi.event.TransferProcessRequested; import org.eclipse.edc.connector.transfer.spi.event.TransferProcessStarted; -import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.edc.spi.event.Event; -import org.eclipse.tractusx.edc.lifecycle.MultiRuntimeTest; +import org.eclipse.tractusx.edc.helpers.ReceivedEvent; +import org.eclipse.tractusx.edc.lifecycle.Participant; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -43,11 +41,21 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +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.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; -@EndToEndTest -public class NegotiateEdrTest extends MultiRuntimeTest { +public abstract class AbstractNegotiateEdrTest { + + protected static final Participant SOKRATES = new Participant(SOKRATES_NAME, SOKRATES_BPN, sokratesConfiguration()); + protected static final Participant PLATO = new Participant(PLATO_NAME, PLATO_BPN, platoConfiguration()); MockWebServer server = new MockWebServer(); @@ -76,7 +84,7 @@ void negotiateEdr_shouldInvokeCallbacks() throws IOException { var authCodeHeaderName = "test-authkey"; var authCode = "test-authcode"; - plato.createAsset(assetId, Json.createObjectBuilder().build(), Json.createObjectBuilder() + 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()) @@ -84,32 +92,42 @@ 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.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); + PLATO.createPolicy(businessPartnerNumberPolicy("policy-1", SOKRATES.getBpn())); + PLATO.createPolicy(businessPartnerNumberPolicy("policy-2", SOKRATES.getBpn())); + PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); - expectedEvents.forEach(_event -> { - server.enqueue(new MockResponse()); - }); + expectedEvents.forEach(event -> server.enqueue(new MockResponse())); var callbacks = Json.createArrayBuilder() .add(createCallback(url.toString(), true, Set.of("contract.negotiation", "transfer.process"))) .build(); - sokrates.negotiateEdr(plato, assetId, callbacks); + SOKRATES.negotiateEdr(PLATO, assetId, callbacks); var events = expectedEvents.stream() .map(this::waitForEvent) .collect(Collectors.toList()); assertThat(expectedEvents).usingRecursiveFieldByFieldElementComparator().containsAll(events); - } - ReceivedEvent createEvent(Class klass) { - return ReceivedEvent.Builder.newInstance().type(klass.getSimpleName()).build(); + var edrCaches = SOKRATES.getEdrEntriesByAssetId(assetId); + + assertThat(edrCaches).hasSize(1); + + var transferProcessId = edrCaches.get(0).asJsonObject().getString("edc:transferProcessId"); + + var edr = SOKRATES.getEdr(transferProcessId); + + assertThat(edr.getJsonString("edc:type").getString()).isEqualTo(EDR_SIMPLE_TYPE); + assertThat(edr.getJsonString("edc:authCode").getString()).isNotNull(); + assertThat(edr.getJsonString("edc:authKey").getString()).isNotNull(); + assertThat(edr.getJsonString("edc:endpoint").getString()).isNotNull(); + assertThat(edr.getJsonString("edc:id").getString()).isEqualTo(transferProcessId); + } + ReceivedEvent waitForEvent(ReceivedEvent event) { try { var request = server.takeRequest(20, TimeUnit.SECONDS); @@ -123,42 +141,5 @@ ReceivedEvent waitForEvent(ReceivedEvent event) { } } - @JsonIgnoreProperties(ignoreUnknown = true) - private static class ReceivedEvent { - private String type; - - public String getType() { - return type; - } - - @Override - public String toString() { - return "ReceivedEvent{" + - "type='" + type + '\'' + - '}'; - } - - public static class Builder { - private final ReceivedEvent event; - - private Builder(ReceivedEvent event) { - this.event = event; - } - - public static Builder newInstance() { - return new Builder(new ReceivedEvent()); - } - - public Builder type(String type) { - this.event.type = type; - return this; - } - - public ReceivedEvent build() { - return event; - } - } - - } } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrInMemoryTest.java new file mode 100644 index 000000000..072131805 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrInMemoryTest.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.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; + +@EndToEndTest +public class NegotiateEdrInMemoryTest extends AbstractNegotiateEdrTest { + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + SOKRATES_NAME, + SOKRATES_BPN, + sokratesConfiguration() + ); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + PLATO_NAME, + PLATO_BPN, + 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 new file mode 100644 index 000000000..ec2ccf7bf --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/NegotiateEdrPostgresqlTest.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.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.platoPostgresqlConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesPostgresqlConfiguration; + +@PostgresqlDbIntegrationTest +public class NegotiateEdrPostgresqlTest extends AbstractNegotiateEdrTest { + + @RegisterExtension + protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( + ":edc-tests:runtime:runtime-postgresql", + SOKRATES_NAME, + SOKRATES_BPN, + sokratesPostgresqlConfiguration() + ); + @RegisterExtension + protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( + ":edc-tests:runtime:runtime-postgresql", + PLATO_NAME, + PLATO_BPN, + platoPostgresqlConfiguration() + ); + +} 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 new file mode 100644 index 000000000..176662720 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/AbstractDataPlaneProxyTest.java @@ -0,0 +1,200 @@ +/* + * 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.proxy; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.json.Json; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.eclipse.edc.connector.transfer.spi.event.TransferProcessCompleted; +import org.eclipse.edc.spi.event.EventEnvelope; +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.util.Set; +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.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; +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 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()); + + MockWebServer server = new MockWebServer(); + + ObjectMapper mapper = new ObjectMapper(); + + @Test + @DisplayName("Verify E2E flow with Data Plane proxies and EDR") + void httpPullDataTransfer_withEdrAndProxy() throws IOException { + + var eventsUrl = server.url("/events"); + + var assetId = "api-asset-1"; + 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.createPolicy(businessPartnerNumberPolicy("policy-1", SOKRATES.getBpn())); + PLATO.createPolicy(businessPartnerNumberPolicy("policy-2", SOKRATES.getBpn())); + 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.pullProxyDataByAssetId(PLATO, assetId); + assertThat(data).isEqualTo(body); + + server.enqueue(new MockResponse().setBody(body)); + data = SOKRATES.pullProxyDataByTransferProcessId(PLATO, transferEvent.getPayload().getTransferProcessId()); + assertThat(data).isEqualTo(body); + } + + @Test + @DisplayName("Verify E2E flow with Data Plane proxies fails when EDR is not found") + void httpPullDataTransfer_withoutEdr() throws IOException { + + var eventsUrl = server.url("/events"); + + var assetId = "api-asset-1"; + 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.createPolicy(businessPartnerNumberPolicy("policy-1", SOKRATES.getBpn())); + PLATO.createPolicy(businessPartnerNumberPolicy("policy-2", SOKRATES.getBpn())); + PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); + + + SOKRATES.pullProxyDataResponseByAssetId(PLATO, assetId) + .then() + .assertThat().statusCode(400); + + } + + @Test + @DisplayName("Verify E2E flow with Data Plane proxies and Two EDR") + void httpPullDataTransfer_shouldFailForAsset_withTwoEdrAndProxy() throws IOException { + + var eventsUrl = server.url("/events"); + + var assetId = "api-asset-1"; + 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.createPolicy(businessPartnerNumberPolicy("policy-1", SOKRATES.getBpn())); + PLATO.createPolicy(businessPartnerNumberPolicy("policy-2", SOKRATES.getBpn())); + 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()); + server.enqueue(new MockResponse()); + + SOKRATES.negotiateEdr(PLATO, assetId, callbacks); + SOKRATES.negotiateEdr(PLATO, assetId, callbacks); + + var transferEvent1 = waitForTransferCompletion(); + var transferEvent2 = waitForTransferCompletion(); + + var body = "{\"response\": \"ok\"}"; + + server.enqueue(new MockResponse().setBody(body)); + SOKRATES.pullProxyDataResponseByAssetId(PLATO, assetId).then() + .assertThat().statusCode(428); + + server.enqueue(new MockResponse().setBody(body)); + var data = SOKRATES.pullProxyDataByTransferProcessId(PLATO, transferEvent1.getPayload().getTransferProcessId()); + assertThat(data).isEqualTo(body); + + server.enqueue(new MockResponse().setBody(body)); + data = SOKRATES.pullProxyDataByTransferProcessId(PLATO, transferEvent2.getPayload().getTransferProcessId()); + assertThat(data).isEqualTo(body); + } + + @BeforeEach + void setup() throws IOException { + server.start(PLATO_PROXIED_AAS_BACKEND_PORT); + } + + @AfterEach + void teardown() throws IOException { + server.shutdown(); + } + + EventEnvelope waitForTransferCompletion() { + try { + var request = server.takeRequest(20, TimeUnit.SECONDS); + if (request != null) { + return mapper.readValue(request.getBody().inputStream(), new TypeReference<>() { + }); + } else { + throw new RuntimeException("Timeout exceeded waiting for events"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/DataPlaneProxyInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/DataPlaneProxyInMemoryTest.java new file mode 100644 index 000000000..828383b19 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/DataPlaneProxyInMemoryTest.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.tests.proxy; + +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; + +@EndToEndTest +public class DataPlaneProxyInMemoryTest extends AbstractDataPlaneProxyTest { + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + SOKRATES_NAME, + SOKRATES_BPN, + sokratesConfiguration() + ); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + PLATO_NAME, + PLATO_BPN, + platoConfiguration() + ); +} 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 new file mode 100644 index 000000000..f2da5eba8 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/DataPlaneProxyPostgresqlTest.java @@ -0,0 +1,45 @@ +/* + * 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.proxy; + +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.platoPostgresqlConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesPostgresqlConfiguration; + +@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() + ); + @RegisterExtension + protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( + ":edc-tests:runtime:runtime-postgresql", + PLATO_NAME, + PLATO_BPN, + platoPostgresqlConfiguration() + ); +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpConsumerPullWithProxyTest.java similarity index 66% rename from edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java rename to edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpConsumerPullWithProxyTest.java index 7ab0419e9..ac229c08e 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpConsumerPullWithProxyTest.java @@ -1,27 +1,26 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 + * This program and the accompanying materials are 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 + * SPDX-License-Identifier: Apache-2.0 * - * Contributors: + * Contributors: * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ -package org.eclipse.tractusx.edc.tests; +package org.eclipse.tractusx.edc.tests.transfer; import jakarta.json.Json; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates; import org.eclipse.edc.connector.transfer.spi.types.TransferProcessStates; -import org.eclipse.edc.junit.annotations.EndToEndTest; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.lifecycle.MultiRuntimeTest; +import org.eclipse.tractusx.edc.lifecycle.Participant; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -38,9 +37,18 @@ 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.TransferProcessHelperFunctions.createProxyRequest; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_NAME; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_BPN; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_NAME; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.platoConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; + +public abstract class AbstractHttpConsumerPullWithProxyTest { + + protected static final Participant SOKRATES = new Participant(SOKRATES_NAME, SOKRATES_BPN, sokratesConfiguration()); + protected static final Participant PLATO = new Participant(PLATO_NAME, PLATO_BPN, platoConfiguration()); -@EndToEndTest -public class HttpConsumerPullWithProxyTest extends MultiRuntimeTest { private static final Duration ASYNC_TIMEOUT = ofSeconds(45); private static final Duration ASYNC_POLL_INTERVAL = ofSeconds(1); MockWebServer server = new MockWebServer(); @@ -60,13 +68,13 @@ void transferData_privateBackend() throws IOException, InterruptedException { .add(EDC_NAMESPACE + "authKey", authCodeHeaderName) .add(EDC_NAMESPACE + "authCode", authCode) .build(); - - plato.createAsset(assetId, Json.createObjectBuilder().build(), dataAddress); - plato.createPolicy(businessPartnerNumberPolicy("policy-1", sokrates.getBpn())); - plato.createPolicy(businessPartnerNumberPolicy("policy-2", sokrates.getBpn())); - plato.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); - var negotiationId = sokrates.negotiateContract(plato, assetId); + PLATO.createAsset(assetId, Json.createObjectBuilder().build(), dataAddress); + + PLATO.createPolicy(businessPartnerNumberPolicy("policy-1", SOKRATES.getBpn())); + PLATO.createPolicy(businessPartnerNumberPolicy("policy-2", SOKRATES.getBpn())); + PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); + var negotiationId = SOKRATES.negotiateContract(PLATO, assetId); // forward declarations of our actual values var transferProcessId = new AtomicReference(); @@ -78,14 +86,14 @@ void transferData_privateBackend() throws IOException, InterruptedException { await().pollInterval(ASYNC_POLL_INTERVAL) .atMost(ASYNC_TIMEOUT) .untilAsserted(() -> { - var negotiationState = sokrates.getNegotiationState(negotiationId); + var negotiationState = SOKRATES.getNegotiationState(negotiationId); assertThat(negotiationState).isEqualTo(ContractNegotiationStates.FINALIZED.toString()); - var agreementId = sokrates.getContractAgreementId(negotiationId); + var agreementId = SOKRATES.getContractAgreementId(negotiationId); assertThat(agreementId).isNotNull(); contractAgreementId.set(agreementId); - var tpId = sokrates.requestTransfer(dataRequestId, contractAgreementId.get(), assetId, plato, createProxyRequest()); + var tpId = SOKRATES.requestTransfer(dataRequestId, contractAgreementId.get(), assetId, PLATO, createProxyRequest()); transferProcessId.set(tpId); assertThat(transferProcessId).isNotNull(); }); @@ -94,7 +102,7 @@ void transferData_privateBackend() throws IOException, InterruptedException { await().pollInterval(fibonacci()) .atMost(ASYNC_TIMEOUT) .untilAsserted(() -> { - var tpState = sokrates.getTransferProcessState(transferProcessId.get()); + var tpState = SOKRATES.getTransferProcessState(transferProcessId.get()); assertThat(tpState).isNotNull().isEqualTo(TransferProcessStates.COMPLETED.toString()); }); @@ -103,13 +111,13 @@ void transferData_privateBackend() throws IOException, InterruptedException { await().pollInterval(fibonacci()) .atMost(ASYNC_TIMEOUT) .untilAsserted(() -> { - edr.set(sokrates.getDataReference(dataRequestId)); + edr.set(SOKRATES.getDataReference(dataRequestId)); assertThat(edr).isNotNull(); }); // pull data out of provider's backend service: // Cons-DP -> Prov-DP -> Prov-backend - assertThat(sokrates.pullData(edr.get(), Map.of())).isEqualTo("test response"); + assertThat(SOKRATES.pullData(edr.get(), Map.of())).isEqualTo("test response"); var rq = server.takeRequest(); assertThat(rq.getHeader(authCodeHeaderName)).isEqualTo(authCode); assertThat(rq.getHeader("Edc-Contract-Agreement-Id")).isEqualTo(contractAgreementId.get()); diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullWithProxyInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullWithProxyInMemoryTest.java new file mode 100644 index 000000000..b786cebd6 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullWithProxyInMemoryTest.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.tests.transfer; + +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; + +@EndToEndTest +public class HttpConsumerPullWithProxyInMemoryTest extends AbstractHttpConsumerPullWithProxyTest { + + @RegisterExtension + protected static final ParticipantRuntime SOKRATES_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + SOKRATES_NAME, + SOKRATES_BPN, + sokratesConfiguration() + ); + + @RegisterExtension + protected static final ParticipantRuntime PLATO_RUNTIME = new ParticipantRuntime( + ":edc-tests:runtime:runtime-memory", + PLATO_NAME, + PLATO_BPN, + 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 new file mode 100644 index 000000000..887c7a307 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/HttpConsumerPullWithProxyPostgresqlTest.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.transfer; + +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.platoPostgresqlConfiguration; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesPostgresqlConfiguration; + +@PostgresqlDbIntegrationTest +public class HttpConsumerPullWithProxyPostgresqlTest extends AbstractHttpConsumerPullWithProxyTest { + + + @RegisterExtension + protected static final PgParticipantRuntime SOKRATES_RUNTIME = new PgParticipantRuntime( + ":edc-tests:runtime:runtime-postgresql", + SOKRATES_NAME, + SOKRATES_BPN, + sokratesPostgresqlConfiguration() + ); + @RegisterExtension + protected static final PgParticipantRuntime PLATO_RUNTIME = new PgParticipantRuntime( + ":edc-tests:runtime:runtime-postgresql", + PLATO_NAME, + PLATO_BPN, + platoPostgresqlConfiguration() + ); + +} 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 c7649ea67..691aa255f 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 @@ -56,45 +56,44 @@ */ @EndToEndTest public class DpfProxyEndToEndTest { + public static final String KEYSTORE_PASS = "test123"; private static final String LAUNCHER_MODULE = ":edc-tests:edc-dataplane-proxy-e2e"; - private static final int CONSUMER_HTTP_PORT = getFreePort(); private static final int CONSUMER_PROXY_PORT = getFreePort(); private static final int PRODUCER_HTTP_PORT = getFreePort(); private static final int MOCK_ENDPOINT_PORT = getFreePort(); - private static final String PROXY_SUBPATH = "proxy/aas/request"; - private static final String SINGLE_TRANSFER_ID = "5355d524-2616-43df-9096-558afffff659"; private static final String SINGLE_ASSET_ID = "79f13b89-59a6-4278-8c8e-8540849dbab8"; private static final String MULTI_ASSET_ID = "9260f395-3d94-4b8b-bdaa-941ead596ce5"; - private static final String REQUEST_TEMPLATE_TP = "{\"transferProcessId\": \"%s\", \"endpointUrl\" : \"http://localhost:%s/api/gateway/aas/test\"}"; private static final String REQUEST_TEMPLATE_ASSET = "{\"assetId\": \"%s\", \"endpointUrl\" : \"http://localhost:%s/api/gateway/aas/test\"}"; - private static final String MOCK_ENDPOINT_200_BODY = "{\"message\":\"test\"}"; - - public static final String KEYSTORE_PASS = "test123"; - - private MockWebServer mockEndpoint; - @RegisterExtension - static EdcRuntimeExtension CONSUMER = new EdcRuntimeExtension( + static EdcRuntimeExtension consumer = new EdcRuntimeExtension( LAUNCHER_MODULE, "consumer", baseConfig(Map.of( "web.http.port", valueOf(CONSUMER_HTTP_PORT), "tx.dpf.consumer.proxy.port", valueOf(CONSUMER_PROXY_PORT) ))); - @RegisterExtension - static EdcRuntimeExtension PROVIDER = new EdcRuntimeExtension( + static EdcRuntimeExtension provider = new EdcRuntimeExtension( LAUNCHER_MODULE, "provider", baseConfig(Map.of( "web.http.port", valueOf(PRODUCER_HTTP_PORT), "tx.dpf.proxy.gateway.aas.proxied.path", "http://localhost:" + MOCK_ENDPOINT_PORT ))); + private MockWebServer mockEndpoint; + + private static Map baseConfig(Map values) { + var map = new HashMap<>(values); + map.put("edc.vault", createVaultStore()); + map.put("edc.keystore", createKeyStore(KEYSTORE_PASS)); + map.put("edc.keystore.password", KEYSTORE_PASS); + return map; + } @BeforeEach void setUp() { @@ -171,23 +170,13 @@ private RequestSpecification createSpecification(String body) { .body(body); } - private static Map baseConfig(Map values) { - var map = new HashMap<>(values); - map.put("edc.vault", createVaultStore()); - map.put("edc.keystore", createKeyStore(KEYSTORE_PASS)); - map.put("edc.keystore.password", KEYSTORE_PASS); - return map; - } - /** * Loads the EDR cache. */ private void seedEdrCache() { - var edrCache = CONSUMER.getContext().getService(EndpointDataReferenceCache.class); - createEntries().forEach(e->edrCache.save(e.getEdrEntry(), e.getEdr())); + var edrCache = consumer.getContext().getService(EndpointDataReferenceCache.class); + createEntries().forEach(e -> edrCache.save(e.getEdrEntry(), e.getEdr())); } - - } diff --git a/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/VaultSetup.java b/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/VaultSetup.java index 8bbae5f09..4c656fca2 100644 --- a/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/VaultSetup.java +++ b/edc-tests/edc-dataplane-proxy-e2e/src/test/java/org/eclipse/tractusx/edc/dataplane/proxy/e2e/VaultSetup.java @@ -26,7 +26,13 @@ public class VaultSetup { private static final String HEADER = DELIMITER + "BEGIN" + " PUBLIC " + "KEY" + DELIMITER; private static final String FOOTER = DELIMITER + "END" + " PUBLIC " + "KEY" + DELIMITER; - private static final String VAULT_CONTENTS = "tx.dpf.data.proxy.public.key=" + HEADER + "\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyMkG7DSIhMjFOtqQJsr+\\nKtzfKKGGQ/7mBdjwDCEj0ijKLG/LiEYWsbPA8L/oMAIdR4xpLGaajtz6wj7NbMiA\\nrtHF1HA3mNoeKGix7SfobfQ9J7gJJmSE5DA4BxatL4sPMfoV2SJanJQQjOEAA6/i\\nI+o8SeeBc/2YE55O3yeFjdHK5JIwDi9vIkGnDRBd9poyrHYV+7dcyBB45r6BwvoW\\nG41mezzlKbOl0ZtPW1T9fqp+lOiZWIHMY5ml1daGSbTWwfJxc7XfHHa8KCNQcsPR\\nhWYx6PnxvgqQwYPjvqZF7OYAMUOQX8pg6jfYiU4HgUI1jwwGw3UpJq4b3kzD3u4T\\nDQIDAQAB\\n" + FOOTER + "\n"; + private static final String VAULT_CONTENTS = "tx.dpf.data.proxy.public.key=" + HEADER + "\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyMkG7DSIhMjFOtqQJsr+\\nKtzfKK" + + "GGQ/7mBdjwDCEj0ijKLG/LiEYWsbPA8L/oMAIdR4xpLGaajtz6wj7NbMiA\\nrtHF1HA3mNoeKGix7SfobfQ9J7gJJmSE5DA4BxatL4sPMfoV2SJanJQQjOEAA6/i\\nI+o8SeeBc/2YE55O3yeFjdHK5JIwDi9v" + + "IkGnDRBd9poyrHYV+7dcyBB45r6BwvoW\\nG41mezzlKbOl0ZtPW1T9fqp+lOiZWIHMY5ml1daGSbTWwfJxc7XfHHa8KCNQcsPR\\nhWYx6PnxvgqQwYPjvqZF7OYAMUOQX8pg6jfYiU4HgUI1jwwGw3UpJq4b3k" + + "zD3u4T\\nDQIDAQAB\\n" + FOOTER + "\n"; + + private VaultSetup() { + } public static String createVaultStore() { try { @@ -40,7 +46,4 @@ public static String createVaultStore() { throw new AssertionError(e); } } - - private VaultSetup() { - } } diff --git a/edc-tests/runtime/extensions/build.gradle.kts b/edc-tests/runtime/extensions/build.gradle.kts new file mode 100644 index 000000000..b9554f001 --- /dev/null +++ b/edc-tests/runtime/extensions/build.gradle.kts @@ -0,0 +1,31 @@ +/* + * 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.core.controlplane) + // for the controller + implementation(libs.jakarta.rsApi) +} + + +// do not publish +edcBuild { + publish.set(false) +} diff --git a/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java b/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java similarity index 73% rename from edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java rename to edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java index 5ca08a922..5f443a865 100644 --- a/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java +++ b/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java @@ -1,13 +1,13 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 + * This program and the accompanying materials are 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 + * SPDX-License-Identifier: Apache-2.0 * - * Contributors: + * Contributors: * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ @@ -32,7 +32,7 @@ public class ConsumerEdrHandlerController { private final Monitor monitor; - private Map dataReference; + private final Map dataReference; public ConsumerEdrHandlerController(Monitor monitor) { this.monitor = monitor; @@ -41,7 +41,7 @@ public ConsumerEdrHandlerController(Monitor monitor) { @Path("/datareference") @POST - @Consumes({ MediaType.APPLICATION_JSON }) + @Consumes({MediaType.APPLICATION_JSON}) public void pushDataReference(EndpointDataReference edr) { monitor.debug("Received new endpoint data reference with url " + edr.getEndpoint()); dataReference.put(edr.getId(), edr); @@ -49,10 +49,9 @@ public void pushDataReference(EndpointDataReference edr) { @Path("/datareference/{id}") @GET - @Produces({ MediaType.APPLICATION_JSON }) + @Produces({MediaType.APPLICATION_JSON}) public EndpointDataReference getDataReference(@PathParam("id") String id) { - return Optional.ofNullable(dataReference.get(id)).orElseGet(() -> - { + return Optional.ofNullable(dataReference.get(id)).orElseGet(() -> { monitor.warning("No EndpointDataReference found with id " + id); return null; }); diff --git a/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java b/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java similarity index 67% rename from edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java rename to edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java index f46ef3e4e..a1a2ab29c 100644 --- a/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java +++ b/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java @@ -1,13 +1,13 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 + * This program and the accompanying materials are 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 + * SPDX-License-Identifier: Apache-2.0 * - * Contributors: + * Contributors: * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ diff --git a/edc-tests/runtime/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 similarity index 100% rename from edc-tests/runtime/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to edc-tests/runtime/extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension diff --git a/edc-tests/runtime/README.md b/edc-tests/runtime/runtime-memory/README.md similarity index 100% rename from edc-tests/runtime/README.md rename to edc-tests/runtime/runtime-memory/README.md diff --git a/edc-tests/runtime/build.gradle.kts b/edc-tests/runtime/runtime-memory/build.gradle.kts similarity index 86% rename from edc-tests/runtime/build.gradle.kts rename to edc-tests/runtime/runtime-memory/build.gradle.kts index 7c476b287..f339202e2 100644 --- a/edc-tests/runtime/build.gradle.kts +++ b/edc-tests/runtime/runtime-memory/build.gradle.kts @@ -15,7 +15,6 @@ plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.1.1" } @@ -28,11 +27,14 @@ dependencies { exclude(module = "data-encryption") } + implementation(project(":edc-tests:runtime:extensions")) + // use basic (all in-mem) data plane runtimeOnly(project(":edc-dataplane:edc-dataplane-base")) { exclude("org.eclipse.edc", "api-observability") } + implementation(libs.edc.core.controlplane) // for the controller implementation(libs.jakarta.rsApi) @@ -42,11 +44,6 @@ application { mainClass.set("org.eclipse.edc.boot.system.runtime.BaseRuntime") } -tasks.withType { - mergeServiceFiles() - archiveFileName.set("app.jar") -} - // do not publish edcBuild { publish.set(false) diff --git a/edc-tests/runtime/runtime-postgresql/README.md b/edc-tests/runtime/runtime-postgresql/README.md new file mode 100644 index 000000000..b5b5d8914 --- /dev/null +++ b/edc-tests/runtime/runtime-postgresql/README.md @@ -0,0 +1,3 @@ +# PostgreSQL Runtime for Testing Purposes + +This module provides a very small,runtime using PostgreSQL as persistence backend to execute tests against. Not intended for anything other than testing! diff --git a/edc-tests/runtime/runtime-postgresql/build.gradle.kts b/edc-tests/runtime/runtime-postgresql/build.gradle.kts new file mode 100644 index 000000000..74f8003ec --- /dev/null +++ b/edc-tests/runtime/runtime-postgresql/build.gradle.kts @@ -0,0 +1,51 @@ +/* + * 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") +} + + +dependencies { + + // use basic (all in-mem) control plane + implementation(project(":edc-controlplane:edc-controlplane-postgresql-hashicorp-vault")) { + exclude("org.eclipse.edc", "oauth2-core") + exclude("org.eclipse.edc", "oauth2-daps") + exclude(module = "data-encryption") + exclude(module = "hashicorp-vault") + } + + implementation(project(":edc-tests:runtime:extensions")) + + // use basic (all in-mem) data plane + runtimeOnly(project(":edc-dataplane:edc-dataplane-base")) { + exclude("org.eclipse.edc", "api-observability") + } + + + implementation(libs.edc.core.controlplane) + // for the controller + implementation(libs.jakarta.rsApi) +} + +application { + mainClass.set("org.eclipse.edc.boot.system.runtime.BaseRuntime") +} + +// do not publish +edcBuild { + publish.set(false) +} diff --git a/gradle.properties b/gradle.properties index d49e094cb..4c09ddaa6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,11 +1,9 @@ -groupId=org.eclipse.tractusx.edc -version=0.4.0 -javaVersion=11 +group=org.eclipse.tractusx.edc +version=0.4.1 # configure the build: -# 0.0.1-SNAPSHOT is needed to leverage gradle 8 -annotationProcessorVersion=0.0.1-milestone-9 -edcGradlePluginsVersion=0.0.1-milestone-9 -metaModelVersion=0.0.1-milestone-9 +annotationProcessorVersion=0.1.0 +edcGradlePluginsVersion=0.1.0 +metaModelVersion=0.1.0 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 \ No newline at end of file +txScmUrl=https://github.com/eclipse-tractusx/tractusx-edc.git diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 34999e204..6fd3c16d4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,21 +2,23 @@ format.version = "1.1" [versions] -edc = "0.0.1-milestone-9" +edc = "0.1.0" postgres = "42.6.0" awaitility = "4.2.0" -nimbus = "9.25" -azure-identity = "+" +nimbus = "9.31" +azure-identity = "1.9.0" slf4j = "2.0.7" -okhttp = "4.10.0" +okhttp = "4.11.0" mockwebserver = "5.0.0-alpha.11" bouncyCastle-jdk18on = "1.73" mockito = "5.2.0" -restAssured = "4.5.0" -apache-sshd = "2.9.2" -testcontainers = "1.17.6" -aws = "2.20.50" +restAssured = "5.3.0" +apache-sshd = "2.10.0" +testcontainers = "1.18.2" +aws = "2.20.75" rsApi = "3.1.0" +jupiter = "5.9.3" +assertj = "3.24.2" [libraries] edc-spi-catalog = { module = "org.eclipse.edc:catalog-spi", version.ref = "edc" } @@ -44,6 +46,7 @@ edc-core-connector = { module = "org.eclipse.edc:connector-core", version.ref = 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-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" } @@ -57,7 +60,7 @@ edc-iam-mock = { module = "org.eclipse.edc:iam-mock", version.ref = "edc" } edc-policy-engine = { module = "org.eclipse.edc:policy-engine", version.ref = "edc" } edc-auth-tokenbased = { module = "org.eclipse.edc:auth-tokenbased", version.ref = "edc" } edc-auth-oauth2-core = { module = "org.eclipse.edc:oauth2-core", version.ref = "edc" } -edc-auth-oauth2-daps = { module = "org.eclipse.edc:oauth2-core", version.ref = "edc" } +edc-auth-oauth2-daps = { module = "org.eclipse.edc:oauth2-daps", version.ref = "edc" } edc-transaction-local = { module = "org.eclipse.edc:transaction-local", version.ref = "edc" } 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" } @@ -124,6 +127,8 @@ apache-sshd-sftp = { module = "org.apache.sshd:sshd-sftp", version.ref = "apache testcontainers-junit = { module = "org.testcontainers:junit-jupiter", 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" } +junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "jupiter" } +assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" } [bundles] edc-connector = ["edc.boot", "edc.core-connector", "edc.core-controlplane", "edc.api-observability"] diff --git a/lombok.config b/lombok.config deleted file mode 100644 index df71bb6a0..000000000 --- a/lombok.config +++ /dev/null @@ -1,2 +0,0 @@ -config.stopBubbling = true -lombok.addLombokGeneratedAnnotation = true diff --git a/resources/openapi/yaml/control-plane-adapter-api.yaml b/resources/openapi/yaml/control-plane-adapter-api.yaml index de9f7fbb0..2e35d60e0 100644 --- a/resources/openapi/yaml/control-plane-adapter-api.yaml +++ b/resources/openapi/yaml/control-plane-adapter-api.yaml @@ -1,6 +1,40 @@ openapi: 3.0.1 paths: /adapter/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 @@ -48,6 +82,44 @@ paths: description: Request body was malformed tags: - Control Plane Adapter EDR Api + /adapter/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: @@ -78,7 +150,7 @@ components: type: type: string example: null - CallbackAddressDto: + CallbackAddress: type: object example: null properties: @@ -101,9 +173,6 @@ components: uri: type: string example: null - required: - - events - - uri Constraint: type: object discriminator: @@ -135,6 +204,24 @@ components: - assetId - offerId - 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 + required: + - properties Duty: type: object example: null @@ -159,21 +246,40 @@ components: 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 - id: - type: string - example: null JsonObject: type: object additionalProperties: - $ref: '#/components/schemas/NegotiateEdrRequestDto' + $ref: '#/components/schemas/JsonValue' example: null properties: empty: @@ -213,7 +319,7 @@ components: type: array example: null items: - $ref: '#/components/schemas/CallbackAddressDto' + $ref: '#/components/schemas/CallbackAddress' connectorAddress: type: string example: null diff --git a/resources/openapi/yaml/edc-dataplane-proxy-consumer-api.yaml b/resources/openapi/yaml/edc-dataplane-proxy-consumer-api.yaml new file mode 100644 index 000000000..861d83730 --- /dev/null +++ b/resources/openapi/yaml/edc-dataplane-proxy-consumer-api.yaml @@ -0,0 +1,34 @@ +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 new file mode 100644 index 000000000..1a55c51b7 --- /dev/null +++ b/resources/openapi/yaml/edc-dataplane-proxy-provider-api.yaml @@ -0,0 +1,12 @@ +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/tx-checkstyle-config.xml b/resources/tx-checkstyle-config.xml index 099908c86..cdce34857 100644 --- a/resources/tx-checkstyle-config.xml +++ b/resources/tx-checkstyle-config.xml @@ -19,7 +19,7 @@ - + @@ -47,12 +47,12 @@ - - - - - + + + + + + diff --git a/settings.gradle.kts b/settings.gradle.kts index ada149481..3cd6fd9e5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,6 +22,7 @@ rootProject.name = "tractusx-edc" // spi modules include(":spi:control-plane-adapter-spi") include(":spi:edr-cache-spi") +include(":spi:core-spi") // core modules include(":core:edr-cache-core") @@ -40,10 +41,14 @@ 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-tests:e2e-tests") -include(":edc-tests:runtime") +include(":edc-tests:runtime:extensions") +include(":edc-tests:runtime:runtime-memory") +include(":edc-tests:runtime:runtime-postgresql") include(":edc-tests:cucumber") // modules for controlplane artifacts diff --git a/spi/control-plane-adapter-spi/build.gradle.kts b/spi/control-plane-adapter-spi/build.gradle.kts index d01290419..80ee806b2 100644 --- a/spi/control-plane-adapter-spi/build.gradle.kts +++ b/spi/control-plane-adapter-spi/build.gradle.kts @@ -19,8 +19,11 @@ plugins { dependencies { + implementation(project(":spi:core-spi")) + implementation(project(":spi:edr-cache-spi")) implementation(libs.edc.spi.core) implementation(libs.edc.spi.contract) implementation(libs.edc.spi.aggregateservices) implementation(libs.edc.spi.controlplane) + implementation(libs.edc.spi.controlplane) } diff --git a/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/service/AdapterTransferProcessService.java b/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/service/AdapterTransferProcessService.java index 2ab135030..3ff0b50e9 100644 --- a/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/service/AdapterTransferProcessService.java +++ b/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/service/AdapterTransferProcessService.java @@ -16,8 +16,12 @@ import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; import org.eclipse.edc.service.spi.result.ServiceResult; +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 java.util.List; + /** * Service for opening a transfer process. */ @@ -31,4 +35,15 @@ public interface AdapterTransferProcessService { * @return The result containing the contract negotiation id */ ServiceResult initiateEdrNegotiation(NegotiateEdrRequest request); + + /** + * Return a {@link EndpointDataReference} associated with the transferProcessId in input + * + * @param transferProcessId The transferProcessId + * @return The result containing the {@link EndpointDataReference} + */ + ServiceResult findByTransferProcessId(String transferProcessId); + + ServiceResult> findByAssetAndAgreement(String assetId, String agreementId); + } diff --git a/spi/core-spi/build.gradle.kts b/spi/core-spi/build.gradle.kts new file mode 100644 index 000000000..75b96d9ae --- /dev/null +++ b/spi/core-spi/build.gradle.kts @@ -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 + * + */ + +plugins { + `java-library` +} + +dependencies { + implementation(libs.edc.spi.core) +} 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 new file mode 100644 index 000000000..5d05db6ee --- /dev/null +++ b/spi/core-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/CoreConstants.java @@ -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 + * + */ + +package org.eclipse.tractusx.edc.edr.spi; + +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/"; + + private CoreConstants() { + } +} diff --git a/spi/edr-cache-spi/build.gradle.kts b/spi/edr-cache-spi/build.gradle.kts index 9ca2f9437..a08b24ee3 100644 --- a/spi/edr-cache-spi/build.gradle.kts +++ b/spi/edr-cache-spi/build.gradle.kts @@ -14,9 +14,16 @@ plugins { `java-library` + `java-test-fixtures` } dependencies { + implementation(project(":spi:core-spi")) implementation(libs.edc.spi.core) + + testFixturesImplementation(libs.edc.junit) + testFixturesImplementation(libs.junit.jupiter.api) + testFixturesImplementation(libs.assertj) + } diff --git a/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCache.java b/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCache.java index 67e533bf3..bf0f3dc37 100644 --- a/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCache.java +++ b/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCache.java @@ -14,12 +14,14 @@ package org.eclipse.tractusx.edc.edr.spi; +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.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.stream.Stream; /** * Caches and resolves {@link EndpointDataReference}s @@ -39,10 +41,10 @@ public interface EndpointDataReferenceCache { List referencesForAsset(String assetId); /** - * Returns the {@link EndpointDataReferenceEntry}s for the asset. + * Returns all the EDR entries in the store that are covered by a given {@link QuerySpec}. */ - @NotNull - List entriesForAsset(String assetId); + + Stream queryForEntries(QuerySpec spec); /** * Saves an {@link EndpointDataReference} to the cache using upsert semantics. 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 index 617c856cc..6351bca79 100644 --- 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 @@ -19,13 +19,25 @@ 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; @@ -55,6 +67,11 @@ public boolean equals(Object 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; 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 new file mode 100644 index 000000000..5a72a535a --- /dev/null +++ b/spi/edr-cache-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCacheBaseTest.java @@ -0,0 +1,130 @@ +/* + * 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/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/TestFunctions.java b/spi/edr-cache-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/TestFunctions.java new file mode 100644 index 000000000..bd9a0b221 --- /dev/null +++ b/spi/edr-cache-spi/src/testFixtures/java/org/eclipse/tractusx/edc/edr/spi/TestFunctions.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.spi.types.domain.edr.EndpointDataReference; + +public class TestFunctions { + + + public static EndpointDataReference edr(String id) { + return EndpointDataReference.Builder.newInstance() + .endpoint("http://test.com") + .id(id) + .authCode("11111") + .authKey("authentication").build(); + } + + public static EndpointDataReferenceEntry edrEntry(String assetId, String agreementId, String transferProcessId) { + return EndpointDataReferenceEntry.Builder.newInstance() + .assetId(assetId) + .agreementId(agreementId) + .transferProcessId(transferProcessId) + .build(); + } +}