diff --git a/.github/workflows/deployment-test.yaml b/.github/workflows/deployment-test.yaml index 3060eb940..d1a9dc501 100644 --- a/.github/workflows/deployment-test.yaml +++ b/.github/workflows/deployment-test.yaml @@ -103,8 +103,8 @@ jobs: test-azure-vault-postgres: runs-on: ubuntu-latest needs: [ test-prepare, secret-presence ] - if: | - needs.secret-presence.outputs.AZURE_KV_CREDS + # disabled cause secret expired + if: false steps: - name: Checkout uses: actions/checkout@v3.3.0 diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index b6e6858da..ce1aebc47 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -79,18 +79,6 @@ jobs: run: | ./gradlew checkstyleMain checkstyleTest - markdown-lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3.5.2 - - - name: Install mardkdownlint - run: npm install -g markdownlint-cli2 - - - name: Run markdownlint - run: | - markdownlint-cli2-config .markdownlint.yaml "**/*.md" "#.github" "#charts" - unit-tests: runs-on: ubuntu-latest needs: [ verify-formatting, verify-license-headers ] diff --git a/DEPENDENCIES b/DEPENDENCIES index 7ae8fd739..6df0c1a19 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -130,7 +130,7 @@ maven/mavencentral/io.opentelemetry.instrumentation/opentelemetry-instrumentatio maven/mavencentral/io.opentelemetry/opentelemetry-api/1.29.0, Apache-2.0, approved, #10088 maven/mavencentral/io.opentelemetry/opentelemetry-context/1.29.0, Apache-2.0, approved, #10090 maven/mavencentral/io.projectreactor.netty/reactor-netty-core/1.0.33, Apache-2.0, approved, #9687 -maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.33, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.33, Apache-2.0, approved, #11661 maven/mavencentral/io.projectreactor/reactor-core/3.4.30, Apache-2.0, approved, #7517 maven/mavencentral/io.rest-assured/json-path/5.3.1, Apache-2.0, approved, #9261 maven/mavencentral/io.rest-assured/rest-assured-common/5.3.1, Apache-2.0, approved, #9264 @@ -207,7 +207,6 @@ maven/mavencentral/org.eclipse.edc/asset-api/0.2.1, Apache-2.0, approved, techno maven/mavencentral/org.eclipse.edc/asset-index-sql/0.2.1, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/asset-spi/0.2.1, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/auth-spi/0.2.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/auth-tokenbased/0.2.1, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/autodoc-processor/0.2.1, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/aws-s3-core/0.2.1, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/boot/0.2.1, Apache-2.0, approved, technology.edc diff --git a/charts/tractusx-connector-azure-vault/Chart.yaml b/charts/tractusx-connector-azure-vault/Chart.yaml index 64caa362d..7bd745db5 100644 --- a/charts/tractusx-connector-azure-vault/Chart.yaml +++ b/charts/tractusx-connector-azure-vault/Chart.yaml @@ -40,12 +40,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.5.3 +version: 0.5.4 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.5.3" +appVersion: "0.5.4" home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector sources: - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector diff --git a/charts/tractusx-connector-azure-vault/README.md b/charts/tractusx-connector-azure-vault/README.md index 24b1935aa..77c37956b 100644 --- a/charts/tractusx-connector-azure-vault/README.md +++ b/charts/tractusx-connector-azure-vault/README.md @@ -1,6 +1,6 @@ # tractusx-connector-azure-vault -![Version: 0.5.3](https://img.shields.io/badge/Version-0.5.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.3](https://img.shields.io/badge/AppVersion-0.5.3-informational?style=flat-square) +![Version: 0.5.4](https://img.shields.io/badge/Version-0.5.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.4](https://img.shields.io/badge/AppVersion-0.5.4-informational?style=flat-square) A Helm chart for Tractus-X Eclipse Data Space Connector. The connector deployment consists of two runtime consists of a Control Plane and a Data Plane. Note that _no_ external dependencies such as a PostgreSQL database and Azure KeyVault are included. @@ -45,7 +45,7 @@ Combined, run this shell command to start the in-memory Tractus-X EDC runtime: ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0.5.3 \ +helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0.5.4 \ -f /tractusx-connector-azure-vault-test.yaml \ --set vault.azure.name=$AZURE_VAULT_NAME \ --set vault.azure.client=$AZURE_CLIENT_ID \ @@ -78,6 +78,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.edr.transferProxyTokenValidity | string | `"2592000"` | | | controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"","path":"/management","port":8081},"metrics":{"path":"/metrics","port":9090},"protocol":{"path":"/api/v1/dsp","port":8084}}` | endpoints of the control plane | | controlplane.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | | controlplane.endpoints.control.path | string | `"/control"` | path for incoming api calls | diff --git a/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml b/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml index e5426d115..e7118d147 100644 --- a/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml @@ -296,6 +296,8 @@ spec: - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | quote }} {{- end }} + - name: "EDC_TRANSFER_PROXY_TOKEN_VALIDITY_SECONDS" + value: {{ .Values.controlplane.edr.transferProxyTokenValidity | required ".Values.controlplane.edr.transferProxyTokenValidity is required" | quote }} # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/transfer/transfer-pull-http-dynamic-receiver diff --git a/charts/tractusx-connector-azure-vault/values.yaml b/charts/tractusx-connector-azure-vault/values.yaml index 0c157c2b1..7dba78f36 100644 --- a/charts/tractusx-connector-azure-vault/values.yaml +++ b/charts/tractusx-connector-azure-vault/values.yaml @@ -117,7 +117,8 @@ controlplane: businessPartnerValidation: log: agreementValidation: true - + edr: + transferProxyTokenValidity: "2592000" # SSI configuration ssi: miw: diff --git a/charts/tractusx-connector-memory/Chart.yaml b/charts/tractusx-connector-memory/Chart.yaml index 6af11114f..90431b664 100644 --- a/charts/tractusx-connector-memory/Chart.yaml +++ b/charts/tractusx-connector-memory/Chart.yaml @@ -34,12 +34,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.5.3 +version: 0.5.4 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.5.3" +appVersion: "0.5.4" home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-memory sources: - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-memory diff --git a/charts/tractusx-connector-memory/README.md b/charts/tractusx-connector-memory/README.md index e1adb1b49..5523235a7 100644 --- a/charts/tractusx-connector-memory/README.md +++ b/charts/tractusx-connector-memory/README.md @@ -1,6 +1,6 @@ # tractusx-connector-memory -![Version: 0.5.3](https://img.shields.io/badge/Version-0.5.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.3](https://img.shields.io/badge/AppVersion-0.5.3-informational?style=flat-square) +![Version: 0.5.4](https://img.shields.io/badge/Version-0.5.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.4](https://img.shields.io/badge/AppVersion-0.5.4-informational?style=flat-square) A Helm chart for Tractus-X Eclipse Data Space Connector based on memory. Please only use this for development or testing purposes, never in production workloads! @@ -39,7 +39,7 @@ Combined, run this shell command to start the in-memory Tractus-X EDC runtime: ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector-memory --version 0.5.3 \ +helm install my-release tractusx-edc/tractusx-connector-memory --version 0.5.4 \ -f /tractusx-connector-memory-test.yaml \ --set vault.secrets="client-secret:$YOUR_CLIENT_SECRET" ``` @@ -77,6 +77,7 @@ helm install my-release tractusx-edc/tractusx-connector-memory --version 0.5.3 \ | runtime.debug.enabled | bool | `false` | | | runtime.debug.port | int | `1044` | | | runtime.debug.suspendOnStart | bool | `false` | | +| runtime.edr.transferProxyTokenValidity | string | `"2592000"` | | | runtime.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"","path":"/management","port":8081},"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 | diff --git a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml index 4df159470..0735345ec 100644 --- a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml @@ -244,6 +244,8 @@ spec: - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | quote }} {{- end }} + - name: "EDC_TRANSFER_PROXY_TOKEN_VALIDITY_SECONDS" + value: {{ .Values.runtime.edr.transferProxyTokenValidity | required ".Values.runtime.edr.transferProxyTokenValidity is required" | quote }} # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/http-receiver - name: "EDC_RECEIVER_HTTP_ENDPOINT" diff --git a/charts/tractusx-connector-memory/values.yaml b/charts/tractusx-connector-memory/values.yaml index d22884373..432f5ba4a 100644 --- a/charts/tractusx-connector-memory/values.yaml +++ b/charts/tractusx-connector-memory/values.yaml @@ -118,7 +118,8 @@ runtime: businessPartnerValidation: log: agreementValidation: true - + edr: + transferProxyTokenValidity: "2592000" # SSI configuration ssi: miw: diff --git a/charts/tractusx-connector/Chart.yaml b/charts/tractusx-connector/Chart.yaml index da8e21751..52d2d405d 100644 --- a/charts/tractusx-connector/Chart.yaml +++ b/charts/tractusx-connector/Chart.yaml @@ -40,12 +40,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.5.3 +version: 0.5.4 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.5.3" +appVersion: "0.5.4" home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector sources: - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector diff --git a/charts/tractusx-connector/README.md b/charts/tractusx-connector/README.md index e280f1e7c..e8f8c5298 100644 --- a/charts/tractusx-connector/README.md +++ b/charts/tractusx-connector/README.md @@ -1,6 +1,6 @@ # tractusx-connector -![Version: 0.5.3](https://img.shields.io/badge/Version-0.5.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.3](https://img.shields.io/badge/AppVersion-0.5.3-informational?style=flat-square) +![Version: 0.5.4](https://img.shields.io/badge/Version-0.5.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.4](https://img.shields.io/badge/AppVersion-0.5.4-informational?style=flat-square) A Helm chart for Tractus-X Eclipse Data Space Connector. The connector deployment consists of two runtime consists of a Control Plane and a Data Plane. Note that _no_ external dependencies such as a PostgreSQL database and HashiCorp Vault are included. @@ -42,7 +42,7 @@ Combined, run this shell command to start the in-memory Tractus-X EDC runtime: ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector --version 0.5.3 \ +helm install my-release tractusx-edc/tractusx-connector --version 0.5.4 \ -f /tractusx-connector-test.yaml ``` @@ -72,6 +72,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.5.3 \ | controlplane.debug.enabled | bool | `false` | | | controlplane.debug.port | int | `1044` | | | controlplane.debug.suspendOnStart | bool | `false` | | +| controlplane.edr.transferProxyTokenValidity | string | `"2592000"` | | | controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"","path":"/management","port":8081},"metrics":{"path":"/metrics","port":9090},"protocol":{"path":"/api/v1/dsp","port":8084}}` | endpoints of the control plane | | controlplane.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | | controlplane.endpoints.control.path | string | `"/control"` | path for incoming api calls | diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 830bf349f..c6a0884da 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -296,6 +296,9 @@ spec: - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | quote }} {{- end }} + - name: "EDC_TRANSFER_PROXY_TOKEN_VALIDITY_SECONDS" + value: {{ .Values.controlplane.edr.transferProxyTokenValidity | required ".Values.controlplane.edr.transferProxyTokenValidity is required" | quote }} + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/transfer/transfer-pull-http-dynamic-receiver - name: "EDC_RECEIVER_HTTP_DYNAMIC_ENDPOINT" diff --git a/charts/tractusx-connector/values.yaml b/charts/tractusx-connector/values.yaml index b98fc645b..65d74aa49 100644 --- a/charts/tractusx-connector/values.yaml +++ b/charts/tractusx-connector/values.yaml @@ -117,7 +117,8 @@ controlplane: businessPartnerValidation: log: agreementValidation: true - + edr: + transferProxyTokenValidity: "2592000" # SSI configuration ssi: miw: diff --git a/edc-controlplane/edc-controlplane-base/build.gradle.kts b/edc-controlplane/edc-controlplane-base/build.gradle.kts index f8bc52809..6c3d55709 100644 --- a/edc-controlplane/edc-controlplane-base/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-base/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { runtimeOnly(project(":edc-extensions:edr:edr-api")) runtimeOnly(project(":edc-extensions:edr:edr-callback")) + runtimeOnly(project(":edc-extensions:auth-tokenbased")) // needed for BPN validation runtimeOnly(project(":edc-extensions:bpn-validation")) @@ -44,7 +45,6 @@ dependencies { runtimeOnly(libs.edc.core.controlplane) runtimeOnly(libs.edc.config.filesystem) - runtimeOnly(libs.edc.auth.tokenbased) runtimeOnly(libs.edc.api.management) runtimeOnly(libs.edc.api.management.config) diff --git a/edc-dataplane/edc-dataplane-base/build.gradle.kts b/edc-dataplane/edc-dataplane-base/build.gradle.kts index 03a5ee3db..2d029ea09 100644 --- a/edc-dataplane/edc-dataplane-base/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-base/build.gradle.kts @@ -23,13 +23,13 @@ plugins { } dependencies { + runtimeOnly(project(":edc-extensions:auth-tokenbased")) runtimeOnly(project(":core:edr-cache-core")) runtimeOnly(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-consumer-api")) runtimeOnly(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-api")) runtimeOnly(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-provider-core")) runtimeOnly(libs.edc.config.filesystem) - runtimeOnly(libs.edc.auth.tokenbased) runtimeOnly(libs.edc.dpf.awss3) runtimeOnly(libs.edc.dpf.oauth2) runtimeOnly(libs.edc.dpf.http) diff --git a/edc-extensions/auth-tokenbased/README.md b/edc-extensions/auth-tokenbased/README.md new file mode 100644 index 000000000..a19a72002 --- /dev/null +++ b/edc-extensions/auth-tokenbased/README.md @@ -0,0 +1,21 @@ +# Token Based Authentication Service + +The token based authentication service extension is used to secure connector APIs. These APIs are not protected by the `AuthenticationService` by default. To find out how a specific API is protected please consult its documentation. + +APIs, protected by this extension, require a client to authenticate by adding a authentication key to the request header. + +Authentication Header Example: +``` +curl --header "X-API-Key: " +``` + +## Configuration + +| Key | Description | Required | +|:-----------------------|:-------------------------------------------------------------|:---------| +| edc.api.auth.key | API Key Header Value | false | +| edc.api.auth.key.alias | Secret name of the API Key Header Value, stored in the vault | false | + +- If the API key is stored in the Vault _and_ in the configuration, the extension will take the key from the vault. + +- If no API key is defined, a random value is generated and printed out into the logs. \ No newline at end of file diff --git a/edc-extensions/auth-tokenbased/build.gradle.kts b/edc-extensions/auth-tokenbased/build.gradle.kts new file mode 100644 index 000000000..5e1798077 --- /dev/null +++ b/edc-extensions/auth-tokenbased/build.gradle.kts @@ -0,0 +1,26 @@ +/* + * 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 + * + */ + +plugins { + `java-library` +} + +dependencies { + implementation(libs.edc.spi.auth) + implementation(libs.jakarta.rsApi) + + testImplementation(testFixtures(libs.edc.junit)) +} + + diff --git a/edc-extensions/auth-tokenbased/src/main/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationExtension.java b/edc-extensions/auth-tokenbased/src/main/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationExtension.java new file mode 100644 index 000000000..5a299eb8f --- /dev/null +++ b/edc-extensions/auth-tokenbased/src/main/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationExtension.java @@ -0,0 +1,65 @@ +/* + * 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 + * Mercedes-Benz Tech Innovation GmbH - add README.md; authentication key can be retrieved from vault + * Fraunhofer Institute for Software and Systems Engineering - update monitor info + * + */ + +package org.eclipse.tractusx.edc.api.auth.token; + +import org.eclipse.edc.api.auth.spi.AuthenticationService; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Provides; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; + +import java.util.UUID; + +/** + * Extension that registers an AuthenticationService that uses API Keys + */ +@Provides(AuthenticationService.class) +@Extension(value = TokenBasedAuthenticationExtension.NAME) +public class TokenBasedAuthenticationExtension implements ServiceExtension { + + public static final String NAME = "Static token API Authentication"; + @Setting + private static final String AUTH_SETTING_APIKEY = "edc.api.auth.key"; + @Setting + private static final String AUTH_SETTING_APIKEY_ALIAS = "edc.api.auth.key.alias"; + @Inject + private Vault vault; + + @Override + public String name() { + return NAME; + } + + @Override + public void initialize(ServiceExtensionContext context) { + String apiKey = null; + + var apiKeyAlias = context.getSetting(AUTH_SETTING_APIKEY_ALIAS, null); + if (apiKeyAlias != null) { + apiKey = vault.resolveSecret(apiKeyAlias); + } + + if (apiKey == null) { + apiKey = context.getSetting(AUTH_SETTING_APIKEY, UUID.randomUUID().toString()); + } + + context.registerService(AuthenticationService.class, new TokenBasedAuthenticationService(apiKey)); + } +} diff --git a/edc-extensions/auth-tokenbased/src/main/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationService.java b/edc-extensions/auth-tokenbased/src/main/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationService.java new file mode 100644 index 000000000..06ed866cc --- /dev/null +++ b/edc-extensions/auth-tokenbased/src/main/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationService.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.api.auth.token; + +import org.eclipse.edc.api.auth.spi.AuthenticationService; +import org.eclipse.edc.web.spi.exception.AuthenticationFailedException; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class TokenBasedAuthenticationService implements AuthenticationService { + + private static final String API_KEY_HEADER_NAME = "x-api-key"; + private final String hardCodedApiKey; //todo: have a list of API keys? + + public TokenBasedAuthenticationService(String hardCodedApiKey) { + this.hardCodedApiKey = hardCodedApiKey; + } + + /** + * Checks whether a particular request is authorized based on the "X-Api-Key" header. + * + * @param headers The headers, that have to contain the "X-Api-Key" header. + * @throws IllegalArgumentException The map of headers did not contain the "X-Api-Key" header + */ + @Override + public boolean isAuthenticated(Map> headers) { + + Objects.requireNonNull(headers, "headers"); + + var apiKey = headers.keySet().stream() + .filter(k -> k.equalsIgnoreCase(API_KEY_HEADER_NAME)) + .map(headers::get) + .findFirst(); + + return apiKey.map(this::checkApiKeyValid).orElseThrow(() -> new AuthenticationFailedException(API_KEY_HEADER_NAME + " not found")); + } + + private boolean checkApiKeyValid(List apiKeys) { + return apiKeys.size() == 1 && apiKeys.stream().allMatch(hardCodedApiKey::equalsIgnoreCase); + } +} diff --git a/edc-extensions/auth-tokenbased/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/auth-tokenbased/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..2b47ab2dd --- /dev/null +++ b/edc-extensions/auth-tokenbased/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,15 @@ + # +# 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 +# +# + +org.eclipse.tractusx.edc.api.auth.token.TokenBasedAuthenticationExtension diff --git a/edc-extensions/auth-tokenbased/src/test/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationExtensionTest.java b/edc-extensions/auth-tokenbased/src/test/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationExtensionTest.java new file mode 100644 index 000000000..4cfcd052c --- /dev/null +++ b/edc-extensions/auth-tokenbased/src/test/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationExtensionTest.java @@ -0,0 +1,80 @@ +/* + * 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 implementation + * + */ + +package org.eclipse.tractusx.edc.api.auth.token; + +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(DependencyInjectionExtension.class) +public class TokenBasedAuthenticationExtensionTest { + + private static final String AUTH_SETTING_APIKEY = "edc.api.auth.key"; + private static final String AUTH_SETTING_APIKEY_ALIAS = "edc.api.auth.key.alias"; + private static final String VAULT_KEY = "foo"; + + private final Vault vault = mock(); + + @BeforeEach + void setup(ServiceExtensionContext context) { + context.registerService(Vault.class, vault); + + when(vault.resolveSecret(VAULT_KEY)).thenReturn("foo"); + } + + @Test + public void testPrimaryMethod_loadKeyFromVault(ServiceExtensionContext context, TokenBasedAuthenticationExtension extension) { + when(context.getSetting(eq(AUTH_SETTING_APIKEY_ALIAS), isNull())).thenReturn(VAULT_KEY); + when(context.getSetting(eq(AUTH_SETTING_APIKEY), anyString())).thenReturn("bar"); + + extension.initialize(context); + + verify(context, never()) + .getSetting(eq(AUTH_SETTING_APIKEY), anyString()); + + verify(context) + .getSetting(AUTH_SETTING_APIKEY_ALIAS, null); + + verify(vault).resolveSecret(VAULT_KEY); + } + + @Test + public void testSecondaryMethod_loadKeyFromConfig(ServiceExtensionContext context, TokenBasedAuthenticationExtension extension) { + when(context.getSetting(eq(AUTH_SETTING_APIKEY_ALIAS), isNull())).thenReturn(null); + when(context.getSetting(eq(AUTH_SETTING_APIKEY), anyString())).thenReturn("bar"); + + extension.initialize(context); + + verify(context) + .getSetting(eq(AUTH_SETTING_APIKEY), anyString()); + + verify(context) + .getSetting(AUTH_SETTING_APIKEY_ALIAS, null); + + verify(vault, never()).resolveSecret(anyString()); + } + +} diff --git a/edc-extensions/auth-tokenbased/src/test/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationServiceTest.java b/edc-extensions/auth-tokenbased/src/test/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationServiceTest.java new file mode 100644 index 000000000..4304ea50b --- /dev/null +++ b/edc-extensions/auth-tokenbased/src/test/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationServiceTest.java @@ -0,0 +1,76 @@ +/* + * 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.api.auth.token; + +import org.eclipse.edc.web.spi.exception.AuthenticationFailedException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class TokenBasedAuthenticationServiceTest { + + private static final String TEST_API_KEY = "test-key"; + private TokenBasedAuthenticationService service; + + @BeforeEach + void setUp() { + service = new TokenBasedAuthenticationService(TEST_API_KEY); + } + + @ParameterizedTest + @ValueSource(strings = { "x-api-key", "X-API-KEY", "X-Api-Key" }) + void isAuthorized(String validKey) { + var map = Map.of(validKey, List.of(TEST_API_KEY)); + assertThat(service.isAuthenticated(map)).isTrue(); + } + + @Test + void isAuthorized_headerNotPresent_throwsException() { + var map = Map.of("header1", List.of("val1, val2"), + "header2", List.of("anotherval1", "anotherval2")); + assertThatThrownBy(() -> service.isAuthenticated(map)).isInstanceOf(AuthenticationFailedException.class).hasMessage("x-api-key not found"); + } + + @Test + void isAuthorized_headersEmpty_throwsException() { + Map> map = Collections.emptyMap(); + assertThatThrownBy(() -> service.isAuthenticated(map)).isInstanceOf(AuthenticationFailedException.class).hasMessage("x-api-key not found"); + } + + @Test + void isAuthorized_headersNull_throwsException() { + assertThatThrownBy(() -> service.isAuthenticated(null)).isInstanceOf(NullPointerException.class); + } + + @Test + void isAuthorized_notAuthorized() { + var map = Map.of("x-api-key", List.of("invalid_api_key")); + assertThat(service.isAuthenticated(map)).isFalse(); + } + + @Test + void isAuthorized_multipleValues_oneAuthorized_shouldReturnFalse() { + var map = Map.of("x-api-key", List.of("invalid_api_key", TEST_API_KEY)); + assertThat(service.isAuthenticated(map)).isFalse(); + } +} \ No newline at end of file diff --git a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallback.java b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallback.java index 50c1791a5..dc2262bb9 100644 --- a/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallback.java +++ b/edc-extensions/edr/edr-callback/src/main/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallback.java @@ -17,6 +17,7 @@ import com.nimbusds.jwt.SignedJWT; import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; import org.eclipse.edc.connector.transfer.spi.event.TransferProcessStarted; +import org.eclipse.edc.connector.transfer.spi.event.TransferProcessTerminated; import org.eclipse.edc.connector.transfer.spi.store.TransferProcessStore; import org.eclipse.edc.spi.event.Event; import org.eclipse.edc.spi.monitor.Monitor; @@ -35,6 +36,7 @@ import java.time.ZoneOffset; import static java.lang.String.format; +import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.REFRESHING; public class TransferProcessLocalCallback implements InProcessCallback { @@ -56,14 +58,43 @@ public TransferProcessLocalCallback(EndpointDataReferenceCache edrCache, Transfe @Override public Result invoke(CallbackEventRemoteMessage message) { - if (message.getEventEnvelope().getPayload() instanceof TransferProcessStarted transferProcessStarted) { - if (transferProcessStarted.getDataAddress() != null) { - return transformerRegistry.transform(transferProcessStarted.getDataAddress(), EndpointDataReference.class) - .compose(this::storeEdr) - .mapTo(); - } + if (message.getEventEnvelope().getPayload() instanceof TransferProcessStarted transferProcessStarted && transferProcessStarted.getDataAddress() != null) { + return transformerRegistry.transform(transferProcessStarted.getDataAddress(), EndpointDataReference.class) + .compose(this::storeEdr) + .mapTo(); + } else if (message.getEventEnvelope().getPayload() instanceof TransferProcessTerminated terminated && terminated.getReason() != null) { + return handleTransferProcessTermination(terminated); + } else { + return Result.success(); } - return Result.success(); + } + + private Result handleTransferProcessTermination(TransferProcessTerminated terminated) { + return transactionContext.execute(() -> { + var transferProcess = transferProcessStore.findById(terminated.getTransferProcessId()); + if (transferProcess != null) { + stopEdrNegotiation(transferProcess.getDataRequest().getAssetId(), transferProcess.getDataRequest().getContractId(), terminated.getReason()); + return Result.success(); + } else { + return Result.failure(format("Failed to find a transfer process with ID %s", terminated.getTransferProcessId())); + } + }); + + } + + private void stopEdrNegotiation(String assetId, String agreementId, String errorDetail) { + var querySpec = QuerySpec.Builder.newInstance() + .filter(fieldFilter("agreementId", agreementId)) + .filter(fieldFilter("assetId", assetId)) + .filter(fieldFilter("state", REFRESHING.code())) + .build(); + + edrCache.queryForEntries(querySpec).forEach((entry -> { + monitor.debug(format("Transitioning EDR to Error Refreshing for transfer process %s", entry.getTransferProcessId())); + entry.setErrorDetail(errorDetail); + entry.transitionError(); + edrCache.update(entry); + })); } private Result storeEdr(EndpointDataReference edr) { @@ -105,6 +136,15 @@ private void cleanOldEdr(String assetId, String agreementId) { monitor.debug(format("Expiring EDR for transfer process %s", entry.getTransferProcessId())); entry.transitionToExpired(); edrCache.update(entry); + + var transferProcess = transferProcessStore.findById(entry.getTransferProcessId()); + + if (transferProcess != null && transferProcess.canBeTerminated()) { + transferProcess.transitionTerminating(); + transferProcessStore.save(transferProcess); + } else { + monitor.info(format("Cannot terminate transfer process with id: %s", entry.getTransferProcessId())); + } })); } @@ -126,7 +166,7 @@ private Result extractExpirationTime(EndpointDataReference edr) { return Result.success(0L); } - private Criterion fieldFilter(String field, String value) { + private Criterion fieldFilter(String field, Object value) { return Criterion.Builder.newInstance() .operandLeft(field) .operator("=") diff --git a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TestFunctions.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TestFunctions.java index a44b2fe4e..1884b9d60 100644 --- a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TestFunctions.java +++ b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TestFunctions.java @@ -26,6 +26,7 @@ import org.eclipse.edc.connector.contract.spi.types.agreement.ContractAgreement; import org.eclipse.edc.connector.spi.callback.CallbackEventRemoteMessage; import org.eclipse.edc.connector.transfer.spi.event.TransferProcessStarted; +import org.eclipse.edc.connector.transfer.spi.event.TransferProcessTerminated; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.event.Event; import org.eclipse.edc.spi.event.EventEnvelope; @@ -80,6 +81,18 @@ public static TransferProcessStarted getTransferProcessStartedEvent(DataAddress .build(); } + public static TransferProcessTerminated getTransferTerminatedEvent(String transferProcessId, String reason) { + return TransferProcessTerminated.Builder.newInstance() + .callbackAddresses(List.of(CallbackAddress.Builder.newInstance() + .uri("local://test") + .events(Set.of("test")) + .transactional(true) + .build())) + .reason(reason) + .transferProcessId(transferProcessId) + .build(); + } + public static EndpointDataReference getEdr() { return EndpointDataReference.Builder.newInstance() .id("dataRequestId") diff --git a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallbackTest.java b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallbackTest.java index 19c5d280e..5d0668dfb 100644 --- a/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallbackTest.java +++ b/edc-extensions/edr/edr-callback/src/test/java/org/eclipse/tractusx/edc/callback/TransferProcessLocalCallbackTest.java @@ -51,6 +51,7 @@ import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.EDR_SIMPLE_TYPE; import static org.eclipse.tractusx.edc.callback.TestFunctions.getEdr; import static org.eclipse.tractusx.edc.callback.TestFunctions.getTransferProcessStartedEvent; +import static org.eclipse.tractusx.edc.callback.TestFunctions.getTransferTerminatedEvent; import static org.eclipse.tractusx.edc.callback.TestFunctions.remoteMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; @@ -122,7 +123,7 @@ void invoke_shouldStoreTheEdrInCache_whenDataAddressIsPresent() { verify(edrCache).save(cacheEntryCaptor.capture(), edrCaptor.capture()); verify(edrCache).update(argThat(entry -> entry.getState() == EndpointDataReferenceEntryStates.EXPIRED.code())); - + assertThat(edrCaptor.getValue()).usingRecursiveComparison().isEqualTo(edr); } @@ -179,6 +180,46 @@ void invoke_shouldFail_withInvalidDataAddress() { verifyNoInteractions(transferProcessStore); } + @Test + void invoke_shouldStopEdrNegotiation_whenTerminatedMessageReceived() { + + var transferProcessId = "transferProcessId"; + var assetId = "assetId"; + var contractId = "contractId"; + var edr = getEdr(); + + var dataRequest = DataRequest.Builder.newInstance().id(edr.getId()) + .destinationType("HttpProxy") + .assetId(assetId) + .contractId(contractId) + .build(); + + var transferProcess = TransferProcess.Builder.newInstance() + .id(transferProcessId) + .dataRequest(dataRequest) + .build(); + + var edrEntry = EndpointDataReferenceEntry.Builder.newInstance() + .agreementId(contractId) + .transferProcessId(transferProcessId) + .assetId(assetId) + .state(EndpointDataReferenceEntryStates.REFRESHING.code()) + .build(); + + when(transferProcessStore.findById(transferProcessId)).thenReturn(transferProcess); + when(edrCache.queryForEntries(any())).thenReturn(Stream.of(edrEntry)); + + var event = getTransferTerminatedEvent(transferProcessId, "Failure"); + var message = remoteMessage(event); + + var result = callback.invoke(message); + assertThat(result.succeeded()).isTrue(); + + verify(edrCache).update(argThat(entry -> entry.getState() == EndpointDataReferenceEntryStates.ERROR.code())); + + } + + @ParameterizedTest @ArgumentsSource(EventInstances.class) void invoke_shouldIgnoreOtherEvents(TransferProcessEvent event) { diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImpl.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImpl.java index c8de50c4b..45dde81fa 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImpl.java +++ b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImpl.java @@ -36,6 +36,7 @@ import java.util.Set; import static java.lang.String.format; +import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwFallbackFactories.retryWhenStatusIsNotIn; public class MiwApiClientImpl implements MiwApiClient { @@ -144,11 +145,7 @@ private Result handleVerifyResult(Map response) { } private Result executeRequest(Request request, TypeReference typeReference) { - try (var response = httpClient.execute(request)) { - return handleResponse(response, typeReference); - } catch (IOException e) { - return Result.failure(e.getMessage()); - } + return httpClient.execute(request, List.of(retryWhenStatusIsNotIn(200, 201)), (response -> handleResponse(response, typeReference))); } private Result handleResponse(Response response, TypeReference tr) { diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwFallbackFactories.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwFallbackFactories.java new file mode 100644 index 000000000..351267239 --- /dev/null +++ b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwFallbackFactories.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.iam.ssi.miw.api; + +import dev.failsafe.Fallback; +import dev.failsafe.event.ExecutionAttemptedEvent; +import dev.failsafe.function.CheckedFunction; +import okhttp3.Response; +import org.eclipse.edc.spi.http.EdcHttpClientException; +import org.eclipse.edc.spi.http.FallbackFactory; + +import java.util.Arrays; +import java.util.stream.Collectors; + +import static java.lang.String.format; + +public class MiwFallbackFactories { + + static FallbackFactory retryWhenStatusIsNot(int status) { + return retryWhenStatusIsNotIn(status); + } + + /** + * Verifies that the response has a specific statuses, otherwise it should be retried + * + * @return the {@link FallbackFactory} + */ + static FallbackFactory retryWhenStatusIsNotIn(int... status) { + var codes = Arrays.stream(status).boxed().collect(Collectors.toSet()); + return request -> { + CheckedFunction, Exception> exceptionSupplier = event -> { + var response = event.getLastResult(); + if (response == null) { + return new EdcHttpClientException(event.getLastException().getMessage()); + } else { + return new EdcHttpClientException(format("Server response to [%s, %s] was not one of %s but was %s", request.method(), request.url(), Arrays.toString(status), response.code())); + } + }; + return Fallback.builderOfException(exceptionSupplier) + .handleResultIf(r -> !codes.contains(r.code())) + .build(); + }; + } +} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImplTest.java b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImplTest.java index ebec7cc35..633552e35 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImplTest.java +++ b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/api/MiwApiClientImplTest.java @@ -49,10 +49,8 @@ import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl.PRESENTATIONS_VALIDATION_PATH; import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl.VERIFIABLE_CREDENTIALS; import static org.eclipse.tractusx.edc.iam.ssi.miw.api.MiwApiClientImpl.VP_FIELD; -import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class MiwApiClientImplTest { @@ -158,7 +156,6 @@ void createPresentation_fails_whenMiwFails() throws IOException { assertThat(result).isNotNull().matches(Result::failed); - verify(monitor).severe(contains("Request Failed")); } @Test 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 36b722640..7c2eafa1b 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 @@ -283,6 +283,17 @@ public String getTransferProcessState(String id) { .extract().body().jsonPath().getString("'edc:state'"); } + public JsonArray getAllTransferProcess() { + return baseRequest() + .when() + .post("/v2/transferprocesses/request") + .then() + .statusCode(200) + .extract() + .body() + .as(JsonArray.class); + } + public EndpointDataReference getDataReference(String dataRequestId) { var dataReference = new AtomicReference(); diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java index 9e6d87a40..93832459d 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java @@ -22,7 +22,6 @@ import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationInitiated; import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationRequested; import org.eclipse.edc.connector.contract.spi.event.contractnegotiation.ContractNegotiationVerified; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessCompleted; import org.eclipse.edc.connector.transfer.spi.event.TransferProcessInitiated; import org.eclipse.edc.connector.transfer.spi.event.TransferProcessProvisioned; import org.eclipse.edc.connector.transfer.spi.event.TransferProcessRequested; @@ -78,8 +77,7 @@ void negotiateEdr_shouldInvokeCallbacks() throws IOException { createEvent(TransferProcessInitiated.class), createEvent(TransferProcessProvisioned.class), createEvent(TransferProcessRequested.class), - createEvent(TransferProcessStarted.class), - createEvent(TransferProcessCompleted.class)); + createEvent(TransferProcessStarted.class)); var assetId = "api-asset-1"; var url = server.url("/mock/api"); diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractRenewalEdrTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractRenewalEdrTest.java index d0c9d1b9c..3c31ac5b5 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractRenewalEdrTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractRenewalEdrTest.java @@ -19,7 +19,8 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.assertj.core.api.Condition; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessCompleted; +import org.eclipse.edc.connector.transfer.spi.event.TransferProcessStarted; +import org.eclipse.edc.connector.transfer.spi.types.TransferProcessStates; import org.eclipse.edc.policy.model.Operator; import org.eclipse.tractusx.edc.lifecycle.Participant; import org.junit.jupiter.api.AfterEach; @@ -38,6 +39,7 @@ import static org.assertj.core.api.Assertions.anyOf; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import static org.awaitility.pollinterval.FibonacciPollInterval.fibonacci; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.EXPIRED; import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntryStates.NEGOTIATED; @@ -70,8 +72,8 @@ void setup() { void negotiateEdr_shouldRenewTheEdr() throws IOException { var expectedEvents = List.of( - createEvent(TransferProcessCompleted.class), - createEvent(TransferProcessCompleted.class)); + createEvent(TransferProcessStarted.class), + createEvent(TransferProcessStarted.class)); var assetId = UUID.randomUUID().toString(); var url = server.url("/mock/api"); @@ -93,7 +95,7 @@ void negotiateEdr_shouldRenewTheEdr() throws IOException { PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); var callbacks = Json.createArrayBuilder() - .add(createCallback(url.toString(), true, Set.of("transfer.process.completed"))) + .add(createCallback(url.toString(), true, Set.of("transfer.process.started"))) .build(); expectedEvents.forEach(event -> server.enqueue(new MockResponse())); @@ -106,20 +108,48 @@ void negotiateEdr_shouldRenewTheEdr() throws IOException { assertThat(expectedEvents).usingRecursiveFieldByFieldElementComparator().containsAll(events); - JsonArrayBuilder edrCaches = Json.createArrayBuilder(); + JsonArrayBuilder edrCachesBuilder = Json.createArrayBuilder(); await().atMost(ASYNC_TIMEOUT) .untilAsserted(() -> { var localEdrCaches = SOKRATES.getEdrEntriesByAssetId(assetId); assertThat(localEdrCaches).hasSizeGreaterThan(1); - localEdrCaches.forEach(edrCaches::add); + localEdrCaches.forEach(edrCachesBuilder::add); }); + var edrCaches = edrCachesBuilder.build(); - assertThat(edrCaches.build()) + assertThat(edrCaches) .extracting(json -> json.asJsonObject().getJsonString("tx:edrState").getString()) .areAtMost(1, anyOf(stateCondition(NEGOTIATED.name(), "Negotiated"), stateCondition(REFRESHING.name(), "Refreshing"))) .areAtLeast(1, stateCondition(EXPIRED.name(), "Expired")); + + var transferProcessId = edrCaches.stream() + .filter(json -> json.asJsonObject().getJsonString("tx:edrState").getString().equals(EXPIRED.name())) + .map(json -> json.asJsonObject().getJsonString("edc:transferProcessId").getString()) + .findFirst() + .orElseThrow(); + + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var tpState = SOKRATES.getTransferProcessState(transferProcessId); + assertThat(tpState).isNotNull().isEqualTo(TransferProcessStates.TERMINATED.toString()); + }); + + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var tpState = PLATO.getAllTransferProcess() + .stream() + .filter(json -> json.asJsonObject().getJsonString("edc:correlationId").getString().equals(transferProcessId)) + .map(json -> json.asJsonObject().getJsonString("edc:state").getString()) + .findFirst(); + + assertThat(tpState) + .isPresent() + .hasValue(TransferProcessStates.TERMINATED.toString()); + }); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/AbstractDataPlaneProxyTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/AbstractDataPlaneProxyTest.java index 37f3df3fe..07d060881 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/AbstractDataPlaneProxyTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/proxy/AbstractDataPlaneProxyTest.java @@ -21,7 +21,7 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; -import org.eclipse.edc.connector.transfer.spi.event.TransferProcessCompleted; +import org.eclipse.edc.connector.transfer.spi.event.TransferProcessStarted; import org.eclipse.edc.policy.model.Operator; import org.eclipse.edc.spi.event.EventEnvelope; import org.eclipse.tractusx.edc.lifecycle.Participant; @@ -32,11 +32,15 @@ import org.junit.jupiter.api.Test; import java.io.IOException; +import java.time.Duration; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; +import static java.time.Duration.ofSeconds; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.awaitility.pollinterval.FibonacciPollInterval.fibonacci; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; import static org.eclipse.tractusx.edc.helpers.EdrNegotiationHelperFunctions.createCallback; import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.businessPartnerGroupPolicy; @@ -58,6 +62,8 @@ public abstract class AbstractDataPlaneProxyTest { private static final String CUSTOM_QUERY_PARAMS = "foo=bar"; + private static final Duration ASYNC_TIMEOUT = ofSeconds(45); + private static final String CUSTOM_FULL_PATH = CUSTOM_BASE_PATH + CUSTOM_SUB_PATH + "?" + CUSTOM_QUERY_PARAMS; private final ObjectMapper mapper = new ObjectMapper(); private MockWebServer server; @@ -86,7 +92,7 @@ void httpPullDataTransfer_withEdrAndProxy() { PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); var callbacks = Json.createArrayBuilder() - .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.completed"))) + .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.started"))) .build(); // response to callback @@ -159,7 +165,7 @@ void httpPullDataTransfer_shouldFailForAsset_withTwoEdrAndProxy() throws IOExcep PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); var callbacks = Json.createArrayBuilder() - .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.completed"))) + .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.started"))) .build(); // response to callback @@ -174,9 +180,14 @@ void httpPullDataTransfer_shouldFailForAsset_withTwoEdrAndProxy() throws IOExcep var body = "{\"response\": \"ok\"}"; - server.enqueue(new MockResponse().setBody(body)); - SOKRATES.pullProxyDataResponseByAssetId(PLATO, assetId).then() - .assertThat().statusCode(428); + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + 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()); @@ -210,7 +221,7 @@ void httpPullDataTransfer_withEdrAndProviderDataPlaneProxy() throws IOException PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); var callbacks = Json.createArrayBuilder() - .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.completed"))) + .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.started"))) .build(); // response to callback @@ -272,7 +283,7 @@ public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) throws In PLATO.createContractDefinition(assetId, "def-1", "policy-1", "policy-2"); var callbacks = Json.createArrayBuilder() - .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.completed"))) + .add(createCallback(eventsUrl.toString(), true, Set.of("transfer.process.started"))) .build(); SOKRATES.negotiateEdr(PLATO, assetId, callbacks); @@ -295,7 +306,7 @@ void teardown() throws IOException { server.shutdown(); } - private EventEnvelope waitForTransferCompletion() { + private EventEnvelope waitForTransferCompletion() { try { var request = server.takeRequest(60, TimeUnit.SECONDS); if (request != null) { diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpConsumerPullWithProxyTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpConsumerPullWithProxyTest.java index 4f5fcda03..56cf54b06 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpConsumerPullWithProxyTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/AbstractHttpConsumerPullWithProxyTest.java @@ -110,7 +110,7 @@ void transferData_privateBackend() throws IOException, InterruptedException { .atMost(ASYNC_TIMEOUT) .untilAsserted(() -> { var tpState = SOKRATES.getTransferProcessState(transferProcessId.get()); - assertThat(tpState).isNotNull().isEqualTo(TransferProcessStates.COMPLETED.toString()); + assertThat(tpState).isNotNull().isEqualTo(TransferProcessStates.STARTED.toString()); }); // wait until EDC is available on the consumer side diff --git a/edc-tests/edc-dataplane-proxy-e2e/build.gradle.kts b/edc-tests/edc-dataplane-proxy-e2e/build.gradle.kts index eeb8473f3..f5b46c633 100644 --- a/edc-tests/edc-dataplane-proxy-e2e/build.gradle.kts +++ b/edc-tests/edc-dataplane-proxy-e2e/build.gradle.kts @@ -24,7 +24,7 @@ dependencies { // test runtime config testImplementation(libs.edc.config.filesystem) testImplementation(libs.edc.dpf.http) - testImplementation(libs.edc.auth.tokenbased) + testImplementation(project(":edc-extensions:auth-tokenbased")) testImplementation(project(":spi:edr-spi")) testImplementation(project(":core:edr-cache-core")) testImplementation(project(":edc-extensions:dataplane-proxy:edc-dataplane-proxy-consumer-api")) diff --git a/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/TestServiceExtension.java b/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/TestServiceExtension.java deleted file mode 100644 index b498f185b..000000000 --- a/edc-tests/runtime/extensions/src/main/java/org/eclipse/tractusx/edc/lifecycle/TestServiceExtension.java +++ /dev/null @@ -1,36 +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.eclipse.edc.connector.transfer.spi.status.StatusCheckerRegistry; -import org.eclipse.edc.connector.transfer.spi.types.TransferProcess; -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; - -@Extension(value = "Extension used to inject dummy services into E2E runtimes") -public class TestServiceExtension implements ServiceExtension { - - @Inject - private StatusCheckerRegistry registry; - - @Override - public void initialize(ServiceExtensionContext context) { - // takes care that ongoing HTTP transfers are actually completed, otherwise they would - // always stay in the "STARTED" state - registry.register("HttpProxy", (tp, r) -> tp.getType() == TransferProcess.Type.CONSUMER); - } -} diff --git a/edc-tests/runtime/extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-tests/runtime/extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index c3abfd832..75f82b423 100644 --- a/edc-tests/runtime/extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-tests/runtime/extensions/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -14,5 +14,3 @@ org.eclipse.tractusx.edc.lifecycle.ConsumerServicesExtension org.eclipse.tractusx.edc.lifecycle.VaultSeedExtension -org.eclipse.tractusx.edc.lifecycle.TestServiceExtension - diff --git a/gradle.properties b/gradle.properties index 851d3e762..3110da84b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=org.eclipse.tractusx.edc -version=0.5.3 +version=0.5.4 # configure the build: annotationProcessorVersion=0.2.1 edcGradlePluginsVersion=0.2.1 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 62eedbb9f..516796d8e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -70,7 +70,6 @@ edc-api-transferprocess = { module = "org.eclipse.edc:transfer-process-api", ver edc-dsp = { module = "org.eclipse.edc:dsp", version.ref = "edc" } edc-iam-mock = { module = "org.eclipse.edc:iam-mock", version.ref = "edc" } edc-policy-engine = { module = "org.eclipse.edc:policy-engine", version.ref = "edc" } -edc-auth-tokenbased = { module = "org.eclipse.edc:auth-tokenbased", version.ref = "edc" } edc-auth-oauth2-core = { module = "org.eclipse.edc:oauth2-core", version.ref = "edc" } edc-auth-oauth2-daps = { module = "org.eclipse.edc:oauth2-daps", version.ref = "edc" } edc-auth-oauth2-client = { module = "org.eclipse.edc:oauth2-client", version.ref = "edc" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 2be4c8d8e..c83594d7c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -33,6 +33,7 @@ include(":core:json-ld-core") include(":edc-extensions:bpn-validation") +include(":edc-extensions:auth-tokenbased") include(":edc-extensions:bpn-validation:bpn-validation-api") include(":edc-extensions:bpn-validation:bpn-validation-spi") include(":edc-extensions:bpn-validation:bpn-validation-core")