From 38ad43cb51f300cf448829a96275f799393e4aae Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Mon, 13 Apr 2026 14:56:59 +0200 Subject: [PATCH] feat: replace edc dataplane with siglet --- extensions/data-plane-certs/build.gradle.kts | 18 +- .../dataplane/cert/CertExchangeExtension.java | 81 +++++-- .../api/CertExchangePublicController.java | 75 +++--- .../data-plane-public-api-v2/build.gradle.kts | 47 ---- .../api/DataPlanePublicApiV2Extension.java | 113 --------- .../ContainerRequestContextApi.java | 68 ------ .../ContainerRequestContextApiImpl.java | 106 -------- .../controller/DataFlowRequestSupplier.java | 73 ------ .../api/controller/DataPlanePublicApiV2.java | 90 ------- .../DataPlanePublicApiV2Controller.java | 184 -------------- ...rg.eclipse.edc.spi.system.ServiceExtension | 1 - .../main/resources/public-api-version.json | 8 - .../DataFlowStartMessageSupplierTest.java | 94 -------- .../DataPlanePublicApiV2ControllerTest.java | 226 ------------------ gradle.properties | 3 +- gradle/libs.versions.toml | 24 +- k8s/apps/dataplane-config.yaml | 11 +- k8s/apps/redline-config.yaml | 1 + launchers/dataplane/build.gradle.kts | 6 - .../dataplane/keyseed/KeySeedExtension.java | 56 ----- ...rg.eclipse.edc.spi.system.ServiceExtension | 14 -- .../Http Certs/Consumer/Get certificate.bru | 2 +- .../Http Certs/Consumer/List certificates.bru | 2 +- .../Http Certs/Consumer/Setup Transfer.bru | 2 +- .../Data Transfer/Http Certs/folder.bru | 2 +- .../Data Transfer/Http Todo/Get Catalog.bru | 55 ----- .../Data Transfer/Http Todo/Get Data.bru | 23 -- .../Data Transfer/Http Todo/folder.bru | 8 - .../Create Cert Asset.bru | 6 +- .../Create Contract Definition.bru | 2 +- .../Create Policy.bru | 2 +- .../Create Todo Asset.bru | 39 --- .../Prepare Dataplane.bru | 9 +- settings.gradle.kts | 1 - .../jad/tests/DataTransferEndToEndTest.java | 46 +--- .../src/test/resources/asset-cert.json | 4 - .../src/test/resources/asset-restricted.json | 7 - tests/end2end/src/test/resources/asset.json | 7 - 38 files changed, 138 insertions(+), 1378 deletions(-) delete mode 100644 extensions/data-plane-public-api-v2/build.gradle.kts delete mode 100644 extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/DataPlanePublicApiV2Extension.java delete mode 100644 extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/ContainerRequestContextApi.java delete mode 100644 extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/ContainerRequestContextApiImpl.java delete mode 100644 extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataFlowRequestSupplier.java delete mode 100644 extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2.java delete mode 100644 extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2Controller.java delete mode 100644 extensions/data-plane-public-api-v2/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension delete mode 100644 extensions/data-plane-public-api-v2/src/main/resources/public-api-version.json delete mode 100644 extensions/data-plane-public-api-v2/src/test/java/org/eclipse/edc/connector/dataplane/api/controller/DataFlowStartMessageSupplierTest.java delete mode 100644 extensions/data-plane-public-api-v2/src/test/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2ControllerTest.java delete mode 100644 launchers/dataplane/src/main/java/org/eclipse/edc/virtualized/dataplane/keyseed/KeySeedExtension.java delete mode 100644 launchers/dataplane/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension delete mode 100644 requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/Get Catalog.bru delete mode 100644 requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/Get Data.bru delete mode 100644 requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/folder.bru delete mode 100644 requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Todo Asset.bru diff --git a/extensions/data-plane-certs/build.gradle.kts b/extensions/data-plane-certs/build.gradle.kts index 876018a..88db7e2 100644 --- a/extensions/data-plane-certs/build.gradle.kts +++ b/extensions/data-plane-certs/build.gradle.kts @@ -21,14 +21,26 @@ dependencies { api(libs.edc.spi.http) api(libs.edc.spi.transaction) api(libs.edc.spi.web) - api(libs.edc.spi.dataplane) + implementation(libs.edc.core.boot) + implementation(libs.edc.core.runtime) + implementation(libs.edc.core.token) + implementation(libs.edc.core.connector) + implementation(libs.edc.core.api) + implementation(libs.edc.core.participantcontext.config) implementation(libs.jersey.multipart) implementation(libs.edc.lib.util) implementation(libs.edc.lib.sql) + implementation(libs.edc.lib.token) + implementation(libs.edc.lib.oauth2.authn) implementation(libs.edc.core.sql.bootstrapper) - implementation(libs.edc.lib.util.dataplane) - implementation(libs.edc.dataplane.iam) + implementation(libs.edc.core.sql) + implementation(libs.edc.core.http) + implementation(libs.edc.transaction.local) + implementation(libs.edc.transaction.pool) + implementation(libs.edc.api.control.configuration) + implementation(libs.edc.api.observability) implementation(libs.jakarta.rsApi) + implementation(libs.postgres) testImplementation(libs.edc.lib.http) testImplementation(libs.edc.junit) diff --git a/extensions/data-plane-certs/src/main/java/org/eclipse/edc/virtualized/dataplane/cert/CertExchangeExtension.java b/extensions/data-plane-certs/src/main/java/org/eclipse/edc/virtualized/dataplane/cert/CertExchangeExtension.java index c9ab5f5..51d165d 100644 --- a/extensions/data-plane-certs/src/main/java/org/eclipse/edc/virtualized/dataplane/cert/CertExchangeExtension.java +++ b/extensions/data-plane-certs/src/main/java/org/eclipse/edc/virtualized/dataplane/cert/CertExchangeExtension.java @@ -14,19 +14,23 @@ package org.eclipse.edc.virtualized.dataplane.cert; -import org.eclipse.edc.connector.dataplane.iam.service.DataPlaneAuthorizationServiceImpl; -import org.eclipse.edc.connector.dataplane.spi.Endpoint; -import org.eclipse.edc.connector.dataplane.spi.edr.EndpointDataReferenceServiceRegistry; -import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAuthorizationService; -import org.eclipse.edc.connector.dataplane.spi.iam.PublicEndpointGeneratorService; +import org.eclipse.edc.api.authentication.JwksResolver; +import org.eclipse.edc.api.authentication.filter.JwtValidatorFilter; +import org.eclipse.edc.keys.spi.KeyParserRegistry; import org.eclipse.edc.runtime.metamodel.annotation.Configuration; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Setting; import org.eclipse.edc.runtime.metamodel.annotation.Settings; +import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.system.Hostname; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.token.rules.ExpirationIssuedAtValidationRule; +import org.eclipse.edc.token.rules.IssuerEqualsValidationRule; +import org.eclipse.edc.token.rules.NotBeforeValidationRule; +import org.eclipse.edc.token.spi.TokenValidationRule; +import org.eclipse.edc.token.spi.TokenValidationService; import org.eclipse.edc.transaction.spi.TransactionContext; import org.eclipse.edc.virtualized.dataplane.cert.api.CertExchangePublicController; import org.eclipse.edc.virtualized.dataplane.cert.api.CertInternalExchangeController; @@ -35,6 +39,11 @@ import org.eclipse.edc.web.spi.configuration.PortMapping; import org.eclipse.edc.web.spi.configuration.PortMappingRegistry; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.Clock; +import java.util.List; + import static org.eclipse.edc.virtualized.dataplane.cert.CertExchangeExtension.NAME; @Extension(NAME) @@ -43,12 +52,7 @@ public class CertExchangeExtension implements ServiceExtension { public static final String API_CONTEXT = "certs"; private static final int DEFAULT_CERTS_PORT = 8186; private static final String DEFAULT_CERTS_PATH = "/api/data"; - - - @Setting(description = "Base url of the public public API endpoint without the trailing slash. This should point to the public certs endpoint configured.", - required = false, - key = "edc.dataplane.api.certs.baseurl", warnOnMissingConfig = true) - private String publicBaseUrl; + private static final long FIVE_MINUTES = 1000 * 60 * 5; @Configuration private CertApiConfiguration apiConfiguration; @@ -60,41 +64,54 @@ public class CertExchangeExtension implements ServiceExtension { private PortMappingRegistry portMappingRegistry; @Inject - private DataPlaneAuthorizationService authorizationService; + private WebService webService; + @Inject - private PublicEndpointGeneratorService generatorService; + private CertStore certStore; @Inject - private EndpointDataReferenceServiceRegistry endpointDataReferenceServiceRegistry; + private TransactionContext transactionContext; @Inject - private WebService webService; + private TokenValidationService tokenValidationService; + + @Configuration + private SigletConfig sigletConfig; @Inject - private CertStore certStore; + private KeyParserRegistry keyParserRegistry; @Inject - private TransactionContext transactionContext; + private Clock clock; @Override public void initialize(ServiceExtensionContext context) { var portMapping = new PortMapping(API_CONTEXT, apiConfiguration.port(), apiConfiguration.path()); portMappingRegistry.register(portMapping); - if (publicBaseUrl == null) { - publicBaseUrl = "http://%s:%d%s".formatted(hostname.get(), portMapping.port(), portMapping.path()); - context.getMonitor().warning("The public API endpoint was not explicitly configured, the default '%s' will be used.".formatted(publicBaseUrl)); + URL url; + try { + url = new URL(sigletConfig.jwksUrl()); + } catch (MalformedURLException e) { + throw new EdcException(e); } - var endpoint = Endpoint.url(publicBaseUrl); - generatorService.addGeneratorFunction("HttpCertData", dataAddress -> endpoint); - webService.registerResource(API_CONTEXT, new CertExchangePublicController(authorizationService, certStore, transactionContext)); + + webService.registerResource(API_CONTEXT, new CertExchangePublicController(certStore, transactionContext)); + webService.registerResource(API_CONTEXT, new JwtValidatorFilter(tokenValidationService, new JwksResolver(url, keyParserRegistry, sigletConfig.cacheValidityInMillis), getRules())); + webService.registerResource("control", new CertInternalExchangeController(certStore, transactionContext)); - if (authorizationService instanceof DataPlaneAuthorizationServiceImpl dpAuthService) { - endpointDataReferenceServiceRegistry.register("HttpCertData", dpAuthService); - } } + private List getRules() { + return List.of( + new IssuerEqualsValidationRule(sigletConfig.expectedIssuer), + new NotBeforeValidationRule(clock, 0, true), + new ExpirationIssuedAtValidationRule(clock, 0, false) + ); + } + + @Settings record CertApiConfiguration( @Setting(key = "web.http." + API_CONTEXT + ".port", description = "Port for " + API_CONTEXT + " api context", defaultValue = DEFAULT_CERTS_PORT + "") @@ -104,4 +121,16 @@ record CertApiConfiguration( ) { } + + @Settings + record SigletConfig( + @Setting(key = "edc.iam.siglet.issuer", description = "Issuer of the Siglet server", required = false) + String expectedIssuer, + @Setting(key = "edc.iam.siglet.jwks.url", description = "Absolute URL where the JWKS of the Siglet server is hosted") + String jwksUrl, + @Setting(key = "edc.iam.siglet.jwks.cache.validity", description = "Time (in ms) that cached JWKS are cached", defaultValue = "" + FIVE_MINUTES) + long cacheValidityInMillis + ) { + + } } diff --git a/extensions/data-plane-certs/src/main/java/org/eclipse/edc/virtualized/dataplane/cert/api/CertExchangePublicController.java b/extensions/data-plane-certs/src/main/java/org/eclipse/edc/virtualized/dataplane/cert/api/CertExchangePublicController.java index 33692ab..f36d9c0 100644 --- a/extensions/data-plane-certs/src/main/java/org/eclipse/edc/virtualized/dataplane/cert/api/CertExchangePublicController.java +++ b/extensions/data-plane-certs/src/main/java/org/eclipse/edc/virtualized/dataplane/cert/api/CertExchangePublicController.java @@ -15,16 +15,16 @@ package org.eclipse.edc.virtualized.dataplane.cert.api; import com.fasterxml.jackson.core.type.TypeReference; -import com.nimbusds.jwt.SignedJWT; import jakarta.ws.rs.GET; -import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.StreamingOutput; -import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAuthorizationService; +import org.eclipse.edc.spi.iam.ClaimToken; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.transaction.spi.TransactionContext; import org.eclipse.edc.virtualized.dataplane.cert.model.ActivityItem; @@ -35,30 +35,27 @@ import java.io.InputStream; import java.time.Instant; import java.util.List; -import java.util.Map; -import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION; import static jakarta.ws.rs.core.Response.Status.FORBIDDEN; import static jakarta.ws.rs.core.Response.Status.UNAUTHORIZED; +import static org.eclipse.edc.api.authentication.filter.Constants.REQUEST_PROPERTY_CLAIMS; @Path("certs") public class CertExchangePublicController { - private final DataPlaneAuthorizationService authorizationService; private final CertStore certStore; private final TransactionContext transactionContext; - public CertExchangePublicController(DataPlaneAuthorizationService authorizationService, CertStore certStore, TransactionContext transactionContext) { - this.authorizationService = authorizationService; + public CertExchangePublicController(CertStore certStore, TransactionContext transactionContext) { this.certStore = certStore; this.transactionContext = transactionContext; } @POST @Path("/request") - public List queryCertificates(@HeaderParam(AUTHORIZATION) String token, QuerySpec querySpec) { + public List queryCertificates(@Context ContainerRequestContext requestContext, QuerySpec querySpec) { return transactionContext.execute(() -> { - checkAuth(token); + checkAuth(requestContext); return certStore.queryMetadata(querySpec) // strip out the history for public API .stream().map(ct -> new CertMetadata(ct.id(), ct.contentType(), ct.properties())).toList(); @@ -67,54 +64,46 @@ public List queryCertificates(@HeaderParam(AUTHORIZATION) String t @GET @Path("/{id}") - public Response certificateDownload(@HeaderParam(AUTHORIZATION) String token, @PathParam("id") String id) { + public Response certificateDownload(@Context ContainerRequestContext requestContext, @PathParam("id") String id) { return transactionContext.execute(() -> { - var subject = checkAuth(token); + var claims = checkAuth(requestContext); var metadata = certStore.getMetadata(id); if (metadata == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - metadata.history().add(new ActivityItem(subject, Instant.now().getEpochSecond(), "DOWNLOAD")); - certStore.updateMetadata(id, metadata); - StreamingOutput stream = output -> { - try (InputStream is = certStore.retrieve(id)) { - is.transferTo(output); - } - }; - + var subject = claims.getClaim("sub"); + if (subject instanceof String s) { + metadata.history().add(new ActivityItem(s, Instant.now().getEpochSecond(), "DOWNLOAD")); + certStore.updateMetadata(id, metadata); + StreamingOutput stream = output -> { + try (InputStream is = certStore.retrieve(id)) { + is.transferTo(output); + } + }; + + + return Response.ok(stream) + .header("Content-Type", metadata.contentType()) + .build(); + } else { + throw new WebApplicationException(FORBIDDEN); + } - return Response.ok(stream) - .header("Content-Type", metadata.contentType()) - .build(); }); } - private String checkAuth(String token) { - if (token == null) { + private ClaimToken checkAuth(ContainerRequestContext requestContext) { + if (requestContext == null) { throw new WebApplicationException(UNAUTHORIZED); } - - // assuming "token" is a JWT, lets parse it and extract the `sub` claim - var subject = parseJwt(token); - - - var sourceDataAddress = authorizationService.authorize(token, Map.of()); - if (sourceDataAddress.failed()) { + var claims = requestContext.getProperty(REQUEST_PROPERTY_CLAIMS); + if (claims instanceof ClaimToken claimToken) { + return claimToken; + } else { throw new WebApplicationException(FORBIDDEN); - } - - return subject; } - private String parseJwt(String token) { - try { - var signedJwt = SignedJWT.parse(token); - return String.join(",", signedJwt.getJWTClaimsSet().getAudience()); - } catch (Exception e) { - return null; - } - } @NotNull protected TypeReference getTypeRef() { diff --git a/extensions/data-plane-public-api-v2/build.gradle.kts b/extensions/data-plane-public-api-v2/build.gradle.kts deleted file mode 100644 index b553ff1..0000000 --- a/extensions/data-plane-public-api-v2/build.gradle.kts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2025 Metaform Systems, Inc. - * - * This program and the accompanying materials are 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: - * Metaform Systems, Inc. - initial API and implementation - * - */ - -plugins { - `java-library` - id(libs.plugins.swagger.get().pluginId) -} - -dependencies { - api(libs.edc.spi.http) - api(libs.edc.spi.web) - api(libs.edc.spi.dataplane) - implementation(libs.edc.lib.util) - implementation(libs.edc.lib.util.dataplane) -// api(project(":spi:data-plane:data-plane-spi")) -// implementation(project(":core:common:lib:util-lib")) - -// implementation(project(":core:data-plane:data-plane-util")) - implementation(libs.jakarta.rsApi) - - testImplementation(libs.edc.lib.http) -// testImplementation(project(":extensions:common:http")) - testImplementation(libs.edc.junit) -// testImplementation(project(":core:common:junit")) - testImplementation(libs.restAssured) -// testImplementation(testFixtures(project(":extensions:common:http:jersey-core"))) - testImplementation(testFixtures(libs.edc.core.jersey)) - -} -edcBuild { - swagger { - apiGroup.set("public-api") - } -} - - diff --git a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/DataPlanePublicApiV2Extension.java b/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/DataPlanePublicApiV2Extension.java deleted file mode 100644 index 612e025..0000000 --- a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/DataPlanePublicApiV2Extension.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2025 Metaform Systems, Inc. - * - * This program and the accompanying materials are 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: - * Metaform Systems, Inc. - initial API and implementation - * - */ - -package org.eclipse.edc.connector.dataplane.api; - -import org.eclipse.edc.connector.dataplane.api.controller.DataPlanePublicApiV2Controller; -import org.eclipse.edc.connector.dataplane.spi.Endpoint; -import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAuthorizationService; -import org.eclipse.edc.connector.dataplane.spi.iam.PublicEndpointGeneratorService; -import org.eclipse.edc.connector.dataplane.spi.pipeline.PipelineService; -import org.eclipse.edc.runtime.metamodel.annotation.Configuration; -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.runtime.metamodel.annotation.Settings; -import org.eclipse.edc.spi.system.ExecutorInstrumentation; -import org.eclipse.edc.spi.system.Hostname; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.web.spi.WebService; -import org.eclipse.edc.web.spi.configuration.PortMapping; -import org.eclipse.edc.web.spi.configuration.PortMappingRegistry; - -import java.util.concurrent.Executors; - -/** - * This extension provides generic endpoints which are open to public participants of the Dataspace to execute - * requests on the actual data source. - */ -@Extension(value = DataPlanePublicApiV2Extension.NAME) -public class DataPlanePublicApiV2Extension implements ServiceExtension { - public static final String NAME = "Data Plane Public API"; - - public static final String API_CONTEXT = "public"; - private static final int DEFAULT_PUBLIC_PORT = 8185; - private static final String DEFAULT_PUBLIC_PATH = "/api/public"; - private static final int DEFAULT_THREAD_POOL = 10; - @Setting(description = "Base url of the public API endpoint without the trailing slash. This should point to the public endpoint configured.", - required = false, - key = "edc.dataplane.api.public.baseurl", warnOnMissingConfig = true) - private String publicBaseUrl; - @Setting(description = "Optional base url of the response channel endpoint without the trailing slash. A common practice is to use /responseChannel", key = "edc.dataplane.api.public.response.baseurl", required = false) - private String publicApiResponseUrl; - @Configuration - private PublicApiConfiguration apiConfiguration; - @Inject - private PortMappingRegistry portMappingRegistry; - @Inject - private PipelineService pipelineService; - @Inject - private WebService webService; - @Inject - private ExecutorInstrumentation executorInstrumentation; - @Inject - private DataPlaneAuthorizationService authorizationService; - @Inject - private PublicEndpointGeneratorService generatorService; - @Inject - private Hostname hostname; - - @Override - public String name() { - return NAME; - } - - @Override - public void initialize(ServiceExtensionContext context) { - context.getMonitor().warning("The `data-plane-public-api-v2` has been deprecated, please provide an" + - "alternative implementation for Http Proxy if needed"); - - var portMapping = new PortMapping(API_CONTEXT, apiConfiguration.port(), apiConfiguration.path()); - portMappingRegistry.register(portMapping); - var executorService = executorInstrumentation.instrument( - Executors.newFixedThreadPool(DEFAULT_THREAD_POOL), - "Data plane proxy transfers" - ); - - if (publicBaseUrl == null) { - publicBaseUrl = "http://%s:%d%s".formatted(hostname.get(), portMapping.port(), portMapping.path()); - context.getMonitor().warning("The public API endpoint was not explicitly configured, the default '%s' will be used.".formatted(publicBaseUrl)); - } - var endpoint = Endpoint.url(publicBaseUrl); - generatorService.addGeneratorFunction("HttpData", dataAddress -> endpoint); - - if (publicApiResponseUrl != null) { - generatorService.addResponseGeneratorFunction("HttpData", () -> Endpoint.url(publicApiResponseUrl)); - } - - var publicApiController = new DataPlanePublicApiV2Controller(pipelineService, executorService, authorizationService); - webService.registerResource(API_CONTEXT, publicApiController); - } - - @Settings - record PublicApiConfiguration( - @Setting(key = "web.http." + API_CONTEXT + ".port", description = "Port for " + API_CONTEXT + " api context", defaultValue = DEFAULT_PUBLIC_PORT + "") - int port, - @Setting(key = "web.http." + API_CONTEXT + ".path", description = "Path for " + API_CONTEXT + " api context", defaultValue = DEFAULT_PUBLIC_PATH) - String path - ) { - - } -} diff --git a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/ContainerRequestContextApi.java b/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/ContainerRequestContextApi.java deleted file mode 100644 index 5bc6050..0000000 --- a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/ContainerRequestContextApi.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2025 Metaform Systems, Inc. - * - * This program and the accompanying materials are 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: - * Metaform Systems, Inc. - initial API and implementation - * - */ - -package org.eclipse.edc.connector.dataplane.api.controller; - -import jakarta.ws.rs.container.ContainerRequestContext; - -import java.util.Map; - -/** - * Wrapper around {@link ContainerRequestContext} enabling mocking. - */ -public interface ContainerRequestContextApi { - - /** - * Get the request headers. Note that if more than one value is associated to a specific header, - * only the first one is retained. - * - * @return Headers map. - */ - Map headers(); - - /** - * Format query of the request as string, e.g. "hello=world\&foo=bar". - * - * @return Query param string. - */ - String queryParams(); - - /** - * Format the request body into a string. - * - * @return Request body. - */ - String body(); - - /** - * Get the media type from incoming request. - * - * @return Media type. - */ - String mediaType(); - - /** - * Return request path, e.g. "hello/world/foo/bar". - * - * @return Path string. - */ - String path(); - - /** - * Get http method from the incoming request, e.g. "GET", "POST"... - * - * @return Http method. - */ - String method(); -} diff --git a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/ContainerRequestContextApiImpl.java b/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/ContainerRequestContextApiImpl.java deleted file mode 100644 index 6bcb5ba..0000000 --- a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/ContainerRequestContextApiImpl.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2025 Metaform Systems, Inc. - * - * This program and the accompanying materials are 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: - * Metaform Systems, Inc. - initial API and implementation - * - */ - -package org.eclipse.edc.connector.dataplane.api.controller; - -import jakarta.ws.rs.container.ContainerRequestContext; -import jakarta.ws.rs.core.MediaType; -import org.eclipse.edc.spi.EdcException; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -/** - * This class provides a set of API wrapping a {@link ContainerRequestContext}. - */ -public class ContainerRequestContextApiImpl implements ContainerRequestContextApi { - - private static final String QUERY_PARAM_SEPARATOR = "&"; - - private final ContainerRequestContext context; - - public ContainerRequestContextApiImpl(ContainerRequestContext context) { - this.context = context; - } - - @Override - public Map headers() { - return context.getHeaders().entrySet() - .stream() - .filter(entry -> !entry.getValue().isEmpty()) - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().get(0))); - } - - @Override - public String queryParams() { - return context.getUriInfo().getQueryParameters().entrySet() - .stream() - .flatMap(entry -> entry.getValue().stream().map(val -> new QueryParam(entry.getKey(), val))) - .map(QueryParam::toString) - .collect(Collectors.joining(QUERY_PARAM_SEPARATOR)); - } - - @Override - public String body() { - try (BufferedReader br = new BufferedReader(new InputStreamReader(context.getEntityStream()))) { - return br.lines().collect(Collectors.joining("\n")); - } catch (IOException e) { - throw new EdcException("Failed to read request body: " + e.getMessage()); - } - } - - @Override - public String mediaType() { - return Optional.ofNullable(context.getMediaType()) - .map(MediaType::toString) - .orElse(null); - } - - @Override - public String path() { - var pathInfo = context.getUriInfo().getPath(); - return pathInfo.startsWith("/") ? pathInfo.substring(1) : pathInfo; - } - - @Override - public String method() { - return context.getMethod(); - } - - private static final class QueryParam { - - private final String key; - private final String values; - private final boolean valid; - - private QueryParam(String key, String values) { - this.key = key; - this.values = values; - this.valid = key != null && values != null && !values.isEmpty(); - } - - public boolean isValid() { - return valid; - } - - @Override - public String toString() { - return valid ? key + "=" + values : ""; - } - } -} diff --git a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataFlowRequestSupplier.java b/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataFlowRequestSupplier.java deleted file mode 100644 index c3c1aa7..0000000 --- a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataFlowRequestSupplier.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2025 Metaform Systems, Inc. - * - * This program and the accompanying materials are 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: - * Metaform Systems, Inc. - initial API and implementation - * - */ - -package org.eclipse.edc.connector.dataplane.api.controller; - -import org.eclipse.edc.connector.dataplane.util.sink.AsyncStreamingDataSink; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; -import org.eclipse.edc.spi.types.domain.transfer.FlowType; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.function.BiFunction; - -import static org.eclipse.edc.connector.dataplane.spi.schema.DataFlowRequestSchema.BODY; -import static org.eclipse.edc.connector.dataplane.spi.schema.DataFlowRequestSchema.MEDIA_TYPE; -import static org.eclipse.edc.connector.dataplane.spi.schema.DataFlowRequestSchema.METHOD; -import static org.eclipse.edc.connector.dataplane.spi.schema.DataFlowRequestSchema.PATH; -import static org.eclipse.edc.connector.dataplane.spi.schema.DataFlowRequestSchema.QUERY_PARAMS; - -public class DataFlowRequestSupplier implements BiFunction { - - /** - * Put all properties of the incoming request (method, request body, query params...) into a map. - */ - private static Map createProps(ContainerRequestContextApi contextApi) { - var props = new HashMap(); - props.put(METHOD, contextApi.method()); - props.put(QUERY_PARAMS, contextApi.queryParams()); - props.put(PATH, contextApi.path()); - Optional.ofNullable(contextApi.mediaType()) - .ifPresent(mediaType -> { - props.put(MEDIA_TYPE, mediaType); - props.put(BODY, contextApi.body()); - }); - return props; - } - - /** - * Create a {@link DataFlowStartMessage} based on incoming request and claims decoded from the access token. - * - * @param contextApi Api for accessing request properties. - * @param dataAddress Source data address. - * @return DataFlowRequest - */ - @Override - public DataFlowStartMessage apply(ContainerRequestContextApi contextApi, DataAddress dataAddress) { - var props = createProps(contextApi); - return DataFlowStartMessage.Builder.newInstance() - .processId(UUID.randomUUID().toString()) - .sourceDataAddress(dataAddress) - .flowType(FlowType.PULL) // if a request hits the public DP API, we can assume a PULL transfer - .destinationDataAddress(DataAddress.Builder.newInstance() - .type(AsyncStreamingDataSink.TYPE) - .build()) - .id(UUID.randomUUID().toString()) - .properties(props) - .build(); - } -} diff --git a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2.java b/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2.java deleted file mode 100644 index aea4942..0000000 --- a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2025 Metaform Systems, Inc. - * - * This program and the accompanying materials are 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: - * Metaform Systems, Inc. - initial API and implementation - * - */ - -package org.eclipse.edc.connector.dataplane.api.controller; - -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.ws.rs.container.AsyncResponse; -import jakarta.ws.rs.container.ContainerRequestContext; - -@OpenAPIDefinition -@Tag(name = "Data Plane public API", - description = "The public API of the Data Plane is a data proxy enabling a data consumer to actively query" + - "data from the provider data source (e.g. backend Rest API, internal database...) through its Data Plane" + - "instance. Thus the Data Plane is the only entry/output door for the data, which avoids the provider to expose" + - "directly its data externally." + - "The Data Plane public API being a proxy, it supports all verbs (i.e. GET, POST, PUT, PATCH, DELETE), which" + - "can then conveyed until the data source is required. This is especially useful when the actual data source" + - "is a Rest API itself." + - "In the same manner, any set of arbitrary query parameters, path parameters and request body are supported " + - "(in the limits fixed by the HTTP server) and can also conveyed to the actual data source.") -public interface DataPlanePublicApiV2 { - - @Operation(description = "Send `GET` data query to the Data Plane.", - responses = { - @ApiResponse(responseCode = "400", description = "Missing access token"), - @ApiResponse(responseCode = "403", description = "Access token is expired or invalid"), - @ApiResponse(responseCode = "500", description = "Failed to transfer data") - } - ) - void get(ContainerRequestContext context, AsyncResponse response); - - @Operation(description = "Send `HEAD` data query to the Data Plane.", - responses = { - @ApiResponse(responseCode = "400", description = "Missing access token"), - @ApiResponse(responseCode = "403", description = "Access token is expired or invalid"), - @ApiResponse(responseCode = "500", description = "Failed to transfer data") - } - ) - void head(ContainerRequestContext context, AsyncResponse response); - - @Operation(description = "Send `POST` data query to the Data Plane.", - responses = { - @ApiResponse(responseCode = "400", description = "Missing access token"), - @ApiResponse(responseCode = "403", description = "Access token is expired or invalid"), - @ApiResponse(responseCode = "500", description = "Failed to transfer data") - } - ) - void post(ContainerRequestContext context, AsyncResponse response); - - @Operation(description = "Send `PUT` data query to the Data Plane.", - responses = { - @ApiResponse(responseCode = "400", description = "Missing access token"), - @ApiResponse(responseCode = "403", description = "Access token is expired or invalid"), - @ApiResponse(responseCode = "500", description = "Failed to transfer data") - } - ) - void put(ContainerRequestContext context, AsyncResponse response); - - @Operation(description = "Send `DELETE` data query to the Data Plane.", - responses = { - @ApiResponse(responseCode = "400", description = "Missing access token"), - @ApiResponse(responseCode = "403", description = "Access token is expired or invalid"), - @ApiResponse(responseCode = "500", description = "Failed to transfer data") - } - ) - void delete(ContainerRequestContext context, AsyncResponse response); - - @Operation(description = "Send `PATCH` data query to the Data Plane.", - responses = { - @ApiResponse(responseCode = "400", description = "Missing access token"), - @ApiResponse(responseCode = "403", description = "Access token is expired or invalid"), - @ApiResponse(responseCode = "500", description = "Failed to transfer data") - } - ) - void patch(ContainerRequestContext context, AsyncResponse response); -} diff --git a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2Controller.java b/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2Controller.java deleted file mode 100644 index 1502fe8..0000000 --- a/extensions/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2Controller.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2025 Metaform Systems, Inc. - * - * This program and the accompanying materials are 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: - * Metaform Systems, Inc. - initial API and implementation - * - */ - -package org.eclipse.edc.connector.dataplane.api.controller; - -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.HEAD; -import jakarta.ws.rs.PATCH; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.PUT; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.container.AsyncResponse; -import jakarta.ws.rs.container.ContainerRequestContext; -import jakarta.ws.rs.container.Suspended; -import jakarta.ws.rs.core.Context; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.StreamingOutput; -import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAuthorizationService; -import org.eclipse.edc.connector.dataplane.spi.pipeline.PipelineService; -import org.eclipse.edc.connector.dataplane.spi.response.TransferErrorResponse; -import org.eclipse.edc.connector.dataplane.util.sink.AsyncStreamingDataSink; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutorService; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; -import static jakarta.ws.rs.core.MediaType.WILDCARD; -import static jakarta.ws.rs.core.Response.Status.FORBIDDEN; -import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; -import static jakarta.ws.rs.core.Response.Status.UNAUTHORIZED; -import static jakarta.ws.rs.core.Response.status; - -@Path("{any:.*}") -@Produces(WILDCARD) -public class DataPlanePublicApiV2Controller implements DataPlanePublicApiV2 { - - private final PipelineService pipelineService; - private final DataFlowRequestSupplier requestSupplier; - private final ExecutorService executorService; - private final DataPlaneAuthorizationService authorizationService; - - public DataPlanePublicApiV2Controller(PipelineService pipelineService, - ExecutorService executorService, - DataPlaneAuthorizationService authorizationService) { - this.pipelineService = pipelineService; - this.authorizationService = authorizationService; - this.requestSupplier = new DataFlowRequestSupplier(); - this.executorService = executorService; - } - - private static Response error(Response.Status status, List errors) { - return status(status).type(APPLICATION_JSON).entity(new TransferErrorResponse(errors)).build(); - } - - @GET - @Override - public void get(@Context ContainerRequestContext requestContext, @Suspended AsyncResponse response) { - handle(requestContext, response); - } - - @HEAD - @Override - public void head(@Context ContainerRequestContext requestContext, @Suspended AsyncResponse response) { - handle(requestContext, response); - } - - /** - * Sends a {@link POST} request to the data source and returns data. - * - * @param requestContext Request context. - * @param response Data fetched from the data source. - */ - @POST - @Override - public void post(@Context ContainerRequestContext requestContext, @Suspended AsyncResponse response) { - handle(requestContext, response); - } - - /** - * Sends a {@link PUT} request to the data source and returns data. - * - * @param requestContext Request context. - * @param response Data fetched from the data source. - */ - @PUT - @Override - public void put(@Context ContainerRequestContext requestContext, @Suspended AsyncResponse response) { - handle(requestContext, response); - } - - /** - * Sends a {@link DELETE} request to the data source and returns data. - * - * @param requestContext Request context. - * @param response Data fetched from the data source. - */ - @DELETE - @Override - public void delete(@Context ContainerRequestContext requestContext, @Suspended AsyncResponse response) { - handle(requestContext, response); - } - - /** - * Sends a {@link PATCH} request to the data source and returns data. - * - * @param requestContext Request context. - * @param response Data fetched from the data source. - */ - @PATCH - @Override - public void patch(@Context ContainerRequestContext requestContext, @Suspended AsyncResponse response) { - handle(requestContext, response); - } - - private void handle(ContainerRequestContext requestContext, AsyncResponse response) { - var contextApi = new ContainerRequestContextApiImpl(requestContext); - - var token = contextApi.headers().get(HttpHeaders.AUTHORIZATION); - if (token == null) { - response.resume(error(UNAUTHORIZED, List.of("Missing Authorization Header"))); - return; - } - - var sourceDataAddress = authorizationService.authorize(token, buildRequestData(requestContext)); - if (sourceDataAddress.failed()) { - response.resume(error(FORBIDDEN, sourceDataAddress.getFailureMessages())); - return; - } - - var startMessage = requestSupplier.apply(contextApi, sourceDataAddress.getContent()); - - processRequest(startMessage, response); - } - - private Map buildRequestData(ContainerRequestContext requestContext) { - var requestData = new HashMap(); - requestData.put("headers", requestContext.getHeaders()); - requestData.put("path", requestContext.getUriInfo()); - requestData.put("method", requestContext.getMethod()); - requestData.put("content-type", requestContext.getMediaType()); - return requestData; - } - - private void processRequest(DataFlowStartMessage dataFlowStartMessage, AsyncResponse response) { - - AsyncStreamingDataSink.AsyncResponseContext asyncResponseContext = callback -> { - StreamingOutput output = t -> callback.outputStreamConsumer().accept(t); - var resp = Response.ok(output).type(callback.mediaType()).build(); - return response.resume(resp); - }; - - var sink = new AsyncStreamingDataSink(asyncResponseContext, executorService); - - pipelineService.transfer(dataFlowStartMessage, sink) - .whenComplete((result, throwable) -> { - if (throwable == null) { - if (result.failed()) { - response.resume(error(INTERNAL_SERVER_ERROR, result.getFailureMessages())); - } - } else { - var error = "Unhandled exception occurred during data transfer: " + throwable.getMessage(); - response.resume(error(INTERNAL_SERVER_ERROR, List.of(error))); - } - }); - } - -} diff --git a/extensions/data-plane-public-api-v2/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/extensions/data-plane-public-api-v2/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension deleted file mode 100644 index 432c052..0000000 --- a/extensions/data-plane-public-api-v2/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ /dev/null @@ -1 +0,0 @@ -org.eclipse.edc.connector.dataplane.api.DataPlanePublicApiV2Extension diff --git a/extensions/data-plane-public-api-v2/src/main/resources/public-api-version.json b/extensions/data-plane-public-api-v2/src/main/resources/public-api-version.json deleted file mode 100644 index 7289070..0000000 --- a/extensions/data-plane-public-api-v2/src/main/resources/public-api-version.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "version": "2.0.1", - "urlPath": "/v2", - "lastUpdated": "2024-07-10T08:56:00Z", - "maturity": "stable" - } -] diff --git a/extensions/data-plane-public-api-v2/src/test/java/org/eclipse/edc/connector/dataplane/api/controller/DataFlowStartMessageSupplierTest.java b/extensions/data-plane-public-api-v2/src/test/java/org/eclipse/edc/connector/dataplane/api/controller/DataFlowStartMessageSupplierTest.java deleted file mode 100644 index 8961e2e..0000000 --- a/extensions/data-plane-public-api-v2/src/test/java/org/eclipse/edc/connector/dataplane/api/controller/DataFlowStartMessageSupplierTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2022 Amadeus - * - * This program and the accompanying materials are 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.connector.dataplane.api.controller; - -import jakarta.ws.rs.HttpMethod; -import jakarta.ws.rs.core.MediaType; -import org.eclipse.edc.connector.dataplane.spi.schema.DataFlowRequestSchema; -import org.eclipse.edc.connector.dataplane.util.sink.AsyncStreamingDataSink; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -class DataFlowStartMessageSupplierTest { - - - private final DataFlowRequestSupplier supplier = new DataFlowRequestSupplier(); - - private static DataAddress createDataAddress() { - return DataAddress.Builder.newInstance().type("test-type").build(); - } - - @Test - void verifyMapping_noInputBody() { - var contextApi = mock(ContainerRequestContextApi.class); - var address = createDataAddress(); - - var method = HttpMethod.GET; - var queryParams = "test-query-param"; - var path = "test-path"; - - when(contextApi.method()).thenReturn(method); - when(contextApi.queryParams()).thenReturn(queryParams); - when(contextApi.path()).thenReturn(path); - - var request = supplier.apply(contextApi, address); - - assertThat(request.getId()).isNotBlank(); - assertThat(request.getDestinationDataAddress().getType()).isEqualTo(AsyncStreamingDataSink.TYPE); - assertThat(request.getSourceDataAddress().getType()).isEqualTo(address.getType()); - assertThat(request.getProperties()).containsExactlyInAnyOrderEntriesOf(Map.of( - DataFlowRequestSchema.PATH, path, - DataFlowRequestSchema.METHOD, method, - DataFlowRequestSchema.QUERY_PARAMS, queryParams - - )); - } - - @Test - void verifyMapping_withInputBody() { - var contextApi = mock(ContainerRequestContextApi.class); - var address = createDataAddress(); - - var method = HttpMethod.GET; - var queryParams = "test-query-param"; - var path = "test-path"; - var body = "Test request body"; - - when(contextApi.method()).thenReturn(method); - when(contextApi.queryParams()).thenReturn(queryParams); - when(contextApi.path()).thenReturn(path); - when(contextApi.mediaType()).thenReturn(MediaType.TEXT_PLAIN); - when(contextApi.body()).thenReturn(body); - - var request = supplier.apply(contextApi, address); - - assertThat(request.getId()).isNotBlank(); - assertThat(request.getDestinationDataAddress().getType()).isEqualTo(AsyncStreamingDataSink.TYPE); - assertThat(request.getSourceDataAddress().getType()).isEqualTo(address.getType()); - assertThat(request.getProperties()).containsExactlyInAnyOrderEntriesOf(Map.of( - DataFlowRequestSchema.PATH, path, - DataFlowRequestSchema.METHOD, method, - DataFlowRequestSchema.QUERY_PARAMS, queryParams, - DataFlowRequestSchema.BODY, body, - DataFlowRequestSchema.MEDIA_TYPE, MediaType.TEXT_PLAIN - )); - } -} diff --git a/extensions/data-plane-public-api-v2/src/test/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2ControllerTest.java b/extensions/data-plane-public-api-v2/src/test/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2ControllerTest.java deleted file mode 100644 index 05aba40..0000000 --- a/extensions/data-plane-public-api-v2/src/test/java/org/eclipse/edc/connector/dataplane/api/controller/DataPlanePublicApiV2ControllerTest.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2022 Amadeus - * - * This program and the accompanying materials are 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: - * Amadeus - initial API and implementation - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - improvements - * - */ - -package org.eclipse.edc.connector.dataplane.api.controller; - -import io.restassured.specification.RequestSpecification; -import jakarta.ws.rs.core.Response; -import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAuthorizationService; -import org.eclipse.edc.connector.dataplane.spi.pipeline.DataSource; -import org.eclipse.edc.connector.dataplane.spi.pipeline.PipelineService; -import org.eclipse.edc.connector.dataplane.spi.pipeline.StreamFailure; -import org.eclipse.edc.connector.dataplane.spi.pipeline.StreamResult; -import org.eclipse.edc.connector.dataplane.spi.resolver.DataAddressResolver; -import org.eclipse.edc.connector.dataplane.util.sink.AsyncStreamingDataSink; -import org.eclipse.edc.junit.annotations.ApiTest; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; -import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; -import java.util.stream.Stream; - -import static io.restassured.RestAssured.given; -import static io.restassured.http.ContentType.JSON; -import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION; -import static java.util.concurrent.CompletableFuture.completedFuture; -import static java.util.concurrent.CompletableFuture.failedFuture; -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.isA; -import static org.hamcrest.CoreMatchers.not; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ApiTest -class DataPlanePublicApiV2ControllerTest extends RestControllerTestBase { - - private final PipelineService pipelineService = mock(); - private final DataAddressResolver dataAddressResolver = mock(); - private final DataPlaneAuthorizationService authorizationService = mock(); - - @BeforeEach - void setup() { - when(authorizationService.authorize(anyString(), anyMap())) - .thenReturn(Result.success(testDestAddress())); - } - - @Test - void should_returnBadRequest_if_missingAuthorizationHeader() { - baseRequest() - .post("/any") - .then() - .statusCode(Response.Status.UNAUTHORIZED.getStatusCode()) - .body("errors[0]", is("Missing Authorization Header")); - } - - @Test - void shouldNotReturn302_whenUrlWithoutTrailingSlash() { - baseRequest() - .post("") - .then() - .statusCode(not(302)); - } - - @Test - void should_returnForbidden_if_tokenValidationFails() { - var token = UUID.randomUUID().toString(); - when(authorizationService.authorize(anyString(), anyMap())).thenReturn(Result.failure("token is not valid")); - - baseRequest() - .header(AUTHORIZATION, token) - .post("/any") - .then() - .statusCode(Response.Status.FORBIDDEN.getStatusCode()) - .contentType(JSON) - .body("errors.size()", is(1)); - - verify(authorizationService).authorize(eq(token), anyMap()); - } - - @Test - void should_returnInternalServerError_if_transferFails() { - var token = UUID.randomUUID().toString(); - var errorMsg = UUID.randomUUID().toString(); - when(dataAddressResolver.resolve(any())).thenReturn(Result.success(testDestAddress())); - when(pipelineService.transfer(any(), any())) - .thenReturn(completedFuture(StreamResult.error(errorMsg))); - - baseRequest() - .header(AUTHORIZATION, token) - .when() - .post("/any") - .then() - .statusCode(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) - .contentType(JSON) - .body("errors[0]", is(errorMsg)); - } - - @Test - void should_returnListOfErrorsAsResponse_if_anythingFails() { - var token = UUID.randomUUID().toString(); - var firstErrorMsg = UUID.randomUUID().toString(); - var secondErrorMsg = UUID.randomUUID().toString(); - - when(dataAddressResolver.resolve(any())).thenReturn(Result.success(testDestAddress())); - when(pipelineService.transfer(any(), any())) - .thenReturn(completedFuture(StreamResult.failure(new StreamFailure(List.of(firstErrorMsg, secondErrorMsg), StreamFailure.Reason.GENERAL_ERROR)))); - - baseRequest() - .header(AUTHORIZATION, token) - .when() - .post("/any") - .then() - .statusCode(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) - .contentType(JSON) - .body("errors", isA(List.class)) - .body("errors[0]", is(firstErrorMsg)) - .body("errors[1]", is(secondErrorMsg)); - } - - @Test - void should_returnInternalServerError_if_transferThrows() { - var token = UUID.randomUUID().toString(); - var errorMsg = UUID.randomUUID().toString(); - when(dataAddressResolver.resolve(any())).thenReturn(Result.success(testDestAddress())); - when(pipelineService.transfer(any(DataFlowStartMessage.class), any())) - .thenReturn(failedFuture(new RuntimeException(errorMsg))); - - baseRequest() - .header(AUTHORIZATION, token) - .when() - .post("/any") - .then() - .statusCode(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) - .contentType(JSON) - .body("errors[0]", is("Unhandled exception occurred during data transfer: " + errorMsg)); - } - - @Test - void shouldStreamSourceToResponse() { - when(dataAddressResolver.resolve(any())).thenReturn(Result.success(testDestAddress())); - when(pipelineService.transfer(any(), any())).thenAnswer(i -> { - ((AsyncStreamingDataSink) i.getArgument(1)).transfer(new TestDataSource("application/something", "data")); - return CompletableFuture.completedFuture(StreamResult.success()); - }); - - var responseBody = baseRequest() - .header(AUTHORIZATION, UUID.randomUUID().toString()) - .when() - .post("/any?foo=bar") - .then() - .log().ifError() - .statusCode(Response.Status.OK.getStatusCode()) - .contentType("application/something") - .extract().body().asString(); - - assertThat(responseBody).isEqualTo("data"); - var requestCaptor = ArgumentCaptor.forClass(DataFlowStartMessage.class); - verify(pipelineService).transfer(requestCaptor.capture(), any()); - var request = requestCaptor.getValue(); - assertThat(request.getDestinationDataAddress().getType()).isEqualTo(AsyncStreamingDataSink.TYPE); - assertThat(request.getSourceDataAddress().getType()).isEqualTo("test"); - assertThat(request.getProperties()).containsEntry("method", "POST").containsEntry("pathSegments", "any").containsEntry("queryParams", "foo=bar"); - } - - @Override - protected Object controller() { - return new DataPlanePublicApiV2Controller(pipelineService, Executors.newSingleThreadExecutor(), authorizationService); - } - - private RequestSpecification baseRequest() { - return given() - .baseUri("http://localhost:" + port) - .when(); - } - - private DataAddress testDestAddress() { - return DataAddress.Builder.newInstance().type("test").build(); - } - - private record TestDataSource(String mediaType, String data) implements DataSource, DataSource.Part { - - @Override - public StreamResult> openPartStream() { - return StreamResult.success(Stream.of(this)); - } - - @Override - public String name() { - return "test"; - } - - @Override - public InputStream openStream() { - return new ByteArrayInputStream(data.getBytes()); - } - - } - -} diff --git a/gradle.properties b/gradle.properties index 36c2f2c..c8d2e03 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,6 +12,5 @@ # # group=org.eclipse.edc -version=0.16.0-SNAPSHOT +version=0.17.0-SNAPSHOT jadVersion=0.0.2-SNAPSHOT -edcGradlePluginsVersion=0.16.0-SNAPSHOT diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 27dd867..b27f677 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,20 +5,24 @@ format.version = "1.1" awaitility = "4.3.0" bouncyCastle-jdk18on = "1.83" edc = "0.17.0-SNAPSHOT" -edc-build = "1.3.0" +edc-build = "1.4.0" jackson = "2.21.2" jackson-annotations = "2.20" restAssured = "6.0.0" rsApi = "4.0.0" swagger = "2.2.45" jersey = "3.1.11" - +postgres = "42.7.10" [libraries] # upstream EDC dependencies edc-did-core = { module = "org.eclipse.edc:identity-did-core", version.ref = "edc" } +edc-core-boot = { module = "org.eclipse.edc:boot", version.ref = "edc" } +edc-core-runtime = { module = "org.eclipse.edc:runtime-core", version.ref = "edc" } +edc-core-token = { module = "org.eclipse.edc:token-core", version.ref = "edc" } edc-core-jersey = { module = "org.eclipse.edc:jersey-core", version.ref = "edc" } edc-core-jetty = { module = "org.eclipse.edc:jetty-core", version.ref = "edc" } +edc-core-api = { module = "org.eclipse.edc:api-core", version.ref = "edc" } edc-core-participantcontext-config = { module = "org.eclipse.edc:participant-context-config-core", version.ref = "edc" } edc-junit = { module = "org.eclipse.edc:junit", version.ref = "edc" } edc-vault-hashicorp = { module = "org.eclipse.edc:vault-hashicorp", version.ref = "edc" } @@ -26,18 +30,25 @@ edc-store-participantcontext-config-sql = { module = "org.eclipse.edc:participan edc-lib-http = { module = "org.eclipse.edc:http-lib", version.ref = "edc" } edc-lib-util = { module = "org.eclipse.edc:util-lib", version.ref = "edc" } edc-lib-sql = { module = "org.eclipse.edc:sql-lib", version.ref = "edc" } -edc-lib-util-dataplane = { module = "org.eclipse.edc:data-plane-util", version.ref = "edc" } +edc-lib-token = { module = "org.eclipse.edc:token-lib", version.ref = "edc" } +edc-lib-oauth2-authn = { module = "org.eclipse.edc:auth-authentication-oauth2-lib", version.ref = "edc" } edc-core-edrstore = { module = "org.eclipse.edc:edr-store-core", version.ref = "edc" } edc-edrstore-receiver = { module = "org.eclipse.edc:edr-store-receiver", version.ref = "edc" } edc-core-sql-bootstrapper = { module = "org.eclipse.edc:sql-bootstrapper", version.ref = "edc" } -edc-dataplane-iam = { module = "org.eclipse.edc:data-plane-iam", version.ref = "edc" } +edc-core-http = { module = "org.eclipse.edc:http", version.ref = "edc" } +edc-core-sql = { module = "org.eclipse.edc:sql-core", version.ref = "edc" } +edc-core-connector = { module = "org.eclipse.edc:connector-core", version.ref = "edc" } +edc-transaction-local = { module = "org.eclipse.edc:transaction-local", version.ref = "edc" } +edc-transaction-pool = { module = "org.eclipse.edc:sql-pool-apache-commons", version.ref = "edc" } +edc-api-control-configuration = { module = "org.eclipse.edc:control-api-configuration", version.ref = "edc" } +edc-api-observability = { module = "org.eclipse.edc:api-observability", version.ref = "edc" } + # EDC spi dependencies edc-spi-participantcontext = { module = "org.eclipse.edc:connector-participant-context-spi", version.ref = "edc" } edc-spi-participantcontext-config = { module = "org.eclipse.edc:participant-context-config-spi", version.ref = "edc" } edc-spi-web = { module = "org.eclipse.edc:web-spi", version.ref = "edc" } edc-spi-controlplane = { module = "org.eclipse.edc:control-plane-spi", version.ref = "edc" } edc-spi-dataplane-selector = { module = "org.eclipse.edc:data-plane-selector-spi", version.ref = "edc" } -edc-spi-dataplane = { module = "org.eclipse.edc:data-plane-spi", version.ref = "edc" } edc-spi-http = { module = "org.eclipse.edc:http-spi", version.ref = "edc" } edc-spi-catalog = { module = "org.eclipse.edc:catalog-spi", version.ref = "edc" } edc-spi-transaction = { module = "org.eclipse.edc:transaction-spi", version.ref = "edc" } @@ -56,8 +67,6 @@ edc-ih-api-participants = { module = "org.eclipse.edc:participant-context-api", edc-issuance-spi = { module = "org.eclipse.edc:issuerservice-issuance-spi", version.ref = "edc" } # BOM modules -edc-bom-dataplane = { module = "org.eclipse.edc:dataplane-base-bom", version.ref = "edc" } -edc-bom-dataplane-sql = { module = "org.eclipse.edc:dataplane-feature-sql-bom", version.ref = "edc" } edc-bom-identityhub = { module = "org.eclipse.edc:identityhub-oauth2-bom", version.ref = "edc" } edc-bom-identityhub-sql = { module = "org.eclipse.edc:identityhub-feature-sql-bom", version.ref = "edc" } edc-bom-issuerservice = { module = "org.eclipse.edc:issuerservice-oauth2-bom", version.ref = "edc" } @@ -76,6 +85,7 @@ tink = { module = "com.google.crypto.tink:tink", version = "1.21.0" } restAssured = { module = "io.rest-assured:rest-assured", version.ref = "restAssured" } jakarta-rsApi = { module = "jakarta.ws.rs:jakarta.ws.rs-api", version.ref = "rsApi" } jersey-multipart = { module = "org.glassfish.jersey.media:jersey-media-multipart", version.ref = "jersey" } +postgres = { module = "org.postgresql:postgresql", version.ref = "postgres" } [plugins] shadow = { id = "com.gradleup.shadow", version = "9.4.1" } diff --git a/k8s/apps/dataplane-config.yaml b/k8s/apps/dataplane-config.yaml index c9e6b43..13970af 100644 --- a/k8s/apps/dataplane-config.yaml +++ b/k8s/apps/dataplane-config.yaml @@ -20,11 +20,6 @@ metadata: data: edc.hostname: "dataplane.edc-v.svc.cluster.local" - edc.transfer.proxy.token.verifier.publickey.alias: "dataplane-private" - edc.transfer.proxy.token.signer.privatekey.alias: "dataplane-public" - - edc.dpf.selector.url: "http://controlplane.edc-v.svc.cluster.local:8083/api/control/v1/dataplanes" - web.http.port: "8080" web.http.path: "/api" web.http.control.port: "8083" @@ -44,6 +39,6 @@ data: edc.datasource.default.password: "dp" edc.sql.schema.autocreate: "true" -# EDC_IAM_STS_OAUTH_TOKEN_URL: "http://foobar/token" -# EDC_IAM_STS_OAUTH_CLIENT_ID: "${PARTICIPANT_ID}" -# EDC_IAM_STS_OAUTH_CLIENT_SECRET_ALIAS: "${PARTICIPANT_ID}-sts-client-secret" \ No newline at end of file + edc.iam.siglet.issuer: "siglet-issuer" + edc.iam.siglet.jwks.url: "http://siglet.edc-v.svc.cluster.local:8080/keys" + diff --git a/k8s/apps/redline-config.yaml b/k8s/apps/redline-config.yaml index b1c5c06..9e3636b 100644 --- a/k8s/apps/redline-config.yaml +++ b/k8s/apps/redline-config.yaml @@ -33,4 +33,5 @@ data: VAULT_URL: "http://vault.edc-v.svc.cluster.local:8200" IDENTITYHUB_URL: "http://identityhub.edc-v.svc.cluster.local:7081/identities" TENANT-MANAGER_URL: "http://tenant-manager.edc-v.svc.cluster.local:8080" + SIGLET_URL: "http://siglet.edc-v.svc.cluster.local:8080" CORS_ALLOWED_ORIGIN: "http://ui.localhost" \ No newline at end of file diff --git a/launchers/dataplane/build.gradle.kts b/launchers/dataplane/build.gradle.kts index f022eb8..9da34e2 100644 --- a/launchers/dataplane/build.gradle.kts +++ b/launchers/dataplane/build.gradle.kts @@ -20,15 +20,9 @@ plugins { dependencies { runtimeOnly(libs.tink) - implementation(libs.edc.bom.dataplane) { - exclude("org.eclipse.edc", "data-plane-self-registration") - } - runtimeOnly(project(":extensions:data-plane-public-api-v2")) runtimeOnly(project(":extensions:data-plane-certs")) - runtimeOnly(libs.edc.core.participantcontext.config) runtimeOnly(libs.edc.vault.hashicorp) - runtimeOnly(libs.edc.bom.dataplane.sql) } tasks.shadowJar { diff --git a/launchers/dataplane/src/main/java/org/eclipse/edc/virtualized/dataplane/keyseed/KeySeedExtension.java b/launchers/dataplane/src/main/java/org/eclipse/edc/virtualized/dataplane/keyseed/KeySeedExtension.java deleted file mode 100644 index ea73f55..0000000 --- a/launchers/dataplane/src/main/java/org/eclipse/edc/virtualized/dataplane/keyseed/KeySeedExtension.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2025 Metaform Systems, Inc. - * - * This program and the accompanying materials are 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: - * Metaform Systems, Inc. - initial API and implementation - * - */ - -package org.eclipse.edc.virtualized.dataplane.keyseed; - -import com.nimbusds.jose.jwk.Curve; -import com.nimbusds.jose.jwk.KeyUse; -import com.nimbusds.jose.jwk.OctetKeyPair; -import com.nimbusds.jose.jwk.gen.OctetKeyPairGenerator; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -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.Date; -import java.util.UUID; - -public class KeySeedExtension implements ServiceExtension { - - @Setting(key = "edc.transfer.proxy.token.signer.privatekey.alias") - private String tokenSignerPrivateKeyAlias; - - @Setting(key = "edc.transfer.proxy.token.verifier.publickey.alias") - private String tokenVerifierPublicKeyAlias; - - @Inject - private Vault vault; - - @Override - public void initialize(ServiceExtensionContext context) { - try { - OctetKeyPair jwk = new OctetKeyPairGenerator(Curve.Ed25519) - .keyUse(KeyUse.SIGNATURE) // indicate the intended use of the key (optional) - .keyID(UUID.randomUUID().toString()) // give the key a unique ID (optional) - .issueTime(new Date()) - .generate(); - vault.storeSecret(tokenSignerPrivateKeyAlias, jwk.toJSONString()); - vault.storeSecret(tokenVerifierPublicKeyAlias, jwk.toPublicJWK().toJSONString()); - context.getMonitor().info("Key seed extension initialized: private key: %s, public key: %s".formatted(tokenSignerPrivateKeyAlias, tokenVerifierPublicKeyAlias)); - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/launchers/dataplane/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/launchers/dataplane/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension deleted file mode 100644 index 44dc346..0000000 --- a/launchers/dataplane/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ /dev/null @@ -1,14 +0,0 @@ -# -# Copyright (c) 2025 Metaform Systems, Inc. -# -# This program and the accompanying materials are 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: -# Metaform Systems, Inc. - initial API and implementation -# -# -org.eclipse.edc.virtualized.dataplane.keyseed.KeySeedExtension \ No newline at end of file diff --git a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/Get certificate.bru b/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/Get certificate.bru index d2ced4b..41948e6 100644 --- a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/Get certificate.bru +++ b/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/Get certificate.bru @@ -11,7 +11,7 @@ get { } headers { - Authorization: {{ACCESS_TOKEN}} + Authorization: Bearer {{ACCESS_TOKEN}} } settings { diff --git a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/List certificates.bru b/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/List certificates.bru index f1930bd..8d7331b 100644 --- a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/List certificates.bru +++ b/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/List certificates.bru @@ -11,7 +11,7 @@ post { } headers { - Authorization: {{ACCESS_TOKEN}} + Authorization: Bearer {{ACCESS_TOKEN}} } body:json { diff --git a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/Setup Transfer.bru b/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/Setup Transfer.bru index 0f078c9..9d9d476 100644 --- a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/Setup Transfer.bru +++ b/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/Consumer/Setup Transfer.bru @@ -20,7 +20,7 @@ body:json { script:post-response { try { const json = res.getBody(); - const accessToken = json['https://w3id.org/edc/v0.0.1/ns/authorization']; + const accessToken = json['authorization']; if (accessToken && typeof accessToken === 'string') { bru.setVar('ACCESS_TOKEN', accessToken); diff --git a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/folder.bru b/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/folder.bru index 7735250..dfa4360 100644 --- a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/folder.bru +++ b/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Certs/folder.bru @@ -1,6 +1,6 @@ meta { name: Http Certs - seq: 2 + seq: 1 } auth { diff --git a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/Get Catalog.bru b/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/Get Catalog.bru deleted file mode 100644 index d297dd1..0000000 --- a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/Get Catalog.bru +++ /dev/null @@ -1,55 +0,0 @@ -meta { - name: Get Catalog - type: http - seq: 1 -} - -post { - url: {{cpBaseUrl}}/api/mgmt/v1alpha/participants/{{consumer_id}}/catalog - body: json - auth: inherit -} - -body:json { - { - "counterPartyDid": "did:web:identityhub.edc-v.svc.cluster.local%3A7083:provider" - } -} - -script:post-response { - try { - const json = res.getBody(); - - // Navigate the expected structure: Catalog -> dataset[0] -> hasPolicy[0] -> @id - const assetId = "todo_asset"; - const datasets = json.dataset || []; - const firstDataset = datasets.filter(dataset => dataset["@id"] === assetId)[0] || {}; - - const policies = firstDataset.hasPolicy || []; - const firstPolicy = policies[0] || {}; - const policyId = firstPolicy['@id']; - - if (policyId && typeof policyId === 'string') { - bru.setVar('POLICY_ID', policyId); - console.log('Offer ID (POLICY_ID) is:', policyId); - } else { - console.warn('Policy ID not found in response at dataset[0].hasPolicy[0]["@id"]'); - } - - // Optional: assertion to ensure it exists - test('Policy ID is present and stored', function () { - expect(policyId, 'Policy ID should exist').to.be.a('string').and.not.empty; - }); - - } catch (e) { - console.error('Failed to parse response or set POLICY_ID:', e); - test('Response is valid JSON', function () { - throw e; - }); - } -} - -settings { - encodeUrl: true - timeout: 0 -} diff --git a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/Get Data.bru b/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/Get Data.bru deleted file mode 100644 index cb93df5..0000000 --- a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/Get Data.bru +++ /dev/null @@ -1,23 +0,0 @@ -meta { - name: Get Data - type: http - seq: 2 -} - -post { - url: {{cpBaseUrl}}/api/mgmt/v1alpha/participants/{{consumer_id}}/data - body: json - auth: inherit -} - -body:json { - { - "providerId":"did:web:identityhub.edc-v.svc.cluster.local%3A7083:provider", - "policyId": "{{POLICY_ID}}" - } -} - -settings { - encodeUrl: true - timeout: 0 -} diff --git a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/folder.bru b/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/folder.bru deleted file mode 100644 index c4e0559..0000000 --- a/requests/EDC-V Onboarding/EDC-V Management/Data Transfer/Http Todo/folder.bru +++ /dev/null @@ -1,8 +0,0 @@ -meta { - name: Http Todo - seq: 1 -} - -auth { - mode: inherit -} diff --git a/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Cert Asset.bru b/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Cert Asset.bru index 690c5d6..0b498d0 100644 --- a/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Cert Asset.bru +++ b/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Cert Asset.bru @@ -1,7 +1,7 @@ meta { name: Create Cert Asset type: http - seq: 2 + seq: 1 } post { @@ -22,10 +22,6 @@ body:json { }, "privateProperties": { "permission": "{{ASSET_PERMISSION}}" - }, - "dataAddress": { - "@type": "DataAddress", - "type": "HttpCertData" } } } diff --git a/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Contract Definition.bru b/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Contract Definition.bru index 907f3b7..3786a26 100644 --- a/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Contract Definition.bru +++ b/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Contract Definition.bru @@ -1,7 +1,7 @@ meta { name: Create Contract Definition type: http - seq: 3 + seq: 2 } post { diff --git a/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Policy.bru b/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Policy.bru index 6048391..4bc1f01 100644 --- a/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Policy.bru +++ b/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Policy.bru @@ -1,7 +1,7 @@ meta { name: Create Policy type: http - seq: 4 + seq: 3 } post { diff --git a/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Todo Asset.bru b/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Todo Asset.bru deleted file mode 100644 index d456bf0..0000000 --- a/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Create Todo Asset.bru +++ /dev/null @@ -1,39 +0,0 @@ -meta { - name: Create Todo Asset - type: http - seq: 1 -} - -post { - url: {{cpBaseUrl}}/api/mgmt/v5alpha/participants/{{provider_id}}/assets - body: json - auth: inherit -} - -body:json { - { - "@context": [ - "https://w3id.org/edc/connector/management/v2" - ], - "@type": "Asset", - "@id": "{{ASSET_TODO_ID}}", - "properties": { - "description": "This asset requires the Membership credential to access" - }, - "privateProperties": { - "permission": "{{ASSET_PERMISSION}}" - }, - "dataAddress": { - "@type": "DataAddress", - "type": "HttpData", - "baseUrl": "https://jsonplaceholder.typicode.com/todos", - "proxyPath": "true", - "proxyQueryParams": "true" - } - } -} - -settings { - encodeUrl: true - timeout: 0 -} diff --git a/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Prepare Dataplane.bru b/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Prepare Dataplane.bru index 1380617..c04a9c2 100644 --- a/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Prepare Dataplane.bru +++ b/requests/EDC-V Onboarding/EDC-V Management/Prepare Provider Participant/Prepare Dataplane.bru @@ -1,7 +1,7 @@ meta { name: Prepare Dataplane type: http - seq: 5 + seq: 4 } post { @@ -12,14 +12,11 @@ post { body:json { { - "allowedSourceTypes": [ - "HttpData", - "HttpCertData" - ], + "id": "dataplane-1", "allowedTransferTypes": [ "HttpData-PULL" ], - "url": "http://dataplane.edc-v.svc.cluster.local:8083/api/control/v1/dataflows" + "url": "http://siglet.edc-v.svc.cluster.local:8081/api/v1/dataflows" } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 801adc7..325a21c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,7 +26,6 @@ pluginManagement { rootProject.name = "jad" include(":extensions:api:mgmt") -include(":extensions:data-plane-public-api-v2") include(":extensions:data-plane-certs") include(":tests:end2end") diff --git a/tests/end2end/src/test/java/org/eclipse/edc/jad/tests/DataTransferEndToEndTest.java b/tests/end2end/src/test/java/org/eclipse/edc/jad/tests/DataTransferEndToEndTest.java index e48e812..8021d8e 100644 --- a/tests/end2end/src/test/java/org/eclipse/edc/jad/tests/DataTransferEndToEndTest.java +++ b/tests/end2end/src/test/java/org/eclipse/edc/jad/tests/DataTransferEndToEndTest.java @@ -146,44 +146,6 @@ private static String getCellId() { .extract().jsonPath().getString("[0].id"); } - @Test - void testTodoDataTransfer() { - - // seed provider - MONITOR.info("Seeding provider"); - var providerAccessToken = getAccessToken(providerCredentials.clientId(), providerCredentials.clientSecret(), "management-api:write").accessToken(); - - var assetId = createAsset(providerCredentials.clientId(), providerAccessToken, "asset.json"); - var policyDefId = createPolicyDef(providerCredentials.clientId(), providerAccessToken, "policy-def.json"); - createContractDef(providerCredentials.clientId(), providerAccessToken, policyDefId, policyDefId, assetId); - registerDataPlane(providerCredentials.clientId(), providerAccessToken); - - // perform data transfer - MONITOR.info("Starting data transfer"); - var catalog = fetchCatalog(consumerCredentials); - - MONITOR.info("Catalog received, starting data transfer"); - var offerId = catalog.datasets().stream().filter(dataSet -> dataSet.id().equals(assetId)).findFirst().get().offers().get(0).id(); - assertThat(offerId).isNotNull(); - - //download dummy data - var jsonResponse = given() - .baseUri(CONTROLPLANE_BASE_URL) - .auth().oauth2(getAccessToken(consumerCredentials.clientId(), consumerCredentials.clientSecret(), "management-api:write").accessToken()) - .body(""" - { - "providerId":"%s", - "policyId": "%s" - } - """.formatted(providerContextId, offerId)) - .contentType("application/json") - .post("/api/mgmt/v1alpha/participants/%s/data".formatted(consumerCredentials.clientId())) - .then() - .statusCode(200) - .extract().body().asPrettyString(); - assertThat(jsonResponse).isNotNull(); - } - @Test void testCertDataTransfer() { @@ -220,11 +182,11 @@ void testCertDataTransfer() { .statusCode(200) .extract().body().as(Map.class); - var accessToken = transferResponse.get("https://w3id.org/edc/v0.0.1/ns/authorization"); + var accessToken = transferResponse.get("authorization"); var list = given() .baseUri(DATAPLANE_BASE_URL) - .header("Authorization", accessToken) + .header("Authorization", "Bearer " + accessToken) .body("{}") .contentType("application/json") .post("/app/public/api/data/certs/request") @@ -323,9 +285,9 @@ private void registerDataPlane(String participantContextId, String accessToken) .auth().oauth2(accessToken) .body(""" { - "allowedSourceTypes": [ "HttpData", "HttpCertData" ], + "id": "dataplane-1", "allowedTransferTypes": [ "HttpData-PULL" ], - "url": "http://dataplane.edc-v.svc.cluster.local:8083/api/control/v1/dataflows" + "url": "http://siglet.edc-v.svc.cluster.local:8081/api/v1/dataflows" } """) .post("/api/mgmt/v5alpha/dataplanes/%s".formatted(participantContextId)) diff --git a/tests/end2end/src/test/resources/asset-cert.json b/tests/end2end/src/test/resources/asset-cert.json index 7b8635c..da19337 100644 --- a/tests/end2end/src/test/resources/asset-cert.json +++ b/tests/end2end/src/test/resources/asset-cert.json @@ -5,9 +5,5 @@ "@type": "Asset", "properties": { "description": "This asset requires the Membership credential to access" - }, - "dataAddress": { - "@type": "DataAddress", - "type": "HttpCertData" } } \ No newline at end of file diff --git a/tests/end2end/src/test/resources/asset-restricted.json b/tests/end2end/src/test/resources/asset-restricted.json index 6b47c9c..f085ca6 100644 --- a/tests/end2end/src/test/resources/asset-restricted.json +++ b/tests/end2end/src/test/resources/asset-restricted.json @@ -5,12 +5,5 @@ "@type": "Asset", "properties": { "description": "This asset requires the Manufacturer credential to access" - }, - "dataAddress": { - "@type": "DataAddress", - "type": "HttpData", - "baseUrl": "https://jsonplaceholder.typicode.com/todos", - "proxyPath": "true", - "proxyQueryParams": "true" } } \ No newline at end of file diff --git a/tests/end2end/src/test/resources/asset.json b/tests/end2end/src/test/resources/asset.json index 2934f3a..da19337 100644 --- a/tests/end2end/src/test/resources/asset.json +++ b/tests/end2end/src/test/resources/asset.json @@ -5,12 +5,5 @@ "@type": "Asset", "properties": { "description": "This asset requires the Membership credential to access" - }, - "dataAddress": { - "@type": "DataAddress", - "type": "HttpData", - "baseUrl": "https://jsonplaceholder.typicode.com/todos", - "proxyPath": "true", - "proxyQueryParams": "true" } } \ No newline at end of file