Skip to content

Commit

Permalink
chore: moved IATP model classes up from IdentityHub
Browse files Browse the repository at this point in the history
  • Loading branch information
paullatzelsperger committed Nov 15, 2023
1 parent 5b13175 commit 93e2c56
Show file tree
Hide file tree
Showing 19 changed files with 869 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@

package org.eclipse.edc.iam.identitytrust.core;

import jakarta.json.Json;
import org.eclipse.edc.iam.did.spi.resolution.DidResolverRegistry;
import org.eclipse.edc.iam.identitytrust.IdentityAndTrustService;
import org.eclipse.edc.iam.identitytrust.core.defaults.DefaultCredentialServiceClient;
import org.eclipse.edc.iam.identitytrust.validation.SelfIssuedIdTokenValidator;
import org.eclipse.edc.iam.identitytrust.verification.MultiFormatPresentationVerifier;
import org.eclipse.edc.identitytrust.CredentialServiceClient;
Expand All @@ -30,15 +32,18 @@
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;

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

import static org.eclipse.edc.spi.CoreConstants.JSON_LD;

Expand All @@ -51,7 +56,6 @@ public class IdentityAndTrustExtension implements ServiceExtension {
@Inject
private SecureTokenService secureTokenService;

private PresentationVerifier presentationVerifier;

@Inject
private CredentialServiceClient credentialServiceClient;
Expand All @@ -71,11 +75,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),
Expand Down Expand Up @@ -115,6 +127,11 @@ public JwtVerifier getJwtVerifier() {
return jwtVerifier;
}

@Provider
public CredentialServiceClient createClient() {
return new DefaultCredentialServiceClient(httpClient, Json.createBuilderFactory(Map.of()), typeManager.getMapper(JSON_LD), typeTransformerRegistry);
}

private String getOwnDid(ServiceExtensionContext context) {
// todo: this must be config value
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,6 @@ public Result<ClaimToken> 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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")))
}

Original file line number Diff line number Diff line change
@@ -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<JsonObject, PresentationQuery> {

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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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": "<the type of VC e.g. degree certificate>"
}
}
]
}
}
]
}
}
""";
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": "<the type of VC e.g. degree certificate>"
}
}
]
}
}
]
}
}
""";
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"));
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading

0 comments on commit 93e2c56

Please sign in to comment.