diff --git a/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/IdentityAndTrustExtension.java b/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/IdentityAndTrustExtension.java index 97961edb2e1..65f5b252139 100644 --- a/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/IdentityAndTrustExtension.java +++ b/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/IdentityAndTrustExtension.java @@ -30,10 +30,12 @@ import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provider; import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.http.EdcHttpClient; import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.verifiablecredentials.jwt.JwtPresentationVerifier; import org.eclipse.edc.verifiablecredentials.linkeddata.LdpVerifier; import org.eclipse.edc.verification.jwt.SelfIssuedIdTokenVerifier; @@ -51,7 +53,6 @@ public class IdentityAndTrustExtension implements ServiceExtension { @Inject private SecureTokenService secureTokenService; - private PresentationVerifier presentationVerifier; @Inject private CredentialServiceClient credentialServiceClient; @@ -71,11 +72,19 @@ public class IdentityAndTrustExtension implements ServiceExtension { @Inject private JsonLd jsonLd; - private JwtValidator jwtValidator; - private JwtVerifier jwtVerifier; @Inject private Clock clock; + @Inject + private TypeTransformerRegistry typeTransformerRegistry; + + @Inject + private EdcHttpClient httpClient; + + private JwtValidator jwtValidator; + private JwtVerifier jwtVerifier; + private PresentationVerifier presentationVerifier; + @Provider public IdentityService createIdentityService(ServiceExtensionContext context) { return new IdentityAndTrustService(secureTokenService, getIssuerDid(context), context.getParticipantId(), getPresentationVerifier(context), @@ -115,6 +124,12 @@ public JwtVerifier getJwtVerifier() { return jwtVerifier; } + @Provider + public CredentialServiceClient createClient(ServiceExtensionContext context) { + context.getMonitor().warning("Using a dummy CredentialServiceClient, that'll return null always. Don't use this in production use cases!"); + return (csUrl, siTokenJwt, scopes) -> null; + } + private String getOwnDid(ServiceExtensionContext context) { // todo: this must be config value return null; diff --git a/extensions/common/iam/identity-trust/identity-trust-service/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityAndTrustService.java b/extensions/common/iam/identity-trust/identity-trust-service/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityAndTrustService.java index 6b44dc9a35f..24e41e42974 100644 --- a/extensions/common/iam/identity-trust/identity-trust-service/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityAndTrustService.java +++ b/extensions/common/iam/identity-trust/identity-trust-service/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityAndTrustService.java @@ -122,8 +122,6 @@ public Result verifyJwtToken(TokenRepresentation tokenRepresentation return issuerResult.mapTo(); } - //todo: implement actual VP request, currently it's a stub - // https://github.com/eclipse-edc/Connector/issues/3495 var vpResponse = credentialServiceClient.requestPresentation(null, null, null); if (vpResponse.failed()) { diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/build.gradle.kts b/extensions/common/iam/identity-trust/identity-trust-transform/build.gradle.kts index 44f7e2b4f3c..f7c9dfd4986 100644 --- a/extensions/common/iam/identity-trust/identity-trust-transform/build.gradle.kts +++ b/extensions/common/iam/identity-trust/identity-trust-transform/build.gradle.kts @@ -25,6 +25,7 @@ dependencies { testImplementation(project(":extensions:common:json-ld")) testImplementation(project(":core:common:transform-core")) //for the TransformerContextImpl + testImplementation(project(":core:common:junit")) //for the TestUtils testImplementation(testFixtures(project(":spi:common:identity-trust-spi"))) } 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 new file mode 100644 index 00000000000..733fb2bf1f7 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/main/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryTransformer.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.iam.identitytrust.transform.to; + +import com.fasterxml.jackson.core.JsonProcessingException; +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.PresentationQuery; +import org.eclipse.edc.identitytrust.model.presentationdefinition.PresentationDefinition; +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; + +/** + * Transforms a JsonObject into a PresentationQuery object. + */ +public class JsonObjectToPresentationQueryTransformer extends AbstractJsonLdTransformer { + + private final ObjectMapper mapper; + + public JsonObjectToPresentationQueryTransformer(ObjectMapper mapper) { + super(JsonObject.class, PresentationQuery.class); + this.mapper = mapper; + } + + @Override + public @Nullable PresentationQuery transform(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) { + var bldr = PresentationQuery.Builder.newinstance(); + visitProperties(jsonObject, (k, v) -> { + switch (k) { + case PresentationQuery.PRESENTATION_QUERY_DEFINITION_PROPERTY -> + bldr.presentationDefinition(readPresentationDefinition(v, context)); + case PresentationQuery.PRESENTATION_QUERY_SCOPE_PROPERTY -> + transformArrayOrObject(v, Object.class, o -> bldr.scope(o.toString()), context); + default -> context.reportProblem("Unknown property '%s'".formatted(k)); + } + }); + + return bldr.build(); + } + + private PresentationDefinition readPresentationDefinition(JsonValue v, TransformerContext context) { + JsonObject jo; + if (v.getValueType() == JsonValue.ValueType.ARRAY && !((JsonArray) v).isEmpty()) { + jo = v.asJsonArray().getJsonObject(0); + } else { + jo = v.asJsonObject(); + } + var rawJson = jo.get(JsonLdKeywords.VALUE); + try { + return mapper.readValue(rawJson.toString(), PresentationDefinition.class); + } catch (JsonProcessingException e) { + context.reportProblem("Error reading JSON literal: %s".formatted(e.getMessage())); + return null; + } + } +} 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/JsonObjectToPresentationQueryTransformerTest.java new file mode 100644 index 00000000000..e3be6ed3920 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/java/org/eclipse/edc/iam/identitytrust/transform/to/JsonObjectToPresentationQueryTransformerTest.java @@ -0,0 +1,175 @@ +/* + * 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.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 static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class JsonObjectToPresentationQueryTransformerTest { + private final ObjectMapper mapper = JacksonJsonLd.createObjectMapper(); + private final JsonObjectToPresentationQueryTransformer transformer = new JsonObjectToPresentationQueryTransformer(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("https://w3id.org/tractusx-trust/v0.8", TestUtils.getFileFromResourceName("presentation_query.json").toURI()); + // delegate to the generic transformer + + trr.register(new JsonValueToGenericTypeTransformer(mapper)); + } + + @Test + void transform_withScopes() throws JsonProcessingException { + var obj = """ + { + "@context": [ + "https://identity.foundation/presentation-exchange/submission/v1", + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "Query", + "scope": [ + "org.eclipse.edc.vc.type:TestCredential:read", + "org.eclipse.edc.vc.type:AnotherCredential:all" + ] + } + """; + 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.getScopes()).hasSize(2) + .containsExactlyInAnyOrder( + "org.eclipse.edc.vc.type:TestCredential:read", + "org.eclipse.edc.vc.type:AnotherCredential:all"); + assertThat(query.getPresentationDefinition()).isNull(); + } + + @Test + void transform_withPresentationDefinition() throws JsonProcessingException { + var json = """ + { + "@context": [ + "https://identity.foundation/presentation-exchange/submission/v1", + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "Query", + "presentationDefinition": { + "id": "first simple example", + "input_descriptors": [ + { + "id": "descriptor-id-1", + "name": "A specific type of VC", + "purpose": "We want a VC of this type", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "pattern": "" + } + } + ] + } + } + ] + } + } + """; + var jobj = mapper.readValue(json, JsonObject.class); + + var expansion = jsonLd.expand(jobj); + assertThat(expansion.succeeded()).withFailMessage(expansion::getFailureDetail).isTrue(); + + var query = transformer.transform(expansion.getContent(), context); + assertThat(query).isNotNull(); + assertThat(query.getScopes()).isNotNull().isEmpty(); + assertThat(query.getPresentationDefinition()).isNotNull(); + assertThat(query.getPresentationDefinition().getInputDescriptors()).isNotEmpty() + .allSatisfy(id -> assertThat(id.getId()).isEqualTo("descriptor-id-1")); + + } + + @Test + void transform_withScopesAndPresDef() throws JsonProcessingException { + var json = """ + { + "@context": [ + "https://identity.foundation/presentation-exchange/submission/v1", + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "Query", + "scope": ["test-scope1"], + "presentationDefinition": { + "id": "first simple example", + "input_descriptors": [ + { + "id": "descriptor-id-1", + "name": "A specific type of VC", + "purpose": "We want a VC of this type", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "pattern": "" + } + } + ] + } + } + ] + } + } + """; + var jobj = mapper.readValue(json, JsonObject.class); + + var expansion = jsonLd.expand(jobj); + assertThat(expansion.succeeded()).withFailMessage(expansion::getFailureDetail).isTrue(); + + var query = transformer.transform(expansion.getContent(), context); + assertThat(query).isNotNull(); + assertThat(query.getScopes()).isNotNull().containsExactly("test-scope1"); + assertThat(query.getPresentationDefinition()).isNotNull(); + assertThat(query.getPresentationDefinition().getInputDescriptors()).isNotEmpty() + .allSatisfy(id -> assertThat(id.getId()).isEqualTo("descriptor-id-1")); + } +} \ No newline at end of file diff --git a/extensions/common/iam/identity-trust/identity-trust-transform/src/test/resources/presentation_ex.json b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/resources/presentation_ex.json new file mode 100644 index 00000000000..488925037e5 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/resources/presentation_ex.json @@ -0,0 +1,15 @@ +{ + "@context": { + "@version": 1.1, + "PresentationSubmission": { + "@id": "https://identity.foundation/presentation-exchange/#presentation-submission", + "@context": { + "@version": 1.1, + "presentation_submission": { + "@id": "https://identity.foundation/presentation-exchange/#presentation-submission", + "@type": "@json" + } + } + } + } +} \ 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 new file mode 100644 index 00000000000..740434b72e1 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-transform/src/test/resources/presentation_query.json @@ -0,0 +1,12 @@ +{ + "@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/VcConstants.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/VcConstants.java index 1215350c1bb..53c1906c1ab 100644 --- a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/VcConstants.java +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/VcConstants.java @@ -18,4 +18,15 @@ public interface VcConstants { String VC_PREFIX = "https://www.w3.org/2018/credentials#"; String SCHEMA_ORG_NAMESPACE = "https://schema.org/"; + + String IATP_CONTEXT_URL = "https://w3id.org/tractusx-trust/v0.8"; + String IATP_PREFIX = IATP_CONTEXT_URL + "/"; + String PRESENTATION_EXCHANGE_URL = "https://identity.foundation/presentation-exchange/submission/v1"; + String W3C_CREDENTIALS_URL = "https://www.w3.org/2018/credentials/v1"; + String VERIFIABLE_PRESENTATION_TYPE = "VerifiablePresentation"; + String JWS_2020_URL = "https://w3id.org/security/suites/jws-2020/v1"; + String DID_CONTEXT_URL = "https://www.w3.org/ns/did/v1"; + String PRESENTATION_SUBMISSION_URL = "https://identity.foundation/presentation-exchange/submission/v1/"; + String JWS_2020_SIGNATURE_SUITE = "JsonWebSignature2020"; + String ED25519_SIGNATURE_SUITE = "Ed25519Signature2020"; // not used right now } diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/InputDescriptorMapping.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/InputDescriptorMapping.java new file mode 100644 index 00000000000..f61e509ce7c --- /dev/null +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/InputDescriptorMapping.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.identitytrust.model.credentialservice; + +/** + * Represents the {@code descriptor_map} of a Presentation Submission + */ +public record InputDescriptorMapping(String id, String format, String path) { +} 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/PresentationQuery.java new file mode 100644 index 00000000000..18c178e6572 --- /dev/null +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationQuery.java @@ -0,0 +1,79 @@ +/* + * 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 org.eclipse.edc.identitytrust.model.presentationdefinition.PresentationDefinition; + +import java.util.ArrayList; +import java.util.List; + +import static org.eclipse.edc.identitytrust.VcConstants.IATP_PREFIX; + + +/** + * Represents a query DTO that is sent to a CredentialService. Must be serialized to JSON-LD. + * + * @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"; + private List scopes = new ArrayList<>(); + private PresentationDefinition presentationDefinition; + + private PresentationQuery() { + } + + public List getScopes() { + return scopes; + } + + public PresentationDefinition getPresentationDefinition() { + return presentationDefinition; + } + + public static final class Builder { + private final PresentationQuery query; + + private Builder() { + query = new PresentationQuery(); + } + + public static Builder newinstance() { + return new Builder(); + } + + public Builder scopes(List scopes) { + this.query.scopes = scopes; + return this; + } + + public Builder scope(String scope) { + this.query.scopes.add(scope); + return this; + } + + public Builder presentationDefinition(PresentationDefinition presentationDefinition) { + this.query.presentationDefinition = presentationDefinition; + return this; + } + + public PresentationQuery 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 new file mode 100644 index 00000000000..30530d028e3 --- /dev/null +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationResponse.java @@ -0,0 +1,25 @@ +/* + * 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. + */ +public record PresentationResponse(@JsonProperty("vp_token") String vpToken, + @JsonProperty("presentation_submission") PresentationSubmission presentationSubmission) { +} diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationSubmission.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationSubmission.java new file mode 100644 index 00000000000..d1e458402b2 --- /dev/null +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationSubmission.java @@ -0,0 +1,27 @@ +/* + * 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; + +import java.util.List; + +/** + * Representation of a DIF Presentation Submission. + */ +public record PresentationSubmission(@JsonProperty("id") String id, + @JsonProperty("definition_id") String definitionId, + @JsonProperty("descriptor_map") List descriptorMap) { +} diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/Constraints.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/Constraints.java new file mode 100644 index 00000000000..330c43e703a --- /dev/null +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/Constraints.java @@ -0,0 +1,20 @@ +/* + * 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.presentationdefinition; + +import java.util.List; + +public record Constraints(List fields) { +} \ No newline at end of file diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/Field.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/Field.java new file mode 100644 index 00000000000..270d86447e8 --- /dev/null +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/Field.java @@ -0,0 +1,94 @@ +/* + * 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.presentationdefinition; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class Field { + private List paths = new ArrayList<>(); + private String id; + private String name; + private String purpose; + private FilterExpression expr; + + private Field() { + + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getPurpose() { + return purpose; + } + + public List getPaths() { + return paths; + } + + public FilterExpression getExpr() { + return expr; + } + + + public static final class Builder { + private final Field field; + + private Builder() { + field = new Field(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder paths(List paths) { + this.field.paths = paths; + return this; + } + + public Builder id(String id) { + this.field.id = id; + return this; + } + + public Builder name(String name) { + this.field.name = name; + return this; + } + + public Builder purpose(String purpose) { + this.field.purpose = purpose; + return this; + } + + public Builder expr(FilterExpression expr) { + this.field.expr = expr; + return this; + } + + public Field build() { + Objects.requireNonNull(field.paths, "Must contain a paths property."); + return field; + } + } +} diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/FilterExpression.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/FilterExpression.java new file mode 100644 index 00000000000..5cc9b663a6b --- /dev/null +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/FilterExpression.java @@ -0,0 +1,18 @@ +/* + * 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.presentationdefinition; + +public record FilterExpression(String type, String pattern) { +} diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/Format.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/Format.java new file mode 100644 index 00000000000..9a0d8be9d42 --- /dev/null +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/Format.java @@ -0,0 +1,19 @@ +/* + * 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.presentationdefinition; + +public class Format { + //todo +} diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/InputDescriptor.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/InputDescriptor.java new file mode 100644 index 00000000000..3dd998edb77 --- /dev/null +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/InputDescriptor.java @@ -0,0 +1,88 @@ +/* + * 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.presentationdefinition; + +import java.util.Objects; + +public class InputDescriptor { + private String id; + private String name; + private String purpose; + private Format format; + private Constraints constraints; + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getPurpose() { + return purpose; + } + + public Format getFormat() { + return format; + } + + public Constraints getConstraints() { + return constraints; + } + + public static final class Builder { + private final InputDescriptor descriptor; + + private Builder() { + descriptor = new InputDescriptor(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder id(String id) { + this.descriptor.id = id; + return this; + } + + public Builder name(String name) { + this.descriptor.name = name; + return this; + } + + public Builder purpose(String purpose) { + this.descriptor.purpose = purpose; + return this; + } + + public Builder format(Format format) { + this.descriptor.format = format; + return this; + } + + public Builder constraints(Constraints constraints) { + this.descriptor.constraints = constraints; + return this; + } + + public InputDescriptor build() { + Objects.requireNonNull(descriptor.id, "InputDescriptor must have an ID."); + Objects.requireNonNull(descriptor.constraints, "InputDescriptor must have a Constraints object."); + return descriptor; + } + } +} diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/PresentationDefinition.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/PresentationDefinition.java new file mode 100644 index 00000000000..a72e1ac81b9 --- /dev/null +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/model/presentationdefinition/PresentationDefinition.java @@ -0,0 +1,102 @@ +/* + * 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.presentationdefinition; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; + +import java.util.List; +import java.util.Objects; + +/** + * Represents a DIF Presentation Definition + */ + +public class PresentationDefinition { + private String id; + private String name; + private String purpose; + @JsonProperty("input_descriptors") + private List inputDescriptors; + private Format format; + + private PresentationDefinition() { + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getPurpose() { + return purpose; + } + + public List getInputDescriptors() { + return inputDescriptors; + } + + public Format getFormat() { + return format; + } + + @JsonPOJOBuilder(withPrefix = "") + public static final class Builder { + private final PresentationDefinition presentationDefinition; + + private Builder() { + presentationDefinition = new PresentationDefinition(); + } + + @JsonCreator + public static Builder newInstance() { + return new Builder(); + } + + public Builder id(String id) { + this.presentationDefinition.id = id; + return this; + } + + public Builder name(String name) { + this.presentationDefinition.name = name; + return this; + } + + public Builder purpose(String purpose) { + this.presentationDefinition.purpose = purpose; + return this; + } + + public Builder inputDescriptors(List inputDescriptor) { + this.presentationDefinition.inputDescriptors = inputDescriptor; + return this; + } + + public Builder format(Format format) { + this.presentationDefinition.format = format; + return this; + } + + public PresentationDefinition build() { + Objects.requireNonNull(presentationDefinition.id, "PresentationDefinition must have an ID."); + return presentationDefinition; + } + } +} diff --git a/spi/common/identity-trust-spi/src/test/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationSubmissionSerDesTest.java b/spi/common/identity-trust-spi/src/test/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationSubmissionSerDesTest.java new file mode 100644 index 00000000000..7808e1d2101 --- /dev/null +++ b/spi/common/identity-trust-spi/src/test/java/org/eclipse/edc/identitytrust/model/credentialservice/PresentationSubmissionSerDesTest.java @@ -0,0 +1,69 @@ +/* + * 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.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class PresentationSubmissionSerDesTest { + private final ObjectMapper mapper = new ObjectMapper(); + + @Test + void verifyDeserialization() throws JsonProcessingException { + var json = """ + { + "id": "a30e3b91-fb77-4d22-95fa-871689c322e2", + "definition_id": "32f54163-7166-48f1-93d8-ff217bdb0653", + "descriptor_map": [ + { + "id": "banking_input_2", + "format": "jwt_vc", + "path": "$.verifiableCredential[0]" + }, + { + "id": "employment_input", + "format": "ldp_vc", + "path": "$.verifiableCredential[1]" + }, + { + "id": "citizenship_input_1", + "format": "ldp_vc", + "path": "$.verifiableCredential[2]" + } + ] + } + """; + var pd = mapper.readValue(json, PresentationSubmission.class); + assertThat(pd).isNotNull(); + + assertThat(pd.id()).isEqualTo("a30e3b91-fb77-4d22-95fa-871689c322e2"); + assertThat(pd.definitionId()).isEqualTo("32f54163-7166-48f1-93d8-ff217bdb0653"); + assertThat(pd.descriptorMap()).hasSize(3); + } + + @Test + void verifySerialization() throws JsonProcessingException { + var pd = new PresentationSubmission("test-id", "test-def-id", List.of(new InputDescriptorMapping("test-input", "ldp_vc", "$.verifiableCredentials[0]"))); + var json = mapper.writeValueAsString(pd); + + var deser = mapper.readValue(json, PresentationSubmission.class); + assertThat(deser).usingRecursiveComparison().isEqualTo(pd); + } +} \ No newline at end of file