Skip to content

Commit

Permalink
feat: implement ParticipantContextEventCoordinator (#265)
Browse files Browse the repository at this point in the history
* renamed event listener impls to *EventPublisher

* feat: implement ParticipantContextEventCoordinator

* run ci

* DEPENDENCIES
  • Loading branch information
paullatzelsperger authored Feb 7, 2024
1 parent f0a22c7 commit 13e6bde
Show file tree
Hide file tree
Showing 12 changed files with 263 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,8 @@ public ServiceResult<Void> removeService(String did, String serviceId) {
@Override
public <E extends Event> void on(EventEnvelope<E> eventEnvelope) {
var payload = eventEnvelope.getPayload();
if (payload instanceof ParticipantContextCreated event) {
created(event);
} else if (payload instanceof ParticipantContextUpdated event) {
monitor.debug("Received a [%s] event".formatted(payload.getClass().getSimpleName()));
if (payload instanceof ParticipantContextUpdated event) {
updated(event);
} else if (payload instanceof ParticipantContextDeleted event) {
deleted(event);
Expand All @@ -224,7 +223,7 @@ public <E extends Event> void on(EventEnvelope<E> eventEnvelope) {
} else if (payload instanceof KeyPairRevoked event) {
keypairRevoked(event);
} else {
monitor.warning("KeyPairServiceImpl Received an event with unexpected payload type: %s".formatted(payload.getClass()));
monitor.warning("Received event with unexpected payload type: %s".formatted(payload.getClass()));
}
}

Expand All @@ -246,6 +245,10 @@ private void keypairRevoked(KeyPairRevoked event) {

private void keypairAdded(KeyPairAdded event) {
var didResources = findByParticipantId(event.getParticipantId());
if (didResources.isEmpty()) {
monitor.warning("No DidResources were found for participant '%s'. No updated will be performed.".formatted(event.getParticipantId()));
return;
}
var serialized = event.getPublicKeySerialized();
var publicKey = keyParserRegistry.parse(serialized);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import org.eclipse.edc.identithub.did.spi.store.DidResourceStore;
import org.eclipse.edc.identityhub.spi.events.keypair.KeyPairAdded;
import org.eclipse.edc.identityhub.spi.events.keypair.KeyPairRevoked;
import org.eclipse.edc.identityhub.spi.events.participant.ParticipantContextCreated;
import org.eclipse.edc.identityhub.spi.events.participant.ParticipantContextDeleted;
import org.eclipse.edc.identityhub.spi.events.participant.ParticipantContextUpdated;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
Expand Down Expand Up @@ -64,8 +63,7 @@ public DidDocumentPublisherRegistry getDidPublisherRegistry() {

@Provider
public DidDocumentService createDidDocumentService(ServiceExtensionContext context) {
var service = new DidDocumentServiceImpl(transactionContext, didResourceStore, getDidPublisherRegistry(), context.getMonitor(), keyParserRegistry);
eventRouter.registerSync(ParticipantContextCreated.class, service);
var service = new DidDocumentServiceImpl(transactionContext, didResourceStore, getDidPublisherRegistry(), context.getMonitor().withPrefix("DidDocumentService"), keyParserRegistry);
eventRouter.registerSync(ParticipantContextUpdated.class, service);
eventRouter.registerSync(ParticipantContextDeleted.class, service);
eventRouter.registerSync(KeyPairAdded.class, service);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@

import java.time.Clock;

public class KeyPairEventListenerImpl implements KeyPairEventListener {
public class KeyPairEventPublisher implements KeyPairEventListener {
private final Clock clock;
private final EventRouter eventRouter;

public KeyPairEventListenerImpl(Clock clock, EventRouter eventRouter) {
public KeyPairEventPublisher(Clock clock, EventRouter eventRouter) {
this.clock = clock;
this.eventRouter = eventRouter;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
package org.eclipse.edc.identityhub.keypairs;

import org.eclipse.edc.identityhub.spi.KeyPairService;
import org.eclipse.edc.identityhub.spi.events.diddocument.DidDocumentPublished;
import org.eclipse.edc.identityhub.spi.events.keypair.KeyPairObservable;
import org.eclipse.edc.identityhub.spi.events.participant.ParticipantContextCreated;
import org.eclipse.edc.identityhub.spi.events.participant.ParticipantContextDeleted;
import org.eclipse.edc.identityhub.spi.store.KeyPairResourceStore;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
Expand Down Expand Up @@ -54,18 +52,16 @@ public String name() {

@Provider
public KeyPairService createParticipantService(ServiceExtensionContext context) {
var service = new KeyPairServiceImpl(keyPairResourceStore, vault, context.getMonitor(), keyPairObservable());
eventRouter.registerSync(ParticipantContextCreated.class, service);
var service = new KeyPairServiceImpl(keyPairResourceStore, vault, context.getMonitor().withPrefix("KeyPairService"), keyPairObservable());
eventRouter.registerSync(ParticipantContextDeleted.class, service);
eventRouter.registerSync(DidDocumentPublished.class, service);
return service;
}

@Provider
public KeyPairObservable keyPairObservable() {
if (observable == null) {
observable = new KeyPairObservableImpl();
observable.registerListener(new KeyPairEventListenerImpl(clock, eventRouter));
observable.registerListener(new KeyPairEventPublisher(clock, eventRouter));
}
return observable;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,11 @@ public ServiceResult<Collection<KeyPairResource>> query(QuerySpec querySpec) {
@Override
public <E extends Event> void on(EventEnvelope<E> eventEnvelope) {
var payload = eventEnvelope.getPayload();
if (payload instanceof ParticipantContextCreated created) {
created(created);
} else if (payload instanceof ParticipantContextDeleted deleted) {
monitor.debug("Received a [%s] event".formatted(payload.getClass().getSimpleName()));
if (payload instanceof ParticipantContextDeleted deleted) {
deleted(deleted);
} else {
monitor.warning("KeyPairServiceImpl Received event with unexpected payload type: %s".formatted(payload.getClass()));
monitor.warning("Received event with unexpected payload type: %s".formatted(payload.getClass()));
}
}

Expand Down Expand Up @@ -180,7 +179,7 @@ private Result<String> generateOrGetKey(KeyDescriptor keyDescriptor) {
if (keyPair.failed()) {
return keyPair.mapTo();
}
var privateJwk = CryptoConverter.createJwk(keyPair.getContent());
var privateJwk = CryptoConverter.createJwk(keyPair.getContent(), keyDescriptor.getKeyId());
publicKeySerialized = privateJwk.toPublicJWK().toJSONString();
vault.storeSecret(keyDescriptor.getPrivateKeyAlias(), privateJwk.toJSONString());
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2024 Metaform Systems, Inc.
*
* 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:
* Metaform Systems, Inc. - initial API and implementation
*
*/

package org.eclipse.edc.identityhub.participantcontext;

import org.eclipse.edc.iam.did.spi.document.DidDocument;
import org.eclipse.edc.identithub.did.spi.DidDocumentService;
import org.eclipse.edc.identityhub.spi.KeyPairService;
import org.eclipse.edc.identityhub.spi.events.participant.ParticipantContextCreated;
import org.eclipse.edc.spi.event.Event;
import org.eclipse.edc.spi.event.EventEnvelope;
import org.eclipse.edc.spi.event.EventSubscriber;
import org.eclipse.edc.spi.monitor.Monitor;

import static org.eclipse.edc.spi.result.ServiceResult.success;

/**
* Coordinates {@link ParticipantContextCreated} events. More specifically, it coordinates the sequence, in which the following actions are performed:
* <ul>
* <li>Create DID Document</li>
* <li>Optional: publish DID document</li>
* <li>Add a KeyPair</li>
* </ul>
* To that end, the {@link ParticipantContextEventCoordinator} directly collaborates with the {@link KeyPairService} and the {@link DidDocumentService}.
* <p>
* Please note that once this initial sequence is executed, every collaborator service emits their events as per their event contract.
* For example, once a KeyPair is added, the {@link KeyPairService} will emit a {@link org.eclipse.edc.identityhub.spi.events.keypair.KeyPairAdded} event. The {@link DidDocumentService}
* can then react to this event by updating the DID Document.
*/
public class ParticipantContextEventCoordinator implements EventSubscriber {
private final Monitor monitor;
private final DidDocumentService didDocumentService;
private final KeyPairService keyPairService;

public ParticipantContextEventCoordinator(Monitor monitor, DidDocumentService didDocumentService, KeyPairService keyPairService) {
this.monitor = monitor;
this.didDocumentService = didDocumentService;
this.keyPairService = keyPairService;
}

@Override
public <E extends Event> void on(EventEnvelope<E> event) {
var payload = event.getPayload();
if (payload instanceof ParticipantContextCreated createdEvent) {
var manifest = createdEvent.getManifest();
var doc = DidDocument.Builder.newInstance()
.id(manifest.getDid())
.service(manifest.getServiceEndpoints().stream().toList())
// updating and adding a verification method happens as a result of the KeyPairAddedEvent
.build();

didDocumentService.store(doc, manifest.getParticipantId())
.compose(u -> manifest.isActive() ? didDocumentService.publish(doc.getId()) : success())
// adding the keypair event will cause the DidDocumentService to update the DID.
.compose(u -> keyPairService.addKeyPair(createdEvent.getParticipantId(), createdEvent.getManifest().getKey(), true))
.onFailure(f -> monitor.warning("%s".formatted(f.getFailureDetail())));

} else {
monitor.warning("Received event with unexpected payload type: %s".formatted(payload.getClass()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@

import java.time.Clock;

public class ParticipantContextListenerImpl implements ParticipantContextListener {
public class ParticipantContextEventPublisher implements ParticipantContextListener {
private final Clock clock;
private final EventRouter eventRouter;

public ParticipantContextListenerImpl(Clock clock, EventRouter eventRouter) {
public ParticipantContextEventPublisher(Clock clock, EventRouter eventRouter) {
this.clock = clock;
this.eventRouter = eventRouter;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
package org.eclipse.edc.identityhub.participantcontext;

import org.eclipse.edc.identithub.did.spi.DidDocumentService;
import org.eclipse.edc.identityhub.spi.KeyPairService;
import org.eclipse.edc.identityhub.spi.ParticipantContextService;
import org.eclipse.edc.identityhub.spi.events.participant.ParticipantContextCreated;
import org.eclipse.edc.identityhub.spi.events.participant.ParticipantContextObservable;
import org.eclipse.edc.identityhub.spi.store.ParticipantContextStore;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
Expand All @@ -25,6 +27,7 @@
import org.eclipse.edc.spi.security.KeyParserRegistry;
import org.eclipse.edc.spi.security.Vault;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.transaction.spi.TransactionContext;

import java.time.Clock;
Expand All @@ -46,6 +49,8 @@ public class ParticipantContextExtension implements ServiceExtension {
@Inject
private DidDocumentService didDocumentService;
@Inject
private KeyPairService keyPairService;
@Inject
private Clock clock;
@Inject
private EventRouter eventRouter;
Expand All @@ -57,6 +62,14 @@ public String name() {
return NAME;
}

@Override
public void initialize(ServiceExtensionContext context) {
var coordinator = new ParticipantContextEventCoordinator(context.getMonitor().withPrefix("ParticipantContextEventCoordinator"),
didDocumentService, keyPairService);

eventRouter.registerSync(ParticipantContextCreated.class, coordinator);
}

@Provider
public ParticipantContextService createParticipantService() {
return new ParticipantContextServiceImpl(participantContextStore, vault, transactionContext, participantContextObservable());
Expand All @@ -66,7 +79,7 @@ public ParticipantContextService createParticipantService() {
public ParticipantContextObservable participantContextObservable() {
if (participantContextObservable == null) {
participantContextObservable = new ParticipantContextObservableImpl();
participantContextObservable.registerListener(new ParticipantContextListenerImpl(clock, eventRouter));
participantContextObservable.registerListener(new ParticipantContextEventPublisher(clock, eventRouter));
}
return participantContextObservable;
}
Expand Down
Loading

0 comments on commit 13e6bde

Please sign in to comment.