Skip to content

Commit

Permalink
feat: implement PresentationGenerator (#172)
Browse files Browse the repository at this point in the history
* feat: implement PresentationGenerator for JWT-VP and LDP-VP

* cleanup

* checkstyle

* DEPENDENCIES

* added tests, and some cleamup

* add presentation submission context

* Update core/identity-hub-core/src/main/java/org/eclipse/edc/identityhub/core/creators/JwtPresentationCreator.java

Co-authored-by: Enrico Risa <enrico.risa@gmail.com>

---------

Co-authored-by: Enrico Risa <enrico.risa@gmail.com>
  • Loading branch information
paullatzelsperger and wolf4ood committed Nov 15, 2023
1 parent ffb40a0 commit 3d5c122
Show file tree
Hide file tree
Showing 29 changed files with 2,770 additions and 28 deletions.
14 changes: 7 additions & 7 deletions DEPENDENCIES
Original file line number Diff line number Diff line change
Expand Up @@ -138,24 +138,24 @@ maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.15, Apache-2.0, a
maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.18, Apache-2.0, approved, #5929
maven/mavencentral/io.swagger.core.v3/swagger-core/2.2.15, Apache-2.0, approved, #9265
maven/mavencentral/io.swagger.core.v3/swagger-core/2.2.8, Apache-2.0, approved, #9265
maven/mavencentral/io.swagger.core.v3/swagger-integration-jakarta/2.2.15, Apache-2.0, approved, clearlydefined
maven/mavencentral/io.swagger.core.v3/swagger-integration-jakarta/2.2.18, , restricted, clearlydefined
maven/mavencentral/io.swagger.core.v3/swagger-integration-jakarta/2.2.15, Apache-2.0, approved, #11475
maven/mavencentral/io.swagger.core.v3/swagger-integration-jakarta/2.2.18, Apache-2.0, approved, #11475
maven/mavencentral/io.swagger.core.v3/swagger-integration/2.2.15, Apache-2.0, approved, #10352
maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2-jakarta/2.2.15, Apache-2.0, approved, clearlydefined
maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2-jakarta/2.2.18, , restricted, clearlydefined
maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2-jakarta/2.2.15, Apache-2.0, approved, #11477
maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2-jakarta/2.2.18, Apache-2.0, approved, #11477
maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2/2.2.15, Apache-2.0, approved, #9814
maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.15, Apache-2.0, approved, #5919
maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.18, Apache-2.0, approved, #5919
maven/mavencentral/io.swagger.core.v3/swagger-models/2.2.15, Apache-2.0, approved, #10353
maven/mavencentral/io.swagger.core.v3/swagger-models/2.2.8, Apache-2.0, approved, #10353
maven/mavencentral/io.swagger.parser.v3/swagger-parser-core/2.1.10, None, restricted, #11314
maven/mavencentral/io.swagger.parser.v3/swagger-parser-core/2.1.10, None, restricted, #11478
maven/mavencentral/io.swagger.parser.v3/swagger-parser-v2-converter/2.1.10, Apache-2.0, approved, #9330
maven/mavencentral/io.swagger.parser.v3/swagger-parser-v3/2.1.10, Apache-2.0, approved, #9323
maven/mavencentral/io.swagger.parser.v3/swagger-parser/2.1.10, None, restricted, #11316
maven/mavencentral/io.swagger/swagger-annotations/1.6.9, Apache-2.0, approved, #3792
maven/mavencentral/io.swagger/swagger-compat-spec-parser/1.0.64, None, restricted, #11282
maven/mavencentral/io.swagger/swagger-compat-spec-parser/1.0.64, None, restricted, #11479
maven/mavencentral/io.swagger/swagger-core/1.6.9, Apache-2.0, approved, #4358
maven/mavencentral/io.swagger/swagger-models/1.6.9, LicenseRef-scancode-proprietary-license, restricted, #11330
maven/mavencentral/io.swagger/swagger-models/1.6.9, LicenseRef-scancode-proprietary-license, restricted, #11476
maven/mavencentral/io.swagger/swagger-parser/1.0.64, Apache-2.0, approved, #4359
maven/mavencentral/jakarta.activation/jakarta.activation-api/1.2.1, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf
maven/mavencentral/jakarta.activation/jakarta.activation-api/2.1.0, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf
Expand Down
8 changes: 8 additions & 0 deletions core/identity-hub-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,18 @@ dependencies {
api(project(":spi:identity-hub-store-spi"))
implementation(libs.edc.core.connector) // for the CriterionToPredicateConverterImpl
implementation(libs.edc.spi.jsonld)
implementation(libs.edc.spi.jsonld)
implementation(libs.edc.ext.jsonld) // for the JSON-LD mapper
implementation(libs.edc.iatp.service) // JWT validator
implementation(libs.edc.core.crypto) // JWT verifier
implementation(libs.edc.jws2020)
implementation(libs.edc.vc.ldp)
implementation(libs.edc.util)
implementation(libs.nimbus.jwt)

testImplementation(libs.edc.junit)
testImplementation(libs.edc.ext.jsonld)
testImplementation(testFixtures(project(":spi:identity-hub-spi")))
testImplementation(testFixtures(libs.edc.vc.jwt)) // JWT generator
testImplementation(libs.edc.identity.did.crypto) // EC private key wrapper
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,39 @@

package org.eclipse.edc.identityhub;

import com.apicatalog.ld.signature.SignatureSuite;
import org.eclipse.edc.identityhub.defaults.EdcScopeToCriterionTransformer;
import org.eclipse.edc.identityhub.defaults.InMemoryCredentialStore;
import org.eclipse.edc.identityhub.spi.ScopeToCriterionTransformer;
import org.eclipse.edc.identityhub.spi.generator.PresentationGenerator;
import org.eclipse.edc.identityhub.spi.model.IdentityHubConstants;
import org.eclipse.edc.identityhub.spi.store.CredentialStore;
import org.eclipse.edc.identitytrust.verification.SignatureSuiteRegistry;
import org.eclipse.edc.jsonld.util.JacksonJsonLd;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Provider;
import org.eclipse.edc.security.signature.jws2020.JwsSignature2020Suite;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;

@Extension("Default Services Extension")
import java.util.Collection;
import java.util.Map;

import static org.eclipse.edc.identityhub.DefaultServicesExtension.NAME;

@Extension(NAME)
public class DefaultServicesExtension implements ServiceExtension {
@Provider(isDefault = true)
public CredentialStore createInMemStore() {
return new InMemoryCredentialStore();

public static final String NAME = "IdentityHub Default Services Extension";

@Override
public String name() {
return NAME;
}

@Provider(isDefault = true)
public PresentationGenerator createPresentationGenerator(ServiceExtensionContext context) {
context.getMonitor().warning(" #### Creating a default NOOP PresentationGenerator, that will always return 'null'!");
return (credentials, presentationDefinition) -> null;
public CredentialStore createInMemStore() {
return new InMemoryCredentialStore();

}

@Provider(isDefault = true)
Expand All @@ -45,4 +56,25 @@ public ScopeToCriterionTransformer createScopeTransformer(ServiceExtensionContex
return new EdcScopeToCriterionTransformer();
}

@Provider(isDefault = true)
public SignatureSuiteRegistry createSignatureSuiteRegistry() {
return new SignatureSuiteRegistry() {
private final Map<String, SignatureSuite> registry = Map.of(IdentityHubConstants.JWS_2020_SIGNATURE_SUITE, new JwsSignature2020Suite(JacksonJsonLd.createObjectMapper()));

@Override
public void register(String w3cIdentifier, SignatureSuite suite) {

}

@Override
public SignatureSuite getForId(String w3cIdentifier) {
return registry.get(w3cIdentifier);
}

@Override
public Collection<SignatureSuite> getAllSuites() {
return registry.values();
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,58 +17,83 @@
import org.eclipse.edc.iam.did.spi.key.PublicKeyWrapper;
import org.eclipse.edc.iam.did.spi.resolution.DidResolverRegistry;
import org.eclipse.edc.iam.identitytrust.validation.SelfIssuedIdTokenValidator;
import org.eclipse.edc.identityhub.core.creators.JwtPresentationCreator;
import org.eclipse.edc.identityhub.core.creators.LdpPresentationCreator;
import org.eclipse.edc.identityhub.spi.ScopeToCriterionTransformer;
import org.eclipse.edc.identityhub.spi.generator.PresentationCreatorRegistry;
import org.eclipse.edc.identityhub.spi.generator.PresentationGenerator;
import org.eclipse.edc.identityhub.spi.model.IdentityHubConstants;
import org.eclipse.edc.identityhub.spi.resolution.CredentialQueryResolver;
import org.eclipse.edc.identityhub.spi.store.CredentialStore;
import org.eclipse.edc.identityhub.spi.verification.AccessTokenVerifier;
import org.eclipse.edc.identityhub.token.verification.AccessTokenVerifierImpl;
import org.eclipse.edc.identitytrust.model.CredentialFormat;
import org.eclipse.edc.identitytrust.validation.JwtValidator;
import org.eclipse.edc.identitytrust.verification.JwtVerifier;
import org.eclipse.edc.identitytrust.verification.SignatureSuiteRegistry;
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.runtime.metamodel.annotation.Provider;
import org.eclipse.edc.runtime.metamodel.annotation.Setting;
import org.eclipse.edc.spi.security.PrivateKeyResolver;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.verifiablecredentials.linkeddata.LdpIssuer;
import org.eclipse.edc.verification.jwt.SelfIssuedIdTokenVerifier;

import java.net.URISyntaxException;
import java.time.Clock;

import static org.eclipse.edc.identityhub.core.CoreServicesExtension.NAME;
import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.DID_CONTEXT_URL;
import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.IATP_CONTEXT_URL;
import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.JWS_2020_URL;
import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.PRESENTATION_EXCHANGE_URL;
import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.PRESENTATION_SUBMISSION_URL;
import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.W3C_CREDENTIALS_URL;

/**
* This extension provides some core services for the IdentityHub, such as:
* <ul>
* <li>an {@link AccessTokenVerifier}</li>
* <li>a {@link JwtValidator}</li>
* <li>a {@link JwtVerifier}</li>
* </ul>
* This extension provides core services for the IdentityHub that are not intended to be user-replaceable.
*/
@Extension(value = "Core Services extension")
@Extension(value = NAME)
public class CoreServicesExtension implements ServiceExtension {

public static final String NAME = "IdentityHub Core Services Extension";
@Setting(value = "Configure this IdentityHub's DID", required = true)
public static final String OWN_DID_PROPERTY = "edc.ih.iam.id";
public static final String PRESENTATION_EXCHANGE_V_1_JSON = "presentation-exchange.v1.json";
public static final String PRESENTATION_QUERY_V_08_JSON = "presentation-query.v08.json";
public static final String PRESENTATION_SUBMISSION_V1_JSON = "presentation-submission.v1.json";
public static final String DID_JSON = "did.json";
public static final String JWS_2020_JSON = "jws2020.json";
public static final String CREDENTIALS_V_1_JSON = "credentials.v1.json";
private final String defaultSuite = IdentityHubConstants.JWS_2020_SIGNATURE_SUITE;
private PresentationCreatorRegistryImpl presentationCreatorRegistry;
private JwtVerifier jwtVerifier;
private JwtValidator jwtValidator;

@Inject
private DidResolverRegistry didResolverRegistry;

@Inject
private PublicKeyWrapper identityHubPublicKey;

@Inject
private JsonLd jsonLd;

@Inject
private CredentialStore credentialStore;

@Inject
private ScopeToCriterionTransformer transformer;
@Inject
private PrivateKeyResolver privateKeyResolver;
@Inject
private Clock clock;
@Inject
private SignatureSuiteRegistry signatureSuiteRegistry;

@Override
public String name() {
return NAME;
}

@Override
public void initialize(ServiceExtensionContext context) {
Expand Down Expand Up @@ -102,6 +127,25 @@ public CredentialQueryResolver createCredentialQueryResolver() {
return new CredentialQueryResolverImpl(credentialStore, transformer);
}

@Provider
public PresentationCreatorRegistry presentationCreatorRegistry(ServiceExtensionContext context) {
if (presentationCreatorRegistry == null) {
presentationCreatorRegistry = new PresentationCreatorRegistryImpl();
presentationCreatorRegistry.addCreator(new JwtPresentationCreator(privateKeyResolver, clock, getOwnDid(context)), CredentialFormat.JWT);

var ldpIssuer = LdpIssuer.Builder.newInstance().jsonLd(jsonLd).monitor(context.getMonitor()).build();
presentationCreatorRegistry.addCreator(new LdpPresentationCreator(privateKeyResolver, getOwnDid(context), signatureSuiteRegistry, defaultSuite, ldpIssuer, null),
CredentialFormat.JSON_LD);
}
return presentationCreatorRegistry;
}

@Provider
public PresentationGenerator presentationGenerator(ServiceExtensionContext context) {
return new PresentationGeneratorImpl(CredentialFormat.JSON_LD, presentationCreatorRegistry, context.getMonitor());
}


private String getOwnDid(ServiceExtensionContext context) {
return context.getConfig().getString(OWN_DID_PROPERTY);
}
Expand All @@ -110,6 +154,10 @@ private void cacheContextDocuments(ClassLoader classLoader) {
try {
jsonLd.registerCachedDocument(PRESENTATION_EXCHANGE_URL, classLoader.getResource(PRESENTATION_EXCHANGE_V_1_JSON).toURI());
jsonLd.registerCachedDocument(IATP_CONTEXT_URL, classLoader.getResource(PRESENTATION_QUERY_V_08_JSON).toURI());
jsonLd.registerCachedDocument(DID_CONTEXT_URL, classLoader.getResource(DID_JSON).toURI());
jsonLd.registerCachedDocument(JWS_2020_URL, classLoader.getResource(JWS_2020_JSON).toURI());
jsonLd.registerCachedDocument(W3C_CREDENTIALS_URL, classLoader.getResource(CREDENTIALS_V_1_JSON).toURI());
jsonLd.registerCachedDocument(PRESENTATION_SUBMISSION_URL, classLoader.getResource(PRESENTATION_SUBMISSION_V1_JSON).toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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.identityhub.core;

import org.eclipse.edc.identityhub.spi.generator.PresentationCreator;
import org.eclipse.edc.identityhub.spi.generator.PresentationCreatorRegistry;
import org.eclipse.edc.identitytrust.model.CredentialFormat;
import org.eclipse.edc.identitytrust.model.VerifiableCredentialContainer;
import org.eclipse.edc.spi.EdcException;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.Optional.ofNullable;

public class PresentationCreatorRegistryImpl implements PresentationCreatorRegistry {

private final Map<CredentialFormat, PresentationCreator<?>> creators = new HashMap<>();
private final Map<CredentialFormat, String> keyIds = new HashMap<>();

@Override
public void addCreator(PresentationCreator<?> creator, CredentialFormat format) {
creators.put(format, creator);
}

@Override
public <T> T createPresentation(List<VerifiableCredentialContainer> credentials, CredentialFormat format) {
var creator = ofNullable(creators.get(format)).orElseThrow(() -> new EdcException("No PresentationCreator was found for CredentialFormat %s".formatted(format)));
var keyId = ofNullable(keyIds.get(format)).orElseThrow(() -> new EdcException("No key ID was registered for CredentialFormat %s".formatted(format)));

return (T) creator.createPresentation(credentials, keyId);
}

@Override
public void addKeyId(String keyId, CredentialFormat format) {
keyIds.put(format, keyId);
}
}
Loading

0 comments on commit 3d5c122

Please sign in to comment.