diff --git a/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/defaults/DefaultCredentialServiceClient.java b/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/defaults/DefaultCredentialServiceClient.java index 8a5e16cad6d..2a59c04f7e6 100644 --- a/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/defaults/DefaultCredentialServiceClient.java +++ b/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/defaults/DefaultCredentialServiceClient.java @@ -26,8 +26,8 @@ import org.eclipse.edc.identitytrust.model.CredentialFormat; import org.eclipse.edc.identitytrust.model.VerifiablePresentation; import org.eclipse.edc.identitytrust.model.VerifiablePresentationContainer; -import org.eclipse.edc.identitytrust.model.credentialservice.PresentationQuery; -import org.eclipse.edc.identitytrust.model.credentialservice.PresentationResponse; +import org.eclipse.edc.identitytrust.model.credentialservice.PresentationQueryMessage; +import org.eclipse.edc.identitytrust.model.credentialservice.PresentationResponseMessage; import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.jsonld.spi.JsonLdKeywords; import org.eclipse.edc.spi.http.EdcHttpClient; @@ -40,7 +40,6 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java.util.stream.Stream; import static org.eclipse.edc.spi.result.Result.failure; import static org.eclipse.edc.spi.result.Result.success; @@ -85,7 +84,7 @@ public Result> requestPresentation(String } if (response.isSuccessful() && response.body() != null) { - var presentationResponse = objectMapper.readValue(body, PresentationResponse.class); + var presentationResponse = objectMapper.readValue(body, JsonObject.class); return parseResponse(presentationResponse); } return failure("Presentation Query failed: HTTP %s, message: %s".formatted(response.code(), body)); @@ -97,8 +96,16 @@ public Result> requestPresentation(String } - private Result> parseResponse(PresentationResponse presentationResponse) throws IOException { - var vpResults = Stream.of(presentationResponse.vpToken()) + private Result> parseResponse(JsonObject presentationResponseMessage) throws IOException { + + var presentationResponse = jsonLd.expand(presentationResponseMessage) + .compose((expanded) -> transformerRegistry.transform(expanded, PresentationResponseMessage.class)); + + if (presentationResponse.failed()) { + return failure("Failed to deserialize presentation response. Details: %s".formatted(presentationResponse.getFailureDetail())); + } + + var vpResults = presentationResponse.getContent().getPresentation().stream() .map(this::parseVpToken) .toList(); @@ -141,7 +148,7 @@ private JsonObject createPresentationQuery(List scopes) { .add(JsonLdKeywords.CONTEXT, jsonFactory.createArrayBuilder() .add(VcConstants.PRESENTATION_EXCHANGE_URL) .add(VcConstants.IATP_CONTEXT_URL)) - .add(JsonLdKeywords.TYPE, PresentationQuery.PRESENTATION_QUERY_TYPE_PROPERTY) + .add(JsonLdKeywords.TYPE, PresentationQueryMessage.PRESENTATION_QUERY_MESSAGE_TYPE_PROPERTY) .add("scope", scopeArray.build()) .build(); } diff --git a/extensions/common/iam/identity-trust/identity-trust-core/src/test/java/org/eclipse/edc/iam/identitytrust/core/defaults/DefaultCredentialServiceClientTest.java b/extensions/common/iam/identity-trust/identity-trust-core/src/test/java/org/eclipse/edc/iam/identitytrust/core/defaults/DefaultCredentialServiceClientTest.java index 35f9e253d67..753e0a564ad 100644 --- a/extensions/common/iam/identity-trust/identity-trust-core/src/test/java/org/eclipse/edc/iam/identitytrust/core/defaults/DefaultCredentialServiceClientTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-core/src/test/java/org/eclipse/edc/iam/identitytrust/core/defaults/DefaultCredentialServiceClientTest.java @@ -14,7 +14,11 @@ package org.eclipse.edc.iam.identitytrust.core.defaults; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.json.Json; +import jakarta.json.JsonObject; import okhttp3.MediaType; import okhttp3.Protocol; import okhttp3.Response; @@ -24,14 +28,18 @@ import org.eclipse.edc.identitytrust.model.Issuer; import org.eclipse.edc.identitytrust.model.VerifiableCredential; import org.eclipse.edc.identitytrust.model.VerifiablePresentation; +import org.eclipse.edc.identitytrust.model.credentialservice.PresentationResponseMessage; import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.jsonld.util.JacksonJsonLd; import org.eclipse.edc.spi.http.EdcHttpClient; +import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.invocation.InvocationOnMock; import java.io.IOException; import java.time.Instant; @@ -45,6 +53,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -55,20 +64,27 @@ class DefaultCredentialServiceClientTest { private final EdcHttpClient httpClientMock = mock(); private DefaultCredentialServiceClient client; + private TypeTransformerRegistry transformerRegistry; + + private ObjectMapper mapper = JacksonJsonLd.createObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + @BeforeEach void setup() { - var registry = mock(TypeTransformerRegistry.class); - when(registry.transform(any(), eq(VerifiablePresentation.class))) + transformerRegistry = mock(TypeTransformerRegistry.class); + when(transformerRegistry.transform(any(), eq(VerifiablePresentation.class))) .thenReturn(success(createPresentation())); + when(transformerRegistry.transform(isA(JsonObject.class), eq(PresentationResponseMessage.class))).thenAnswer(this::presentationResponse); + var jsonLdMock = mock(JsonLd.class); when(jsonLdMock.expand(any())).thenAnswer(a -> success(a.getArgument(0))); client = new DefaultCredentialServiceClient(httpClientMock, Json.createBuilderFactory(Map.of()), - createObjectMapper(), registry, jsonLdMock, mock()); + createObjectMapper(), transformerRegistry, jsonLdMock, mock()); } @Test @DisplayName("CS returns a single LDP-VP") void requestPresentation_singleLdpVp() throws IOException { + when(httpClientMock.execute(any())) .thenReturn(response(200, getResourceFileContentAsString("single_ldp-vp.json"))); @@ -146,7 +162,7 @@ void requestPresentation_csReturnsError(int httpCode) throws IOException { @Test void requestPresentation_emptyArray() throws IOException { when(httpClientMock.execute(any())) - .thenReturn(response(200, "{\"vp_token\":[],\"presentation_submission\":null}")); + .thenReturn(response(200, "{\"presentation\":[],\"presentationSubmission\":null}")); var res = client.requestPresentation(CS_URL, "foo", List.of()); assertThat(res.succeeded()).isTrue(); @@ -169,6 +185,25 @@ private VerifiablePresentation createPresentation() { .build(); } + private Result presentationResponseResult(String path) { + var content = getResourceFileContentAsString(path); + try { + var response = mapper.readValue(content, PresentationResponseMessage.class); + return Result.success(response); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + private Result presentationResponse(InvocationOnMock args) { + try { + var response = mapper.readValue(args.getArgument(0, JsonObject.class).toString(), PresentationResponseMessage.class); + return Result.success(response); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + private Response response(int code, String body) { return new Response.Builder() .request(mock()) diff --git a/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_jwt.json b/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_jwt.json index c43f91cd7fc..8c0689c5929 100644 --- a/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_jwt.json +++ b/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_jwt.json @@ -1,9 +1,9 @@ { - "vp_token": [ + "presentation": [ "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDpleGFtcGxlOjB4YWJjI2tleTEifQ.eyJpc3MiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJqdGkiOiJ1cm46dXVpZDozOTc4MzQ0Zi04NTk2LTRjM2EtYTk3OC04ZmNhYmEzOTAzYzUiLCJhdWQiOiJkaWQ6ZXhhbXBsZTo0YTU3NTQ2OTczNDM2ZjZmNmM0YTRhNTc1NzMiLCJuYmYiOjE1NDE0OTM3MjQsImlhdCI6MTU0MTQ5MzcyNCwiZXhwIjoxNTczMDI5NzIzLCJub25jZSI6IjM0M3MkRlNGRGEtIiwidnAiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy9leGFtcGxlcy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iLCJDcmVkZW50aWFsTWFuYWdlclByZXNlbnRhdGlvbiJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6WyJleUpoYkdjaU9pSlNVekkxTmlJc0luUjVjQ0k2SWtwWFZDSXNJbXRwWkNJNkltUnBaRHBsZUdGdGNHeGxPbUZpWm1VeE0yWTNNVEl4TWpBME16RmpNamMyWlRFeVpXTmhZaU5yWlhsekxURWlmUS5leUp6ZFdJaU9pSmthV1E2WlhoaGJYQnNaVHBsWW1abFlqRm1OekV5WldKak5tWXhZekkzTm1VeE1tVmpNakVpTENKcWRHa2lPaUpvZEhSd09pOHZaWGhoYlhCc1pTNWxaSFV2WTNKbFpHVnVkR2xoYkhNdk16Y3pNaUlzSW1semN5STZJbWgwZEhCek9pOHZaWGhoYlhCc1pTNWpiMjB2YTJWNWN5OW1iMjh1YW5kcklpd2libUptSWpveE5UUXhORGt6TnpJMExDSnBZWFFpT2pFMU5ERTBPVE0zTWpRc0ltVjRjQ0k2TVRVM016QXlPVGN5TXl3aWJtOXVZMlVpT2lJMk5qQWhOak0wTlVaVFpYSWlMQ0oyWXlJNmV5SkFZMjl1ZEdWNGRDSTZXeUpvZEhSd2N6b3ZMM2QzZHk1M015NXZjbWN2TWpBeE9DOWpjbVZrWlc1MGFXRnNjeTkyTVNJc0ltaDBkSEJ6T2k4dmQzZDNMbmN6TG05eVp5OHlNREU0TDJOeVpXUmxiblJwWVd4ekwyVjRZVzF3YkdWekwzWXhJbDBzSW5SNWNHVWlPbHNpVm1WeWFXWnBZV0pzWlVOeVpXUmxiblJwWVd3aUxDSlZibWwyWlhKemFYUjVSR1ZuY21WbFEzSmxaR1Z1ZEdsaGJDSmRMQ0pqY21Wa1pXNTBhV0ZzVTNWaWFtVmpkQ0k2ZXlKa1pXZHlaV1VpT25zaWRIbHdaU0k2SWtKaFkyaGxiRzl5UkdWbmNtVmxJaXdpYm1GdFpTSTZJanh6Y0dGdUlHeGhibWM5SjJaeUxVTkJKejVDWVdOallXeGhkWExEcVdGMElHVnVJRzExYzJseGRXVnpJRzUxYmNPcGNtbHhkV1Z6UEM5emNHRnVQaUo5ZlgxOS5LTEpvNUdBeUJORDNMRFRuOUg3RlFva0VzVUVpOGpLd1hoR3ZvTjNKdFJhNTF4ck5EZ1hEYjBjcTFVVFlCLXJLNEZ0OVlWbVIxTklfWk9GOG9HY183d0FwOFBIYkYySGFXb2RRSW9PQnh4VC00V05xQXhmdDdFVDZsa0gtNFM2VXgzclNHQW1jek1vaEVFZjhlQ2VOLWpDOFdla2RQbDZ6S1pRajBZUEIxcng2WDAteGxGQnM3Y2w2V3Q4cmZCUF90WjlZZ1ZXclFtVVd5cFNpb2MwTVV5aXBobXlFYkxaYWdUeVBsVXlmbEdsRWRxclpBdjZlU2U2UnR4Snk2TTEtbEQ3YTVIVHphbllUV0JQQVVIRFpHeUdLWGRKdy1XX3gwSVdDaEJ6STh0M2twRzI1M2ZnNlYzdFBnSGVLWEU5NGZ6X1FwWWZnLS03a0xzeUJBZlFHYmciXX19.ft_Eq4IniBrr7gtzRfrYj8Vy1aPXuFZU-6_ai0wvaKcsrzI4JkQEKTvbJwdvIeuGuTqy7ipO-EYi7V4TvonPuTRdpB7ZHOlYlbZ4wA9WJ6mSVSqDACvYRiFvrOFmie8rgm6GacWatgO4m4NqiFKFko3r58LueFfGw47NK9RcfOkVQeHCq4btaDqksDKeoTrNysF4YS89INa-prWomrLRAhnwLOo1Etp3E4ESAxg73CR2kA5AoMbf5KtFueWnMcSbQkMRdWcGC1VssC0tB0JffVjq7ZV6OTyV4kl1-UVgiPLXUTpupFfLRhf9QpqMBjYgP62KvhIvW8BbkGUelYMetA", "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDpleGFtcGxlOjB4YWJjI2tleTEifQ.eyJpc3MiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJqdGkiOiJ1cm46dXVpZDozOTc4MzQ0Zi04NTk2LTRjM2EtYTk3OC04ZmNhYmEzOTAzYzUiLCJhdWQiOiJkaWQ6ZXhhbXBsZTo0YTU3NTQ2OTczNDM2ZjZmNmM0YTRhNTc1NzMiLCJuYmYiOjE1NDE0OTM3MjQsImlhdCI6MTU0MTQ5MzcyNCwiZXhwIjoxNTczMDI5NzIzLCJub25jZSI6IjM0M3MkRlNGRGEtIiwidnAiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy9leGFtcGxlcy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iLCJDcmVkZW50aWFsTWFuYWdlclByZXNlbnRhdGlvbiJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6WyJleUpoYkdjaU9pSlNVekkxTmlJc0luUjVjQ0k2SWtwWFZDSXNJbXRwWkNJNkltUnBaRHBsZUdGdGNHeGxPbUZpWm1VeE0yWTNNVEl4TWpBME16RmpNamMyWlRFeVpXTmhZaU5yWlhsekxURWlmUS5leUp6ZFdJaU9pSmthV1E2WlhoaGJYQnNaVHBsWW1abFlqRm1OekV5WldKak5tWXhZekkzTm1VeE1tVmpNakVpTENKcWRHa2lPaUpvZEhSd09pOHZaWGhoYlhCc1pTNWxaSFV2WTNKbFpHVnVkR2xoYkhNdk16Y3pNaUlzSW1semN5STZJbWgwZEhCek9pOHZaWGhoYlhCc1pTNWpiMjB2YTJWNWN5OW1iMjh1YW5kcklpd2libUptSWpveE5UUXhORGt6TnpJMExDSnBZWFFpT2pFMU5ERTBPVE0zTWpRc0ltVjRjQ0k2TVRVM016QXlPVGN5TXl3aWJtOXVZMlVpT2lJMk5qQWhOak0wTlVaVFpYSWlMQ0oyWXlJNmV5SkFZMjl1ZEdWNGRDSTZXeUpvZEhSd2N6b3ZMM2QzZHk1M015NXZjbWN2TWpBeE9DOWpjbVZrWlc1MGFXRnNjeTkyTVNJc0ltaDBkSEJ6T2k4dmQzZDNMbmN6TG05eVp5OHlNREU0TDJOeVpXUmxiblJwWVd4ekwyVjRZVzF3YkdWekwzWXhJbDBzSW5SNWNHVWlPbHNpVm1WeWFXWnBZV0pzWlVOeVpXUmxiblJwWVd3aUxDSlZibWwyWlhKemFYUjVSR1ZuY21WbFEzSmxaR1Z1ZEdsaGJDSmRMQ0pqY21Wa1pXNTBhV0ZzVTNWaWFtVmpkQ0k2ZXlKa1pXZHlaV1VpT25zaWRIbHdaU0k2SWtKaFkyaGxiRzl5UkdWbmNtVmxJaXdpYm1GdFpTSTZJanh6Y0dGdUlHeGhibWM5SjJaeUxVTkJKejVDWVdOallXeGhkWExEcVdGMElHVnVJRzExYzJseGRXVnpJRzUxYmNPcGNtbHhkV1Z6UEM5emNHRnVQaUo5ZlgxOS5LTEpvNUdBeUJORDNMRFRuOUg3RlFva0VzVUVpOGpLd1hoR3ZvTjNKdFJhNTF4ck5EZ1hEYjBjcTFVVFlCLXJLNEZ0OVlWbVIxTklfWk9GOG9HY183d0FwOFBIYkYySGFXb2RRSW9PQnh4VC00V05xQXhmdDdFVDZsa0gtNFM2VXgzclNHQW1jek1vaEVFZjhlQ2VOLWpDOFdla2RQbDZ6S1pRajBZUEIxcng2WDAteGxGQnM3Y2w2V3Q4cmZCUF90WjlZZ1ZXclFtVVd5cFNpb2MwTVV5aXBobXlFYkxaYWdUeVBsVXlmbEdsRWRxclpBdjZlU2U2UnR4Snk2TTEtbEQ3YTVIVHphbllUV0JQQVVIRFpHeUdLWGRKdy1XX3gwSVdDaEJ6STh0M2twRzI1M2ZnNlYzdFBnSGVLWEU5NGZ6X1FwWWZnLS03a0xzeUJBZlFHYmciXX19.ft_Eq4IniBrr7gtzRfrYj8Vy1aPXuFZU-6_ai0wvaKcsrzI4JkQEKTvbJwdvIeuGuTqy7ipO-EYi7V4TvonPuTRdpB7ZHOlYlbZ4wA9WJ6mSVSqDACvYRiFvrOFmie8rgm6GacWatgO4m4NqiFKFko3r58LueFfGw47NK9RcfOkVQeHCq4btaDqksDKeoTrNysF4YS89INa-prWomrLRAhnwLOo1Etp3E4ESAxg73CR2kA5AoMbf5KtFueWnMcSbQkMRdWcGC1VssC0tB0JffVjq7ZV6OTyV4kl1-UVgiPLXUTpupFfLRhf9QpqMBjYgP62KvhIvW8BbkGUelYMetA" ], - "presentation_submission": { + "presentationSubmission": { "id": "Presentation example 2", "definition_id": "Example with multiple VPs", "descriptor_map": [ diff --git a/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_ldp.json b/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_ldp.json index c37b14bdbed..d64c469dd89 100644 --- a/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_ldp.json +++ b/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_ldp.json @@ -1,5 +1,5 @@ { - "vp_token": [ + "presentation": [ { "@context": [ "https://www.w3.org/2018/credentials/v1" @@ -97,7 +97,7 @@ } } ], - "presentation_submission": { + "presentationSubmission": { "id": "Presentation example 2", "definition_id": "Example with multiple VPs", "descriptor_map": [ diff --git a/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_mixed.json b/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_mixed.json index 318b6348b6b..17d3df96781 100644 --- a/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_mixed.json +++ b/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/multiple_vp-token_mixed.json @@ -1,5 +1,5 @@ { - "vp_token": [ + "presentation": [ { "@context": [ "https://www.w3.org/2018/credentials/v1" @@ -50,7 +50,7 @@ }, "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDpleGFtcGxlOjB4YWJjI2tleTEifQ.eyJpc3MiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJqdGkiOiJ1cm46dXVpZDozOTc4MzQ0Zi04NTk2LTRjM2EtYTk3OC04ZmNhYmEzOTAzYzUiLCJhdWQiOiJkaWQ6ZXhhbXBsZTo0YTU3NTQ2OTczNDM2ZjZmNmM0YTRhNTc1NzMiLCJuYmYiOjE1NDE0OTM3MjQsImlhdCI6MTU0MTQ5MzcyNCwiZXhwIjoxNTczMDI5NzIzLCJub25jZSI6IjM0M3MkRlNGRGEtIiwidnAiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy9leGFtcGxlcy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iLCJDcmVkZW50aWFsTWFuYWdlclByZXNlbnRhdGlvbiJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6WyJleUpoYkdjaU9pSlNVekkxTmlJc0luUjVjQ0k2SWtwWFZDSXNJbXRwWkNJNkltUnBaRHBsZUdGdGNHeGxPbUZpWm1VeE0yWTNNVEl4TWpBME16RmpNamMyWlRFeVpXTmhZaU5yWlhsekxURWlmUS5leUp6ZFdJaU9pSmthV1E2WlhoaGJYQnNaVHBsWW1abFlqRm1OekV5WldKak5tWXhZekkzTm1VeE1tVmpNakVpTENKcWRHa2lPaUpvZEhSd09pOHZaWGhoYlhCc1pTNWxaSFV2WTNKbFpHVnVkR2xoYkhNdk16Y3pNaUlzSW1semN5STZJbWgwZEhCek9pOHZaWGhoYlhCc1pTNWpiMjB2YTJWNWN5OW1iMjh1YW5kcklpd2libUptSWpveE5UUXhORGt6TnpJMExDSnBZWFFpT2pFMU5ERTBPVE0zTWpRc0ltVjRjQ0k2TVRVM016QXlPVGN5TXl3aWJtOXVZMlVpT2lJMk5qQWhOak0wTlVaVFpYSWlMQ0oyWXlJNmV5SkFZMjl1ZEdWNGRDSTZXeUpvZEhSd2N6b3ZMM2QzZHk1M015NXZjbWN2TWpBeE9DOWpjbVZrWlc1MGFXRnNjeTkyTVNJc0ltaDBkSEJ6T2k4dmQzZDNMbmN6TG05eVp5OHlNREU0TDJOeVpXUmxiblJwWVd4ekwyVjRZVzF3YkdWekwzWXhJbDBzSW5SNWNHVWlPbHNpVm1WeWFXWnBZV0pzWlVOeVpXUmxiblJwWVd3aUxDSlZibWwyWlhKemFYUjVSR1ZuY21WbFEzSmxaR1Z1ZEdsaGJDSmRMQ0pqY21Wa1pXNTBhV0ZzVTNWaWFtVmpkQ0k2ZXlKa1pXZHlaV1VpT25zaWRIbHdaU0k2SWtKaFkyaGxiRzl5UkdWbmNtVmxJaXdpYm1GdFpTSTZJanh6Y0dGdUlHeGhibWM5SjJaeUxVTkJKejVDWVdOallXeGhkWExEcVdGMElHVnVJRzExYzJseGRXVnpJRzUxYmNPcGNtbHhkV1Z6UEM5emNHRnVQaUo5ZlgxOS5LTEpvNUdBeUJORDNMRFRuOUg3RlFva0VzVUVpOGpLd1hoR3ZvTjNKdFJhNTF4ck5EZ1hEYjBjcTFVVFlCLXJLNEZ0OVlWbVIxTklfWk9GOG9HY183d0FwOFBIYkYySGFXb2RRSW9PQnh4VC00V05xQXhmdDdFVDZsa0gtNFM2VXgzclNHQW1jek1vaEVFZjhlQ2VOLWpDOFdla2RQbDZ6S1pRajBZUEIxcng2WDAteGxGQnM3Y2w2V3Q4cmZCUF90WjlZZ1ZXclFtVVd5cFNpb2MwTVV5aXBobXlFYkxaYWdUeVBsVXlmbEdsRWRxclpBdjZlU2U2UnR4Snk2TTEtbEQ3YTVIVHphbllUV0JQQVVIRFpHeUdLWGRKdy1XX3gwSVdDaEJ6STh0M2twRzI1M2ZnNlYzdFBnSGVLWEU5NGZ6X1FwWWZnLS03a0xzeUJBZlFHYmciXX19.ft_Eq4IniBrr7gtzRfrYj8Vy1aPXuFZU-6_ai0wvaKcsrzI4JkQEKTvbJwdvIeuGuTqy7ipO-EYi7V4TvonPuTRdpB7ZHOlYlbZ4wA9WJ6mSVSqDACvYRiFvrOFmie8rgm6GacWatgO4m4NqiFKFko3r58LueFfGw47NK9RcfOkVQeHCq4btaDqksDKeoTrNysF4YS89INa-prWomrLRAhnwLOo1Etp3E4ESAxg73CR2kA5AoMbf5KtFueWnMcSbQkMRdWcGC1VssC0tB0JffVjq7ZV6OTyV4kl1-UVgiPLXUTpupFfLRhf9QpqMBjYgP62KvhIvW8BbkGUelYMetA" ], - "presentation_submission": { + "presentationSubmission": { "id": "Presentation example 2", "definition_id": "Example with multiple VPs", "descriptor_map": [ diff --git a/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/single_jwt-vp.json b/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/single_jwt-vp.json index 2b8ab33f259..33cc5d0dd5e 100644 --- a/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/single_jwt-vp.json +++ b/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/single_jwt-vp.json @@ -1,6 +1,6 @@ { - "vp_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDpleGFtcGxlOjB4YWJjI2tleTEifQ.eyJpc3MiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJqdGkiOiJ1cm46dXVpZDozOTc4MzQ0Zi04NTk2LTRjM2EtYTk3OC04ZmNhYmEzOTAzYzUiLCJhdWQiOiJkaWQ6ZXhhbXBsZTo0YTU3NTQ2OTczNDM2ZjZmNmM0YTRhNTc1NzMiLCJuYmYiOjE1NDE0OTM3MjQsImlhdCI6MTU0MTQ5MzcyNCwiZXhwIjoxNTczMDI5NzIzLCJub25jZSI6IjM0M3MkRlNGRGEtIiwidnAiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy9leGFtcGxlcy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iLCJDcmVkZW50aWFsTWFuYWdlclByZXNlbnRhdGlvbiJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6WyJleUpoYkdjaU9pSlNVekkxTmlJc0luUjVjQ0k2SWtwWFZDSXNJbXRwWkNJNkltUnBaRHBsZUdGdGNHeGxPbUZpWm1VeE0yWTNNVEl4TWpBME16RmpNamMyWlRFeVpXTmhZaU5yWlhsekxURWlmUS5leUp6ZFdJaU9pSmthV1E2WlhoaGJYQnNaVHBsWW1abFlqRm1OekV5WldKak5tWXhZekkzTm1VeE1tVmpNakVpTENKcWRHa2lPaUpvZEhSd09pOHZaWGhoYlhCc1pTNWxaSFV2WTNKbFpHVnVkR2xoYkhNdk16Y3pNaUlzSW1semN5STZJbWgwZEhCek9pOHZaWGhoYlhCc1pTNWpiMjB2YTJWNWN5OW1iMjh1YW5kcklpd2libUptSWpveE5UUXhORGt6TnpJMExDSnBZWFFpT2pFMU5ERTBPVE0zTWpRc0ltVjRjQ0k2TVRVM016QXlPVGN5TXl3aWJtOXVZMlVpT2lJMk5qQWhOak0wTlVaVFpYSWlMQ0oyWXlJNmV5SkFZMjl1ZEdWNGRDSTZXeUpvZEhSd2N6b3ZMM2QzZHk1M015NXZjbWN2TWpBeE9DOWpjbVZrWlc1MGFXRnNjeTkyTVNJc0ltaDBkSEJ6T2k4dmQzZDNMbmN6TG05eVp5OHlNREU0TDJOeVpXUmxiblJwWVd4ekwyVjRZVzF3YkdWekwzWXhJbDBzSW5SNWNHVWlPbHNpVm1WeWFXWnBZV0pzWlVOeVpXUmxiblJwWVd3aUxDSlZibWwyWlhKemFYUjVSR1ZuY21WbFEzSmxaR1Z1ZEdsaGJDSmRMQ0pqY21Wa1pXNTBhV0ZzVTNWaWFtVmpkQ0k2ZXlKa1pXZHlaV1VpT25zaWRIbHdaU0k2SWtKaFkyaGxiRzl5UkdWbmNtVmxJaXdpYm1GdFpTSTZJanh6Y0dGdUlHeGhibWM5SjJaeUxVTkJKejVDWVdOallXeGhkWExEcVdGMElHVnVJRzExYzJseGRXVnpJRzUxYmNPcGNtbHhkV1Z6UEM5emNHRnVQaUo5ZlgxOS5LTEpvNUdBeUJORDNMRFRuOUg3RlFva0VzVUVpOGpLd1hoR3ZvTjNKdFJhNTF4ck5EZ1hEYjBjcTFVVFlCLXJLNEZ0OVlWbVIxTklfWk9GOG9HY183d0FwOFBIYkYySGFXb2RRSW9PQnh4VC00V05xQXhmdDdFVDZsa0gtNFM2VXgzclNHQW1jek1vaEVFZjhlQ2VOLWpDOFdla2RQbDZ6S1pRajBZUEIxcng2WDAteGxGQnM3Y2w2V3Q4cmZCUF90WjlZZ1ZXclFtVVd5cFNpb2MwTVV5aXBobXlFYkxaYWdUeVBsVXlmbEdsRWRxclpBdjZlU2U2UnR4Snk2TTEtbEQ3YTVIVHphbllUV0JQQVVIRFpHeUdLWGRKdy1XX3gwSVdDaEJ6STh0M2twRzI1M2ZnNlYzdFBnSGVLWEU5NGZ6X1FwWWZnLS03a0xzeUJBZlFHYmciXX19.ft_Eq4IniBrr7gtzRfrYj8Vy1aPXuFZU-6_ai0wvaKcsrzI4JkQEKTvbJwdvIeuGuTqy7ipO-EYi7V4TvonPuTRdpB7ZHOlYlbZ4wA9WJ6mSVSqDACvYRiFvrOFmie8rgm6GacWatgO4m4NqiFKFko3r58LueFfGw47NK9RcfOkVQeHCq4btaDqksDKeoTrNysF4YS89INa-prWomrLRAhnwLOo1Etp3E4ESAxg73CR2kA5AoMbf5KtFueWnMcSbQkMRdWcGC1VssC0tB0JffVjq7ZV6OTyV4kl1-UVgiPLXUTpupFfLRhf9QpqMBjYgP62KvhIvW8BbkGUelYMetA", - "presentation_submission": { + "presentation": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDpleGFtcGxlOjB4YWJjI2tleTEifQ.eyJpc3MiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJqdGkiOiJ1cm46dXVpZDozOTc4MzQ0Zi04NTk2LTRjM2EtYTk3OC04ZmNhYmEzOTAzYzUiLCJhdWQiOiJkaWQ6ZXhhbXBsZTo0YTU3NTQ2OTczNDM2ZjZmNmM0YTRhNTc1NzMiLCJuYmYiOjE1NDE0OTM3MjQsImlhdCI6MTU0MTQ5MzcyNCwiZXhwIjoxNTczMDI5NzIzLCJub25jZSI6IjM0M3MkRlNGRGEtIiwidnAiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy9leGFtcGxlcy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iLCJDcmVkZW50aWFsTWFuYWdlclByZXNlbnRhdGlvbiJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6WyJleUpoYkdjaU9pSlNVekkxTmlJc0luUjVjQ0k2SWtwWFZDSXNJbXRwWkNJNkltUnBaRHBsZUdGdGNHeGxPbUZpWm1VeE0yWTNNVEl4TWpBME16RmpNamMyWlRFeVpXTmhZaU5yWlhsekxURWlmUS5leUp6ZFdJaU9pSmthV1E2WlhoaGJYQnNaVHBsWW1abFlqRm1OekV5WldKak5tWXhZekkzTm1VeE1tVmpNakVpTENKcWRHa2lPaUpvZEhSd09pOHZaWGhoYlhCc1pTNWxaSFV2WTNKbFpHVnVkR2xoYkhNdk16Y3pNaUlzSW1semN5STZJbWgwZEhCek9pOHZaWGhoYlhCc1pTNWpiMjB2YTJWNWN5OW1iMjh1YW5kcklpd2libUptSWpveE5UUXhORGt6TnpJMExDSnBZWFFpT2pFMU5ERTBPVE0zTWpRc0ltVjRjQ0k2TVRVM016QXlPVGN5TXl3aWJtOXVZMlVpT2lJMk5qQWhOak0wTlVaVFpYSWlMQ0oyWXlJNmV5SkFZMjl1ZEdWNGRDSTZXeUpvZEhSd2N6b3ZMM2QzZHk1M015NXZjbWN2TWpBeE9DOWpjbVZrWlc1MGFXRnNjeTkyTVNJc0ltaDBkSEJ6T2k4dmQzZDNMbmN6TG05eVp5OHlNREU0TDJOeVpXUmxiblJwWVd4ekwyVjRZVzF3YkdWekwzWXhJbDBzSW5SNWNHVWlPbHNpVm1WeWFXWnBZV0pzWlVOeVpXUmxiblJwWVd3aUxDSlZibWwyWlhKemFYUjVSR1ZuY21WbFEzSmxaR1Z1ZEdsaGJDSmRMQ0pqY21Wa1pXNTBhV0ZzVTNWaWFtVmpkQ0k2ZXlKa1pXZHlaV1VpT25zaWRIbHdaU0k2SWtKaFkyaGxiRzl5UkdWbmNtVmxJaXdpYm1GdFpTSTZJanh6Y0dGdUlHeGhibWM5SjJaeUxVTkJKejVDWVdOallXeGhkWExEcVdGMElHVnVJRzExYzJseGRXVnpJRzUxYmNPcGNtbHhkV1Z6UEM5emNHRnVQaUo5ZlgxOS5LTEpvNUdBeUJORDNMRFRuOUg3RlFva0VzVUVpOGpLd1hoR3ZvTjNKdFJhNTF4ck5EZ1hEYjBjcTFVVFlCLXJLNEZ0OVlWbVIxTklfWk9GOG9HY183d0FwOFBIYkYySGFXb2RRSW9PQnh4VC00V05xQXhmdDdFVDZsa0gtNFM2VXgzclNHQW1jek1vaEVFZjhlQ2VOLWpDOFdla2RQbDZ6S1pRajBZUEIxcng2WDAteGxGQnM3Y2w2V3Q4cmZCUF90WjlZZ1ZXclFtVVd5cFNpb2MwTVV5aXBobXlFYkxaYWdUeVBsVXlmbEdsRWRxclpBdjZlU2U2UnR4Snk2TTEtbEQ3YTVIVHphbllUV0JQQVVIRFpHeUdLWGRKdy1XX3gwSVdDaEJ6STh0M2twRzI1M2ZnNlYzdFBnSGVLWEU5NGZ6X1FwWWZnLS03a0xzeUJBZlFHYmciXX19.ft_Eq4IniBrr7gtzRfrYj8Vy1aPXuFZU-6_ai0wvaKcsrzI4JkQEKTvbJwdvIeuGuTqy7ipO-EYi7V4TvonPuTRdpB7ZHOlYlbZ4wA9WJ6mSVSqDACvYRiFvrOFmie8rgm6GacWatgO4m4NqiFKFko3r58LueFfGw47NK9RcfOkVQeHCq4btaDqksDKeoTrNysF4YS89INa-prWomrLRAhnwLOo1Etp3E4ESAxg73CR2kA5AoMbf5KtFueWnMcSbQkMRdWcGC1VssC0tB0JffVjq7ZV6OTyV4kl1-UVgiPLXUTpupFfLRhf9QpqMBjYgP62KvhIvW8BbkGUelYMetA", + "presentationSubmission": { "id": "Presentation example 1", "definition_id": "Example with selective disclosure", "descriptor_map": [ diff --git a/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/single_ldp-vp.json b/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/single_ldp-vp.json index 8fa2b31f652..c175a029d1c 100644 --- a/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/single_ldp-vp.json +++ b/extensions/common/iam/identity-trust/identity-trust-core/src/test/resources/single_ldp-vp.json @@ -1,5 +1,5 @@ { - "vp_token": { + "presentation": { "@context": [ "https://www.w3.org/2018/credentials/v1" ], @@ -47,7 +47,7 @@ "verificationMethod": "did:example:holder#key-1" } }, - "presentation_submission": { + "presentationSubmission": { "id": "Presentation example 1", "definition_id": "Example with selective disclosure", "descriptor_map": [ diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityTrustTransformExtension.java b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityTrustTransformExtension.java index 9032a6fee11..1457dfd48cc 100644 --- a/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityTrustTransformExtension.java +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityTrustTransformExtension.java @@ -15,10 +15,12 @@ package org.eclipse.edc.iam.identitytrust; import org.eclipse.edc.iam.identitytrust.transform.from.JsonObjectFromPresentationQueryTransformer; +import org.eclipse.edc.iam.identitytrust.transform.from.JsonObjectFromPresentationResponseMessageTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToCredentialStatusTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToCredentialSubjectTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToIssuerTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToPresentationQueryTransformer; +import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToPresentationResponseMessageTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToVerifiableCredentialTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToVerifiablePresentationTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JwtToVerifiableCredentialTransformer; @@ -38,6 +40,7 @@ import java.net.URISyntaxException; import static java.lang.String.format; +import static org.eclipse.edc.identitytrust.VcConstants.IATP_CONTEXT_URL; import static org.eclipse.edc.spi.CoreConstants.JSON_LD; @Extension(value = IdentityTrustTransformExtension.NAME, categories = { "iam", "transform", "jsonld" }) @@ -62,8 +65,14 @@ public void initialize(ServiceExtensionContext context) { .onSuccess(uri -> jsonLdService.registerCachedDocument("https://www.w3.org/2018/credentials/v1", uri)) .onFailure(failure -> context.getMonitor().warning("Failed to register cached json-ld document: " + failure.getFailureDetail())); + getResourceUri("document" + File.separator + "iatp.v08.jsonld") + .onSuccess(uri -> jsonLdService.registerCachedDocument(IATP_CONTEXT_URL, uri)) + .onFailure(failure -> context.getMonitor().warning("Failed to register cached json-ld document: " + failure.getFailureDetail())); + typeTransformerRegistry.register(new JsonObjectToPresentationQueryTransformer(typeManager.getMapper(JSON_LD))); + typeTransformerRegistry.register(new JsonObjectToPresentationResponseMessageTransformer(typeManager.getMapper(JSON_LD))); typeTransformerRegistry.register(new JsonObjectFromPresentationQueryTransformer()); + typeTransformerRegistry.register(new JsonObjectFromPresentationResponseMessageTransformer()); typeTransformerRegistry.register(new JsonObjectToVerifiablePresentationTransformer()); typeTransformerRegistry.register(new JsonObjectToVerifiableCredentialTransformer()); typeTransformerRegistry.register(new JsonObjectToIssuerTransformer()); diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/from/JsonObjectFromPresentationQueryTransformer.java b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/from/JsonObjectFromPresentationQueryTransformer.java index 6ff7b2048eb..bc1d779b930 100644 --- a/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/from/JsonObjectFromPresentationQueryTransformer.java +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/from/JsonObjectFromPresentationQueryTransformer.java @@ -15,20 +15,20 @@ package org.eclipse.edc.iam.identitytrust.transform.from; import jakarta.json.JsonObject; -import org.eclipse.edc.identitytrust.model.credentialservice.PresentationQuery; +import org.eclipse.edc.identitytrust.model.credentialservice.PresentationQueryMessage; import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer; import org.eclipse.edc.transform.spi.TransformerContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class JsonObjectFromPresentationQueryTransformer extends AbstractJsonLdTransformer { +public class JsonObjectFromPresentationQueryTransformer extends AbstractJsonLdTransformer { public JsonObjectFromPresentationQueryTransformer() { - super(PresentationQuery.class, JsonObject.class); + super(PresentationQueryMessage.class, JsonObject.class); } @Override - public @Nullable JsonObject transform(@NotNull PresentationQuery presentationQuery, @NotNull TransformerContext context) { + public @Nullable JsonObject transform(@NotNull PresentationQueryMessage presentationQueryMessage, @NotNull TransformerContext context) { return null; } } diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/from/JsonObjectFromPresentationResponseMessageTransformer.java b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/from/JsonObjectFromPresentationResponseMessageTransformer.java new file mode 100644 index 00000000000..2c6275c14dc --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/from/JsonObjectFromPresentationResponseMessageTransformer.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.iam.identitytrust.transform.from; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import org.eclipse.edc.identitytrust.model.credentialservice.PresentationResponseMessage; +import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer; +import org.eclipse.edc.transform.spi.TransformerContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static org.eclipse.edc.identitytrust.model.credentialservice.PresentationResponseMessage.PRESENTATION_RESPONSE_MESSAGE_PRESENTATION_PROPERTY; +import static org.eclipse.edc.identitytrust.model.credentialservice.PresentationResponseMessage.PRESENTATION_RESPONSE_MESSAGE_TYPE_PROPERTY; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; + + +/** + * Transforms a {@link PresentationResponseMessage} into a {@link JsonObject} object. + */ +public class JsonObjectFromPresentationResponseMessageTransformer extends AbstractJsonLdTransformer { + public JsonObjectFromPresentationResponseMessageTransformer() { + super(PresentationResponseMessage.class, JsonObject.class); + } + + @Override + public @Nullable JsonObject transform(@NotNull PresentationResponseMessage presentationQueryMessage, @NotNull TransformerContext context) { + // Presentation Submission not supported yet + return Json.createObjectBuilder() + .add(TYPE, PRESENTATION_RESPONSE_MESSAGE_TYPE_PROPERTY) + .add(PRESENTATION_RESPONSE_MESSAGE_PRESENTATION_PROPERTY, Json.createArrayBuilder(presentationQueryMessage.getPresentation()).build()) + .build(); + } + +} diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryTransformer.java b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryTransformer.java index fcf12a95624..4a2d270e35e 100644 --- a/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryTransformer.java +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryTransformer.java @@ -19,7 +19,7 @@ import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.JsonValue; -import org.eclipse.edc.identitytrust.model.credentialservice.PresentationQuery; +import org.eclipse.edc.identitytrust.model.credentialservice.PresentationQueryMessage; import org.eclipse.edc.identitytrust.model.presentationdefinition.PresentationDefinition; import org.eclipse.edc.jsonld.spi.JsonLdKeywords; import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer; @@ -32,23 +32,23 @@ /** * Transforms a JsonObject into a PresentationQuery object. */ -public class JsonObjectToPresentationQueryTransformer extends AbstractJsonLdTransformer { +public class JsonObjectToPresentationQueryTransformer extends AbstractJsonLdTransformer { private final ObjectMapper mapper; public JsonObjectToPresentationQueryTransformer(ObjectMapper mapper) { - super(JsonObject.class, PresentationQuery.class); + super(JsonObject.class, PresentationQueryMessage.class); this.mapper = mapper; } @Override - public @Nullable PresentationQuery transform(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) { - var bldr = PresentationQuery.Builder.newinstance(); + public @Nullable PresentationQueryMessage transform(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) { + var bldr = PresentationQueryMessage.Builder.newinstance(); visitProperties(jsonObject, (k, v) -> { switch (k) { - case PresentationQuery.PRESENTATION_QUERY_DEFINITION_PROPERTY -> + case PresentationQueryMessage.PRESENTATION_QUERY_MESSAGE_DEFINITION_PROPERTY -> bldr.presentationDefinition(readPresentationDefinition(v, context)); - case PresentationQuery.PRESENTATION_QUERY_SCOPE_PROPERTY -> + case PresentationQueryMessage.PRESENTATION_QUERY_MESSAGE_SCOPE_PROPERTY -> transformArrayOrObject(v, Object.class, o -> bldr.scopes(List.of(o.toString().split(" "))), context); default -> context.reportProblem("Unknown property '%s'".formatted(k)); } diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationResponseMessageTransformer.java b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationResponseMessageTransformer.java new file mode 100644 index 00000000000..8a30162def8 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationResponseMessageTransformer.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.iam.identitytrust.transform.to; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; +import org.eclipse.edc.identitytrust.model.credentialservice.PresentationResponseMessage; +import org.eclipse.edc.identitytrust.model.credentialservice.PresentationSubmission; +import org.eclipse.edc.jsonld.spi.JsonLdKeywords; +import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer; +import org.eclipse.edc.transform.spi.TransformerContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Transforms a {@link JsonObject} into a {@link PresentationResponseMessage} object. + */ +public class JsonObjectToPresentationResponseMessageTransformer extends AbstractJsonLdTransformer { + + private final ObjectMapper mapper; + + public JsonObjectToPresentationResponseMessageTransformer(ObjectMapper mapper) { + super(JsonObject.class, PresentationResponseMessage.class); + this.mapper = mapper.copy().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + } + + @Override + public @Nullable PresentationResponseMessage transform(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) { + var builder = PresentationResponseMessage.Builder.newinstance(); + visitProperties(jsonObject, (k, v) -> { + switch (k) { + case PresentationResponseMessage.PRESENTATION_RESPONSE_MESSAGE_PRESENTATION_SUBMISSION_PROPERTY -> + builder.presentationSubmission(readPresentationSubmission(v, context)); + case PresentationResponseMessage.PRESENTATION_RESPONSE_MESSAGE_PRESENTATION_PROPERTY -> + builder.presentation(readPresentation(v, context)); + default -> context.reportProblem("Unknown property '%s'".formatted(k)); + } + }); + + return builder.build(); + } + + private PresentationSubmission readPresentationSubmission(JsonValue v, TransformerContext context) { + var rawJson = getRawJsonValue(v); + try { + return mapper.readValue(rawJson.toString(), PresentationSubmission.class); + } catch (JsonProcessingException e) { + context.reportProblem("Error reading JSON literal: %s".formatted(e.getMessage())); + return null; + } + } + + + private List readPresentation(JsonValue v, TransformerContext context) { + var rawJson = getRawJsonValue(v); + try { + return mapper.readValue(rawJson.toString(), new TypeReference<>() { + }); + } catch (JsonProcessingException e) { + context.reportProblem("Error reading JSON literal: %s".formatted(e.getMessage())); + return null; + } + } + + private JsonValue getRawJsonValue(JsonValue v) { + JsonObject jo; + if (v.getValueType() == JsonValue.ValueType.ARRAY && !((JsonArray) v).isEmpty()) { + jo = v.asJsonArray().getJsonObject(0); + } else { + jo = v.asJsonObject(); + } + return jo.get(JsonLdKeywords.VALUE); + } + +} diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/main/resources/document/iatp.v08.jsonld b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/resources/document/iatp.v08.jsonld new file mode 100644 index 00000000000..68d33163ed4 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/resources/document/iatp.v08.jsonld @@ -0,0 +1,127 @@ +{ + "@context": { + "@version": 1.1, + "@protected": true, + "iatp": "https://w3id.org/tractusx-trust/v0.8/", + "cred": "https://www.w3.org/2018/credentials/", + "xsd": "http://www.w3.org/2001/XMLSchema/", + "CredentialContainer": { + "@id": "iatp:CredentialContainer", + "@context": { + "payload": { + "@id": "iatp:payload", + "@type": "xsd:string" + } + } + }, + "CredentialMessage": { + "@id": "iatp:CredentialMessage", + "@context": { + "credentials": "iatp:credentials" + } + }, + "CredentialObject": { + "@id": "iatp:CredentialObject", + "@context": { + "credentialType": { + "@id": "iatp:credentialType", + "@container": "@set" + }, + "format": "iatp:format", + "offerReason": { + "@id": "iatp:offerReason", + "@type": "xsd:string" + }, + "bindingMethods": { + "@id": "iatp:bindingMethods", + "@type": "xsd:string", + "@container": "@set" + }, + "cryptographicSuites": { + "@id": "iatp:cryptographicSuites", + "@type": "xsd:string", + "@container": "@set" + }, + "issuancePolicy": "iatp:issuancePolicy" + } + }, + "CredentialOfferMessage": { + "@id": "iatp:CredentialOfferMessage", + "@context": { + "credentialIssuer": "cred:issuer", + "credentials": "iatp:credentials" + } + }, + "CredentialRequestMessage": { + "@id": "iatp:CredentialRequestMessage", + "@context": { + "format": "iatp:format", + "type": "@type" + } + }, + "CredentialService": "iatp:CredentialService", + "CredentialStatus": { + "@id": "iatp:CredentialStatus", + "@context": { + "requestId": { + "@id": "iatp:requestId", + "@type": "@id" + }, + "status": { + "@id": "iatp:status", + "@type": "xsd:string" + } + } + }, + "IssuerMetadata": { + "@id": "iatp:IssuerMetadata", + "@context": { + "credentialIssuer": "cred:issuer", + "credentialsSupported": { + "@id": "iatp:credentialsSupported", + "@container": "@set" + } + } + }, + "PresentationQueryMessage": { + "@id": "iatp:PresentationQueryMessage", + "@context": { + "presentationDefinition": { + "@id": "iatp:presentationDefinition", + "@type": "@json" + }, + "scope": { + "@id": "iatp:scope", + "@type": "xsd:string", + "@container": "@set" + } + } + }, + "PresentationResponseMessage": { + "@id": "iatp:PresentationResponseMessage", + "@context": { + "presentation": { + "@id": "iatp:presentation", + "@type": "@json" + }, + "presentationSubmission": { + "@id": "iatp:presentationSubmission", + "@type": "@json" + } + } + }, + "credentials": { + "@id": "iatp:credentials", + "@container": "@set" + }, + "credentialSubject": { + "@id": "iatp:credentialSubject", + "@type": "cred:credentialSubject" + }, + "format": { + "@id": "iatp:format", + "@type": "xsd:string" + }, + "type": "@type" + } +} diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/IdentityTrustTransformExtensionTest.java b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/IdentityTrustTransformExtensionTest.java index 2906d9e3f69..4d72b743053 100644 --- a/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/IdentityTrustTransformExtensionTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/IdentityTrustTransformExtensionTest.java @@ -14,32 +14,42 @@ package org.eclipse.edc.iam.identitytrust; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.iam.identitytrust.transform.from.JsonObjectFromPresentationResponseMessageTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToCredentialStatusTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToCredentialSubjectTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToIssuerTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToPresentationQueryTransformer; +import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToPresentationResponseMessageTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToVerifiableCredentialTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToVerifiablePresentationTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JwtToVerifiableCredentialTransformer; import org.eclipse.edc.iam.identitytrust.transform.to.JwtToVerifiablePresentationTransformer; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import static org.eclipse.edc.spi.CoreConstants.JSON_LD; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @ExtendWith(DependencyInjectionExtension.class) class IdentityTrustTransformExtensionTest { private final TypeTransformerRegistry mockRegistry = mock(); + private final TypeManager typeManager = mock(); + @BeforeEach void setup(ServiceExtensionContext context) { + when(typeManager.getMapper(JSON_LD)).thenReturn(new ObjectMapper()); + context.registerService(TypeManager.class, typeManager); context.registerService(TypeTransformerRegistry.class, mockRegistry); } @@ -48,6 +58,8 @@ void initialize_assertTransformerRegistrations(IdentityTrustTransformExtension e extension.initialize(context); verify(mockRegistry).register(isA(JsonObjectToCredentialStatusTransformer.class)); + verify(mockRegistry).register(isA(JsonObjectFromPresentationResponseMessageTransformer.class)); + verify(mockRegistry).register(isA(JsonObjectToPresentationResponseMessageTransformer.class)); verify(mockRegistry).register(isA(JsonObjectToCredentialSubjectTransformer.class)); verify(mockRegistry).register(isA(JsonObjectToIssuerTransformer.class)); verify(mockRegistry).register(isA(JsonObjectToPresentationQueryTransformer.class)); diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/from/JsonObjectFromPresentationResponseMessageTransformerTest.java b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/from/JsonObjectFromPresentationResponseMessageTransformerTest.java new file mode 100644 index 00000000000..ffa9c44f939 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/from/JsonObjectFromPresentationResponseMessageTransformerTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.iam.identitytrust.transform.from; + +import jakarta.json.Json; +import org.eclipse.edc.identitytrust.model.credentialservice.PresentationResponseMessage; +import org.eclipse.edc.transform.spi.TransformerContext; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.identitytrust.model.credentialservice.PresentationResponseMessage.PRESENTATION_RESPONSE_MESSAGE_PRESENTATION_PROPERTY; +import static org.eclipse.edc.identitytrust.model.credentialservice.PresentationResponseMessage.PRESENTATION_RESPONSE_MESSAGE_TYPE_PROPERTY; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; +import static org.mockito.Mockito.mock; + +public class JsonObjectFromPresentationResponseMessageTransformerTest { + + private final JsonObjectFromPresentationResponseMessageTransformer transformer = new JsonObjectFromPresentationResponseMessageTransformer(); + + private final TransformerContext context = mock(); + + @Test + void transform() { + + var response = PresentationResponseMessage.Builder.newinstance().presentation(List.of("jwt")).build(); + + var json = transformer.transform(response, context); + + assertThat(json).isNotNull(); + assertThat(json.getJsonString(TYPE).getString()).isEqualTo(PRESENTATION_RESPONSE_MESSAGE_TYPE_PROPERTY); + assertThat(json.getJsonArray(PRESENTATION_RESPONSE_MESSAGE_PRESENTATION_PROPERTY)) + .hasSize(1) + .first() + .isEqualTo(Json.createValue("jwt")); + + } + + @Test + void transform_withJson() { + var response = PresentationResponseMessage.Builder.newinstance() + .presentation(List.of(Map.of("@context", List.of()))) + .build(); + + var json = transformer.transform(response, context); + + assertThat(json).isNotNull(); + assertThat(json.getJsonString(TYPE).getString()).isEqualTo(PRESENTATION_RESPONSE_MESSAGE_TYPE_PROPERTY); + + var expected = Json.createObjectBuilder() + .add("@context", Json.createArrayBuilder().build()) + .build(); + + assertThat(json.getJsonArray(PRESENTATION_RESPONSE_MESSAGE_PRESENTATION_PROPERTY)) + .hasSize(1) + .first() + .isEqualTo(expected); + + } + + @Test + void transform_withStringAndJson() { + var response = PresentationResponseMessage.Builder.newinstance() + .presentation(List.of("jwt", Map.of("@context", List.of()))) + .build(); + var json = transformer.transform(response, context); + + assertThat(json).isNotNull(); + assertThat(json.getJsonString(TYPE).getString()).isEqualTo(PRESENTATION_RESPONSE_MESSAGE_TYPE_PROPERTY); + + var complex = Json.createObjectBuilder() + .add("@context", Json.createArrayBuilder().build()) + .build(); + + assertThat(json.getJsonArray(PRESENTATION_RESPONSE_MESSAGE_PRESENTATION_PROPERTY)) + .hasSize(2) + .containsExactly(Json.createValue("jwt"), complex); + + } +} diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryTransformerTest.java b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryMessageTransformerTest.java similarity index 94% rename from extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryTransformerTest.java rename to extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryMessageTransformerTest.java index 7be40b0dab3..4dd9be1bc30 100644 --- a/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryTransformerTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryMessageTransformerTest.java @@ -30,9 +30,10 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.identitytrust.VcConstants.IATP_CONTEXT_URL; import static org.mockito.Mockito.mock; -class JsonObjectToPresentationQueryTransformerTest { +class JsonObjectToPresentationQueryMessageTransformerTest { private final ObjectMapper mapper = JacksonJsonLd.createObjectMapper(); private final JsonObjectToPresentationQueryTransformer transformer = new JsonObjectToPresentationQueryTransformer(mapper); private final JsonLd jsonLd = new TitaniumJsonLd(mock()); @@ -43,7 +44,7 @@ class JsonObjectToPresentationQueryTransformerTest { @BeforeEach void setUp() { jsonLd.registerCachedDocument("https://identity.foundation/presentation-exchange/submission/v1", TestUtils.getFileFromResourceName("presentation_ex.json").toURI()); - jsonLd.registerCachedDocument("https://w3id.org/tractusx-trust/v0.8", TestUtils.getFileFromResourceName("presentation_query.json").toURI()); + jsonLd.registerCachedDocument(IATP_CONTEXT_URL, TestUtils.getFileFromResourceName("document/iatp.v08.jsonld").toURI()); // delegate to the generic transformer trr.register(new JsonValueToGenericTypeTransformer(mapper)); @@ -57,7 +58,7 @@ void transform_withScopes() throws JsonProcessingException { "https://identity.foundation/presentation-exchange/submission/v1", "https://w3id.org/tractusx-trust/v0.8" ], - "@type": "Query", + "@type": "PresentationQueryMessage", "scope": [ "org.eclipse.edc.vc.type:TestCredential:read", "org.eclipse.edc.vc.type:AnotherCredential:all" @@ -85,7 +86,7 @@ void transform_withScopes_separatedByWhitespace() throws JsonProcessingException "https://identity.foundation/presentation-exchange/submission/v1", "https://w3id.org/tractusx-trust/v0.8" ], - "@type": "Query", + "@type": "PresentationQueryMessage", "scope": [ "org.eclipse.edc.vc.type:TestCredential:read org.eclipse.edc.vc.type:AnotherCredential:all" ] @@ -112,7 +113,7 @@ void transform_withPresentationDefinition() throws JsonProcessingException { "https://identity.foundation/presentation-exchange/submission/v1", "https://w3id.org/tractusx-trust/v0.8" ], - "@type": "Query", + "@type": "PresentationQueryMessage", "presentationDefinition": { "id": "first simple example", "input_descriptors": [ @@ -160,7 +161,7 @@ void transform_withScopesAndPresDef() throws JsonProcessingException { "https://identity.foundation/presentation-exchange/submission/v1", "https://w3id.org/tractusx-trust/v0.8" ], - "@type": "Query", + "@type": "PresentationQueryMessage", "scope": ["test-scope1"], "presentationDefinition": { "id": "first simple example", diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationResponseMessageTransformerTest.java b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationResponseMessageTransformerTest.java new file mode 100644 index 00000000000..54f5b5c4f24 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationResponseMessageTransformerTest.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.iam.identitytrust.transform.to; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.json.JsonObject; +import org.eclipse.edc.core.transform.TransformerContextImpl; +import org.eclipse.edc.core.transform.TypeTransformerRegistryImpl; +import org.eclipse.edc.core.transform.transformer.to.JsonValueToGenericTypeTransformer; +import org.eclipse.edc.jsonld.TitaniumJsonLd; +import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.jsonld.util.JacksonJsonLd; +import org.eclipse.edc.junit.testfixtures.TestUtils; +import org.eclipse.edc.transform.spi.TransformerContext; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.identitytrust.VcConstants.IATP_CONTEXT_URL; +import static org.mockito.Mockito.mock; + +class JsonObjectToPresentationResponseMessageTransformerTest { + private final ObjectMapper mapper = JacksonJsonLd.createObjectMapper(); + private final JsonObjectToPresentationResponseMessageTransformer transformer = new JsonObjectToPresentationResponseMessageTransformer(mapper); + private final JsonLd jsonLd = new TitaniumJsonLd(mock()); + private final TypeTransformerRegistry trr = new TypeTransformerRegistryImpl(); + private final TransformerContext context = new TransformerContextImpl(trr); + + @BeforeEach + void setUp() { + jsonLd.registerCachedDocument("https://identity.foundation/presentation-exchange/submission/v1", TestUtils.getFileFromResourceName("presentation_ex.json").toURI()); + jsonLd.registerCachedDocument(IATP_CONTEXT_URL, TestUtils.getFileFromResourceName("document/iatp.v08.jsonld").toURI()); + // delegate to the generic transformer + + trr.register(new JsonValueToGenericTypeTransformer(mapper)); + } + + @Test + void transform() throws JsonProcessingException { + var obj = """ + { + "@context": [ + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "PresentationResponseMessage", + "presentation": "jwtPresentation" + } + """; + var json = mapper.readValue(obj, JsonObject.class); + var jo = jsonLd.expand(json); + assertThat(jo.succeeded()).withFailMessage(jo::getFailureDetail).isTrue(); + + var query = transformer.transform(jo.getContent(), context); + assertThat(query).isNotNull(); + assertThat(query.getPresentation()).hasSize(1) + .containsExactly("jwtPresentation"); + assertThat(query.getPresentationSubmission()).isNull(); + } + + @Test + void transform_MultipleJwt() throws JsonProcessingException { + var obj = """ + { + "@context": [ + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "PresentationResponseMessage", + "presentation": ["firstJwtPresentation", "secondJwtPresentation"] + } + """; + var json = mapper.readValue(obj, JsonObject.class); + var jo = jsonLd.expand(json); + assertThat(jo.succeeded()).withFailMessage(jo::getFailureDetail).isTrue(); + + var query = transformer.transform(jo.getContent(), context); + assertThat(query).isNotNull(); + assertThat(query.getPresentation()).hasSize(2) + .containsExactly("firstJwtPresentation", "secondJwtPresentation"); + assertThat(query.getPresentationSubmission()).isNull(); + } + + + @Test + void transform_singleJson() throws JsonProcessingException { + var obj = """ + { + "@context": [ + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "PresentationResponseMessage", + "presentation": { + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "type": [ + "VerifiablePresentation" + ] + } + } + """; + var json = mapper.readValue(obj, JsonObject.class); + var jo = jsonLd.expand(json); + assertThat(jo.succeeded()).withFailMessage(jo::getFailureDetail).isTrue(); + + var query = transformer.transform(jo.getContent(), context); + assertThat(query).isNotNull(); + assertThat(query.getPresentation()).hasSize(1) + .allMatch((val) -> val instanceof Map); + + assertThat(query.getPresentationSubmission()).isNull(); + } + + @Test + void transform_multipleJson() throws JsonProcessingException { + var obj = """ + { + "@context": [ + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "PresentationResponseMessage", + "presentation": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "type": [ + "VerifiablePresentation" + ] + }, + { + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "type": [ + "VerifiablePresentation" + ] + } + ] + } + """; + var json = mapper.readValue(obj, JsonObject.class); + var jo = jsonLd.expand(json); + assertThat(jo.succeeded()).withFailMessage(jo::getFailureDetail).isTrue(); + + var query = transformer.transform(jo.getContent(), context); + assertThat(query).isNotNull(); + assertThat(query.getPresentation()).hasSize(2) + .allMatch((val) -> val instanceof Map); + + + assertThat(query.getPresentationSubmission()).isNull(); + } + + @Test + void transform_mixed() throws JsonProcessingException { + var obj = """ + { + "@context": [ + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "PresentationResponseMessage", + "presentation": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "type": [ + "VerifiablePresentation" + ] + }, + "jwtPresentation" + ] + } + """; + var json = mapper.readValue(obj, JsonObject.class); + var jo = jsonLd.expand(json); + assertThat(jo.succeeded()).withFailMessage(jo::getFailureDetail).isTrue(); + + var query = transformer.transform(jo.getContent(), context); + assertThat(query).isNotNull(); + assertThat(query.getPresentation()).hasSize(2) + .allMatch((val) -> val instanceof Map || val instanceof String); + + + assertThat(query.getPresentationSubmission()).isNull(); + } + +} \ No newline at end of file diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/test/resources/presentation_query.json b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/resources/presentation_query.json deleted file mode 100644 index 740434b72e1..00000000000 --- a/extensions/common/iam/identity-trust/identity-trust-transform/src/test/resources/presentation_query.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "@context": { - "scope": { - "@id": "https://w3id.org/tractusx-trust/v0.8/scope", - "@container": "@set" - }, - "presentationDefinition": { - "@id": "https://w3id.org/tractusx-trust/v0.8/presentationDefinition", - "@type": "@json" - } - } -} \ No newline at end of file diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationQuery.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationQueryMessage.java similarity index 77% rename from spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationQuery.java rename to spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationQueryMessage.java index b485b7cb124..ace80ed35d7 100644 --- a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationQuery.java +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationQueryMessage.java @@ -28,14 +28,14 @@ * * @see IATP Specification */ -public class PresentationQuery { - public static final String PRESENTATION_QUERY_SCOPE_PROPERTY = IATP_PREFIX + "scope"; - public static final String PRESENTATION_QUERY_DEFINITION_PROPERTY = IATP_PREFIX + "presentationDefinition"; - public static final String PRESENTATION_QUERY_TYPE_PROPERTY = IATP_PREFIX + "Query"; +public class PresentationQueryMessage { + public static final String PRESENTATION_QUERY_MESSAGE_SCOPE_PROPERTY = IATP_PREFIX + "scope"; + public static final String PRESENTATION_QUERY_MESSAGE_DEFINITION_PROPERTY = IATP_PREFIX + "presentationDefinition"; + public static final String PRESENTATION_QUERY_MESSAGE_TYPE_PROPERTY = IATP_PREFIX + "PresentationQueryMessage"; private final List scopes = new ArrayList<>(); private PresentationDefinition presentationDefinition; - private PresentationQuery() { + private PresentationQueryMessage() { } public List getScopes() { @@ -47,10 +47,10 @@ public PresentationDefinition getPresentationDefinition() { } public static final class Builder { - private final PresentationQuery query; + private final PresentationQueryMessage query; private Builder() { - query = new PresentationQuery(); + query = new PresentationQueryMessage(); } public static Builder newinstance() { @@ -72,7 +72,7 @@ public Builder presentationDefinition(PresentationDefinition presentationDefinit return this; } - public PresentationQuery build() { + public PresentationQueryMessage build() { return query; } } diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationResponse.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationResponse.java deleted file mode 100644 index 99143d06161..00000000000 --- a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationResponse.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation - * - */ - -package org.eclipse.edc.identitytrust.model.credentialservice; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * A representation of a Presentation Response - * that the credential service sends back to the requester. - *

- * The {@code vp_token} param is a JSON String or JSON object that MUST contain a single Verifiable Presentation or an - * array of JSON Strings and JSON objects, each of them containing a Verifiable Presentations. Each Verifiable Presentation - * MUST be represented as a JSON string (that is a Base64url encoded value) or a JSON object, depending on the requested format. - */ -public record PresentationResponse(@JsonProperty("vp_token") Object[] vpToken, - @JsonProperty("presentation_submission") PresentationSubmission presentationSubmission) { -} diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationResponseMessage.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationResponseMessage.java new file mode 100644 index 00000000000..71de133b1aa --- /dev/null +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationResponseMessage.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.identitytrust.model.credentialservice; + +import java.util.ArrayList; +import java.util.List; + +import static org.eclipse.edc.identitytrust.VcConstants.IATP_PREFIX; + +/** + * A representation of a Presentation Response + * that the credential service sends back to the requester. + *

+ * The {@code presentation} param is a JSON String or JSON object that MUST contain a single Verifiable Presentation or an + * array of JSON Strings and JSON objects, each of them containing a Verifiable Presentations. Each Verifiable Presentation + * MUST be represented as a JSON string (that is a Base64url encoded value) or a JSON object, depending on the requested format. + */ +public class PresentationResponseMessage { + + public static final String PRESENTATION_RESPONSE_MESSAGE_PRESENTATION_PROPERTY = IATP_PREFIX + "presentation"; + public static final String PRESENTATION_RESPONSE_MESSAGE_PRESENTATION_SUBMISSION_PROPERTY = IATP_PREFIX + "presentationSubmission"; + public static final String PRESENTATION_RESPONSE_MESSAGE_TYPE_PROPERTY = IATP_PREFIX + "PresentationResponseMessage"; + + private List presentation = new ArrayList<>(); + + private PresentationSubmission presentationSubmission; + + public PresentationSubmission getPresentationSubmission() { + return presentationSubmission; + } + + public List getPresentation() { + return presentation; + } + + public static final class Builder { + private final PresentationResponseMessage responseMessage; + + private Builder() { + responseMessage = new PresentationResponseMessage(); + } + + public static PresentationResponseMessage.Builder newinstance() { + return new PresentationResponseMessage.Builder(); + } + + public PresentationResponseMessage.Builder presentation(List presentations) { + this.responseMessage.presentation = presentations; + return this; + } + + public PresentationResponseMessage.Builder presentationSubmission(PresentationSubmission presentationSubmission) { + this.responseMessage.presentationSubmission = presentationSubmission; + return this; + } + + public PresentationResponseMessage build() { + return responseMessage; + } + } +}