Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(DataPlane): Add system test for Client Data Pull with query parameters in the request #1731

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ in the detailed section referring to by linking pull requests or issues.

#### Added

*
* System test for client data pull with query parameters (#1180)

#### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@
import org.eclipse.dataspaceconnector.iam.did.spi.credentials.CredentialsVerifier;
import org.eclipse.dataspaceconnector.iam.did.spi.document.DidConstants;
import org.eclipse.dataspaceconnector.iam.did.spi.document.DidDocument;
import org.eclipse.dataspaceconnector.iam.did.spi.document.JwkPublicKey;
import org.eclipse.dataspaceconnector.iam.did.spi.document.Service;
import org.eclipse.dataspaceconnector.iam.did.spi.document.VerificationMethod;
import org.eclipse.dataspaceconnector.iam.did.spi.key.PrivateKeyWrapper;
import org.eclipse.dataspaceconnector.iam.did.spi.key.PublicKeyWrapper;
import org.eclipse.dataspaceconnector.iam.did.spi.resolution.DidResolverRegistry;
import org.eclipse.dataspaceconnector.spi.iam.ClaimToken;
import org.eclipse.dataspaceconnector.spi.iam.IdentityService;
Expand Down Expand Up @@ -78,17 +75,17 @@ public Result<ClaimToken> verifyJwtToken(TokenRepresentation tokenRepresentation
monitor.debug("Extracting public key");

// this will return the _first_ public key entry
Optional<VerificationMethod> publicKey = getPublicKey(didResult.getContent());
var publicKey = getPublicKey(didResult.getContent());
if (publicKey.isEmpty()) {
return Result.failure("Public Key not found in DID Document!");
}

//convert the POJO into a usable PK-wrapper:
JwkPublicKey publicKeyJwk = publicKey.get().getPublicKeyJwk();
PublicKeyWrapper publicKeyWrapper = KeyConverter.toPublicKeyWrapper(publicKeyJwk, publicKey.get().getId());
var publicKeyJwk = publicKey.get().getPublicKeyJwk();
var publicKeyWrapper = KeyConverter.toPublicKeyWrapper(publicKeyJwk, publicKey.get().getId());

monitor.debug("Verifying JWT with public key...");
Result<Void> verified = VerifiableCredentialFactory.verify(jwt, publicKeyWrapper, audience);
var verified = VerifiableCredentialFactory.verify(jwt, publicKeyWrapper, audience);
if (verified.failed()) {
verified.getFailureMessages().forEach(m -> monitor.debug(() -> "Failure in token verification: " + m));
return Result.failure("Token could not be verified!");
Expand All @@ -108,10 +105,6 @@ public Result<ClaimToken> verifyJwtToken(TokenRepresentation tokenRepresentation
}
}

String getHubUrl(DidDocument did) {
return did.getService().stream().filter(service -> service.getType().equals(DidConstants.HUB_URL)).map(Service::getServiceEndpoint).findFirst().orElseThrow();
}

@NotNull
private Optional<VerificationMethod> getPublicKey(DidDocument did) {
return did.getVerificationMethod().stream().filter(vm -> DidConstants.ALLOWED_VERIFICATION_TYPES.contains(vm.getType())).findFirst();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.time.Clock;
import java.util.Map;

Expand All @@ -52,15 +51,9 @@ abstract class DecentralizedIdentityServiceTest {
private static final Faker FAKER = new Faker();
private static final String DID_DOCUMENT = getResourceFileContentAsString("dids.json");

String didUrl = FAKER.internet().url();
private final String didUrl = FAKER.internet().url();
private DecentralizedIdentityService identityService;

@Test
void verifyResolveHubUrl() throws IOException {
var url = identityService.getHubUrl(new ObjectMapper().readValue(DID_DOCUMENT, DidDocument.class));
assertEquals("https://myhub.com", url);
}

@Test
void generateAndVerifyJwtToken_valid() {
var result = identityService.obtainClientCredentials(TokenParameters.Builder.newInstance()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public String name() {
public void initialize(ServiceExtensionContext context) {
var exposedHttpPort = context.getConfig().getInteger("web.http.port");
webService.registerResource(new ProviderBackendApiController());
webService.registerResource(new ConsumerBackendServiceController(context.getMonitor(), okHttpClient));
webService.registerResource(new ConsumerBackendServiceController(context.getMonitor()));
webService.registerResource(new BackendServiceHttpProvisionerController(context.getMonitor(), okHttpClient, typeManager, exposedHttpPort));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,52 +18,42 @@
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.eclipse.dataspaceconnector.spi.monitor.Monitor;
import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference;

import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;

import static java.lang.String.format;

@Path("/consumer")
public class ConsumerBackendServiceController {

private final Monitor monitor;
private final OkHttpClient httpClient;
private final AtomicReference<String> data = new AtomicReference<>();
private final Map<String, EndpointDataReference> dataReference = new ConcurrentHashMap<>();

public ConsumerBackendServiceController(Monitor monitor, OkHttpClient httpClient) {
public ConsumerBackendServiceController(Monitor monitor) {
this.monitor = monitor;
this.httpClient = httpClient;
}

@Path("/pull")
@Path("/dataReference")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
public void pullData(EndpointDataReference dataReference) {
String url = dataReference.getEndpoint();
monitor.debug("Endpoint Data Reference received, will call data plane at " + url);
var request = new Request.Builder()
.url(url)
.addHeader(dataReference.getAuthKey(), dataReference.getAuthCode())
.build();
public void pushDataReference(EndpointDataReference edr) {
monitor.debug("Received new endpoint data reference with url " + edr.getEndpoint());
dataReference.put(edr.getId(), edr);
}

try (var response = httpClient.newCall(request).execute()) {
var body = response.body();
var string = body.string();
if (response.isSuccessful()) {
monitor.info("Data plane responded correctly: " + string);
data.set(string);
} else {
monitor.warning(format("Data plane responded with error: %s %s", response.code(), string));
}
} catch (Exception e) {
monitor.severe(format("Error in calling the data plane at %s", url), e);
}
@Path("/dataReference/{id}")
@GET
@Produces({ MediaType.APPLICATION_JSON })
public EndpointDataReference getDataReference(@PathParam("id") String id) {
return Optional.ofNullable(dataReference.get(id)).orElseThrow(NoSuchElementException::new);
}

@Path("/store")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,22 @@

package org.eclipse.dataspaceconnector.test.e2e;

import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;

import java.util.Map;

@Path("/provider")
public class ProviderBackendApiController {

@Path("/data")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> getData() {
return Map.of("message", "some information");
public Map<String, String> getData(@DefaultValue("some information") @QueryParam("message") String message) {
return Map.of("message", message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.Map;
import java.util.UUID;

import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -37,7 +39,7 @@ public abstract class AbstractEndToEndTransfer {
void httpPullDataTransfer() {
PROVIDER.registerDataPlane();
CONSUMER.registerDataPlane();
String definitionId = "1";
var definitionId = "1";
createAssetAndContractDefinitionOnProvider("asset-id", definitionId, "HttpData");

var catalog = CONSUMER.getCatalog(PROVIDER.idsEndpoint());
Expand All @@ -50,29 +52,30 @@ void httpPullDataTransfer() {

assertThat(contractAgreementId).isNotEmpty();

var transferProcessId = CONSUMER.dataRequest(contractAgreementId, assetId, PROVIDER, sync());
var dataRequestId = UUID.randomUUID().toString();
var transferProcessId = CONSUMER.dataRequest(dataRequestId, contractAgreementId, assetId, PROVIDER, sync());

await().atMost(timeout).untilAsserted(() -> {
var state = CONSUMER.getTransferProcessState(transferProcessId);
assertThat(state).isEqualTo(COMPLETED.name());
});

await().atMost(timeout).untilAsserted(() -> {
given()
.baseUri(CONSUMER.backendService().toString())
.when()
.get("/api/consumer/data")
.then()
.statusCode(200)
.body("message", equalTo("some information"));
});
// retrieve the data reference
var edr = CONSUMER.getDataReference(dataRequestId);

// pull the data without query parameter
await().atMost(timeout).untilAsserted(() -> CONSUMER.pullData(edr, Map.of(), equalTo("some information")));

// pull the data with additional query parameter
var msg = UUID.randomUUID().toString();
await().atMost(timeout).untilAsserted(() -> CONSUMER.pullData(edr, Map.of("message", msg), equalTo(msg)));
}

@Test
void httpPullDataTransferProvisioner() {
PROVIDER.registerDataPlane();
CONSUMER.registerDataPlane();
String definitionId = "1";
var definitionId = "1";
createAssetAndContractDefinitionOnProvider("asset-id", definitionId, "HttpProvision");

await().atMost(timeout).untilAsserted(() -> {
Expand All @@ -88,22 +91,16 @@ void httpPullDataTransferProvisioner() {

assertThat(contractAgreementId).isNotEmpty();

var transferProcessId = CONSUMER.dataRequest(contractAgreementId, assetId, PROVIDER, sync());
var dataRequestId = UUID.randomUUID().toString();
var transferProcessId = CONSUMER.dataRequest(dataRequestId, contractAgreementId, assetId, PROVIDER, sync());

await().atMost(timeout).untilAsserted(() -> {
var state = CONSUMER.getTransferProcessState(transferProcessId);
assertThat(state).isEqualTo(COMPLETED.name());
});

await().atMost(timeout).untilAsserted(() -> {
given()
.baseUri(CONSUMER.backendService().toString())
.when()
.get("/api/consumer/data")
.then()
.statusCode(200)
.body("message", equalTo("some information"));
});
var edr = CONSUMER.getDataReference(dataRequestId);
await().atMost(timeout).untilAsserted(() -> CONSUMER.pullData(edr, Map.of(), equalTo("some information")));
}

@Test
Expand All @@ -125,7 +122,7 @@ void httpPushDataTransfer() {
var destination = HttpDataAddress.Builder.newInstance()
.baseUrl(CONSUMER.backendService() + "/api/consumer/store")
.build();
var transferProcessId = CONSUMER.dataRequest(contractAgreementId, assetId, PROVIDER, destination);
var transferProcessId = CONSUMER.dataRequest(UUID.randomUUID().toString(), contractAgreementId, assetId, PROVIDER, destination);

await().atMost(timeout).untilAsserted(() -> {
var state = CONSUMER.getTransferProcessState(transferProcessId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import org.eclipse.dataspaceconnector.spi.types.domain.DataAddress;
import org.eclipse.dataspaceconnector.spi.types.domain.catalog.Catalog;
import org.eclipse.dataspaceconnector.spi.types.domain.contract.offer.ContractOffer;
import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference;
import org.eclipse.dataspaceconnector.spi.types.domain.transfer.TransferType;
import org.hamcrest.Matcher;
import org.jetbrains.annotations.NotNull;

import java.net.URI;
Expand Down Expand Up @@ -74,7 +76,8 @@ public void createAsset(String assetId, String addressType) {
"properties", Map.of(
"name", "transfer-test",
"baseUrl", backendService + "/api/provider/data",
"type", addressType
"type", addressType,
"proxyQueryParams", "true"
)
)
);
Expand Down Expand Up @@ -170,8 +173,9 @@ public String getContractAgreementId(String negotiationId) {
return contractAgreementId.get();
}

public String dataRequest(String contractAgreementId, String assetId, Participant provider, DataAddress dataAddress) {
public String dataRequest(String id, String contractAgreementId, String assetId, Participant provider, DataAddress dataAddress) {
var request = Map.of(
"id", id,
"contractId", contractAgreementId,
"assetId", assetId,
"connectorId", "provider",
Expand Down Expand Up @@ -207,6 +211,37 @@ public String getTransferProcessState(String transferProcessId) {
.extract().body().jsonPath().getString("state");
}

public EndpointDataReference getDataReference(String id) {
var dataReference = new AtomicReference<EndpointDataReference>();

await().atMost(timeout).untilAsserted(() -> {
var result = given()
.baseUri(backendService.toString())
.when()
.get("/api/consumer/dataReference/{id}", id)
.then()
.statusCode(200)
.extract()
.body()
.as(EndpointDataReference.class);
dataReference.set(result);
});

return dataReference.get();
}

public void pullData(EndpointDataReference edr, Map<String, String> queryParams, Matcher<String> bodyMatcher) {
given()
.baseUri(edr.getEndpoint())
.header(edr.getAuthKey(), edr.getAuthCode())
.queryParams(queryParams)
.when()
.get()
.then()
.statusCode(200)
.body("message", bodyMatcher);
}

public URI backendService() {
return backendService;
}
Expand Down Expand Up @@ -264,7 +299,7 @@ public Map<String, String> controlPlaneConfiguration() {
put("edc.keystore", resourceAbsolutePath("certs/cert.pfx"));
put("edc.keystore.password", "123456");
put("ids.webhook.address", idsEndpoint.toString());
put("edc.receiver.http.endpoint", backendService + "/api/consumer/pull");
put("edc.receiver.http.endpoint", backendService + "/api/consumer/dataReference");
put("edc.transfer.proxy.token.signer.privatekey.alias", "1");
put("edc.transfer.proxy.token.verifier.publickey.alias", "public-key");
put("edc.transfer.proxy.endpoint", dataPlanePublic.toString());
Expand Down