Skip to content

Commit

Permalink
feat: create model transformers for VC and VP (#3515)
Browse files Browse the repository at this point in the history
* feat: add transformers for IATP model classes

* updated VC test, removed 'from' tests

* removed 'from' transformers

* cleanup

* externalized raw VC

* removed expansion inside the transformer

* added presentation tests

* pr remarks

* fix tests
  • Loading branch information
paullatzelsperger authored Oct 6, 2023
1 parent d2f8e13 commit 9d39f5c
Show file tree
Hide file tree
Showing 30 changed files with 1,582 additions and 81 deletions.
23 changes: 23 additions & 0 deletions extensions/common/iam/identity-trust/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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
*
*/

plugins {
`java-library`
`maven-publish`
}

dependencies {
api(project(":extensions:common:iam:identity-trust:identity-trust-transform"))
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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
*
*/

plugins {
`java-library`
`maven-publish`
}

dependencies {
api(project(":spi:common:identity-trust-spi"))
api(project(":spi:common:json-ld-spi"))
api(project(":spi:common:transform-spi"))
api(project(":spi:common:transform-spi"))

testImplementation(project(":extensions:common:json-ld"))
testImplementation(project(":core:common:transform-core")) //for the TransformerContextImpl
testImplementation(testFixtures(project(":spi:common:identity-trust-spi")))
}

Original file line number Diff line number Diff line change
@@ -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.iam.identitytrust;

import org.eclipse.edc.iam.identitytrust.transform.to.JsonObjectToCredentialStatusTransformer;
import org.eclipse.edc.jsonld.spi.JsonLd;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.spi.result.Result;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;

import static java.lang.String.format;

@Extension(value = IdentityTrustTransformExtension.NAME, categories = { "iam", "transform", "jsonld" })
public class IdentityTrustTransformExtension implements ServiceExtension {
public static final String NAME = "Identity And Trust Transform Extension";

@Inject
private TypeTransformerRegistry typeTransformerRegistry;

@Inject
private JsonLd jsonLdService;

@Override
public void initialize(ServiceExtensionContext context) {
getResourceUri("document" + File.separator + "credentials.v2.jsonld")
.onSuccess(uri -> jsonLdService.registerCachedDocument("https://www.w3.org/2018/credentials/v2", uri))
.onFailure(failure -> context.getMonitor().warning("Failed to register cached json-ld document: " + failure.getFailureDetail()));

getResourceUri("document" + File.separator + "credentials.v1.jsonld")
.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()));

typeTransformerRegistry.register(new JsonObjectToCredentialStatusTransformer());
}

@NotNull
private Result<URI> getResourceUri(String name) {
var uri = getClass().getClassLoader().getResource(name);
if (uri == null) {
return Result.failure(format("Cannot find resource %s", name));
}

try {
return Result.success(uri.toURI());
} catch (URISyntaxException e) {
return Result.failure(format("Cannot read resource %s: %s", name, e.getMessage()));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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 jakarta.json.JsonObject;
import org.eclipse.edc.identitytrust.model.CredentialStatus;
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.HashMap;

import static org.eclipse.edc.identitytrust.model.CredentialStatus.CREDENTIAL_STATUS_TYPE_PROPERTY;

public class JsonObjectToCredentialStatusTransformer extends AbstractJsonLdTransformer<JsonObject, CredentialStatus> {
public JsonObjectToCredentialStatusTransformer() {
super(JsonObject.class, CredentialStatus.class);
}

@Override
public @Nullable CredentialStatus transform(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) {


var props = new HashMap<String, Object>();
var id = nodeId(jsonObject);
var type = transformString(jsonObject.get(CREDENTIAL_STATUS_TYPE_PROPERTY), context);
visitProperties(jsonObject, (s, jsonValue) -> props.put(s, transformGenericProperty(jsonValue, context)));

return new CredentialStatus(id, type, props);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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 jakarta.json.JsonObject;
import org.eclipse.edc.identitytrust.model.CredentialSubject;
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 JsonObjectToCredentialSubjectTransformer extends AbstractJsonLdTransformer<JsonObject, CredentialSubject> {
public JsonObjectToCredentialSubjectTransformer() {
super(JsonObject.class, CredentialSubject.class);
}

@Override
public @Nullable CredentialSubject transform(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) {
var builder = CredentialSubject.Builder.newInstance();

visitProperties(jsonObject, (s, jsonValue) -> {
if (s.equals(CredentialSubject.CREDENTIAL_SUBJECT_ID_PROPERTY)) {
builder.id(transformString(jsonValue, context));
} else {
builder.claim(s, transformGenericProperty(jsonValue, context));
}
});

return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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 jakarta.json.JsonObject;
import jakarta.json.JsonValue;
import org.eclipse.edc.identitytrust.model.CredentialStatus;
import org.eclipse.edc.identitytrust.model.CredentialSubject;
import org.eclipse.edc.identitytrust.model.VerifiableCredential;
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.time.Instant;

import static org.eclipse.edc.identitytrust.model.VerifiableCredential.Builder;
import static org.eclipse.edc.identitytrust.model.VerifiableCredential.VERIFIABLE_CREDENTIAL_DESCRIPTION_PROPERTY;
import static org.eclipse.edc.identitytrust.model.VerifiableCredential.VERIFIABLE_CREDENTIAL_EXPIRATIONDATE_PROPERTY;
import static org.eclipse.edc.identitytrust.model.VerifiableCredential.VERIFIABLE_CREDENTIAL_ISSUANCEDATE_PROPERTY;
import static org.eclipse.edc.identitytrust.model.VerifiableCredential.VERIFIABLE_CREDENTIAL_ISSUER_PROPERTY;
import static org.eclipse.edc.identitytrust.model.VerifiableCredential.VERIFIABLE_CREDENTIAL_NAME_PROPERTY;
import static org.eclipse.edc.identitytrust.model.VerifiableCredential.VERIFIABLE_CREDENTIAL_STATUS_PROPERTY;
import static org.eclipse.edc.identitytrust.model.VerifiableCredential.VERIFIABLE_CREDENTIAL_SUBJECT_PROPERTY;
import static org.eclipse.edc.identitytrust.model.VerifiableCredential.VERIFIABLE_CREDENTIAL_VALIDFROM_PROPERTY;
import static org.eclipse.edc.identitytrust.model.VerifiableCredential.VERIFIABLE_CREDENTIAL_VALIDUNTIL_PROPERTY;

/**
* Transforms a JSON-LD structure into a {@link VerifiableCredential}.
* Note that keeping a raw form of the JSON-LD for verification purposes is highly recommended.
*/
public class JsonObjectToVerifiableCredentialTransformer extends AbstractJsonLdTransformer<JsonObject, VerifiableCredential> {

public JsonObjectToVerifiableCredentialTransformer() {
super(JsonObject.class, VerifiableCredential.class);
}

@Override
public @Nullable VerifiableCredential transform(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) {

var vcBuilder = Builder.newInstance();
vcBuilder.id(nodeId(jsonObject));
transformArrayOrObject(jsonObject.get(JsonLdKeywords.TYPE), Object.class, o -> vcBuilder.type(o.toString()), context);

visitProperties(jsonObject, (s, jsonValue) -> transformProperties(s, jsonValue, vcBuilder, context));
return vcBuilder.build();
}

private void transformProperties(String key, JsonValue jsonValue, Builder vcBuilder, TransformerContext context) {
switch (key) {
case VERIFIABLE_CREDENTIAL_DESCRIPTION_PROPERTY ->
vcBuilder.description(transformString(jsonValue, context));
case VERIFIABLE_CREDENTIAL_ISSUER_PROPERTY -> vcBuilder.issuer(parseIssuer(jsonValue, context));
case VERIFIABLE_CREDENTIAL_VALIDFROM_PROPERTY, VERIFIABLE_CREDENTIAL_ISSUANCEDATE_PROPERTY ->
vcBuilder.issuanceDate(parseDate(jsonValue, context));
case VERIFIABLE_CREDENTIAL_VALIDUNTIL_PROPERTY, VERIFIABLE_CREDENTIAL_EXPIRATIONDATE_PROPERTY ->
vcBuilder.expirationDate(parseDate(jsonValue, context));
case VERIFIABLE_CREDENTIAL_STATUS_PROPERTY ->
vcBuilder.credentialStatus(transformObject(jsonValue, CredentialStatus.class, context));
case VERIFIABLE_CREDENTIAL_SUBJECT_PROPERTY ->
vcBuilder.credentialSubject(transformArray(jsonValue, CredentialSubject.class, context));
case VERIFIABLE_CREDENTIAL_NAME_PROPERTY -> vcBuilder.name(transformString(jsonValue, context));

default ->
context.reportProblem("Unknown property: %s type: %s".formatted(key, jsonValue.getValueType().name()));
}
}

private Instant parseDate(JsonValue jsonValue, TransformerContext context) {
var str = transformString(jsonValue, context);
return Instant.parse(str);
}

private Object parseIssuer(JsonValue jsonValue, TransformerContext context) {
return transformString(jsonValue, context); //todo: handle the case where the issuer is an object
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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 jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonValue;
import org.eclipse.edc.identitytrust.model.VerifiableCredential;
import org.eclipse.edc.identitytrust.model.VerifiablePresentation;
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;

public class JsonObjectToVerifiablePresentationTransformer extends AbstractJsonLdTransformer<JsonObject, VerifiablePresentation> {
public JsonObjectToVerifiablePresentationTransformer() {
super(JsonObject.class, VerifiablePresentation.class);
}

@Override
public @Nullable VerifiablePresentation transform(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) {
var vcBuilder = VerifiablePresentation.Builder.newInstance();
vcBuilder.id(nodeId(jsonObject));
transformArrayOrObject(jsonObject.get(JsonLdKeywords.TYPE), Object.class, o -> vcBuilder.type(o.toString()), context);

visitProperties(jsonObject, (s, jsonValue) -> transformProperties(s, jsonValue, vcBuilder, context));
return vcBuilder.build();
}

private void transformProperties(String key, JsonValue jsonValue, VerifiablePresentation.Builder vpBuilder, TransformerContext context) {
switch (key) {
case VerifiablePresentation.VERIFIABLE_PRESENTATION_HOLDER_PROPERTY ->
vpBuilder.holder(transformString(jsonValue, context));
case VerifiablePresentation.VERIFIABLE_PRESENTATION_VC_PROPERTY ->
transformCredential(jsonValue, vpBuilder, context);
default ->
context.reportProblem("Unknown property: %s type: %s".formatted(key, jsonValue.getValueType().name()));
}
}

/**
* Credentials appear to be defined as "@graph", so thats what they're expanded to.
*/
private void transformCredential(JsonValue jsonValue, VerifiablePresentation.Builder vpBuilder, TransformerContext context) {
if (jsonValue instanceof JsonArray) {
var content = ((JsonArray) jsonValue).get(0);
if (content instanceof JsonObject) {
var credArray = ((JsonObject) content).getJsonArray(JsonLdKeywords.GRAPH);
transformArrayOrObject(credArray, VerifiableCredential.class, vpBuilder::credential, context);

}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.eclipse.edc.iam.identitytrust.IdentityTrustTransformExtension
Loading

0 comments on commit 9d39f5c

Please sign in to comment.