Skip to content

Commit

Permalink
feat: allow customization of the STS Account's secret_alias
Browse files Browse the repository at this point in the history
  • Loading branch information
paullatzelsperger committed Oct 2, 2024
1 parent 46eaf00 commit 887bde7
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import static io.restassured.http.ContentType.JSON;
import static java.util.stream.IntStream.range;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.edc.identityhub.spi.participantcontext.AccountProvisioner.CLIENT_SECRET_PROPERTY;
import static org.eclipse.edc.identityhub.tests.fixtures.IdentityHubEndToEndTestContext.SUPER_USER;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.equalTo;
Expand Down Expand Up @@ -197,6 +198,32 @@ void createNewUser_whenKeyPairActive(IdentityHubEndToEndTestContext context, Eve

}

@Test
void createNewUser_withCustomSecretAlias(IdentityHubEndToEndTestContext context, Vault vault) {
var apikey = context.createSuperUser();

var participantId = UUID.randomUUID().toString();
var manifest = context.createNewParticipant()
.participantId(participantId)
.active(true)
.did("did:web:" + participantId)
.key(context.createKeyDescriptor().active(true).build())
.property(CLIENT_SECRET_PROPERTY, "test-alias")
.build();

context.getIdentityApiEndpoint().baseRequest()
.header(new Header("x-api-key", apikey))
.contentType(ContentType.JSON)
.body(manifest)
.post("/v1alpha/participants/")
.then()
.log().ifError()
.statusCode(anyOf(equalTo(200), equalTo(204)))
.body(notNullValue());

assertThat(vault.resolveSecret("test-alias")).isNotNull();
}

@Test
void createNewUser_whenKeyPairNotActive(IdentityHubEndToEndTestContext context, EventRouter router) {
var subscriber = mock(EventSubscriber.class);
Expand Down Expand Up @@ -304,7 +331,6 @@ void createNewUser_whenDidAlreadyExists_expect409(IdentityHubEndToEndTestContext
verify(subscriber, never()).on(argThat(env -> ((ParticipantContextCreated) env.getPayload()).getParticipantId().equals(manifest.getParticipantId())));
}


@Test
void createNewUser_andNotActive_shouldNotPublishDid(IdentityHubEndToEndTestContext context, DidResourceStore didResourceStore, DidDocumentPublisherRegistry publisherRegistry) {
var apikey = context.createSuperUser();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@

import java.util.Objects;

import static java.util.Optional.ofNullable;

/**
* AccountProvisioner, that synchronizes the {@link org.eclipse.edc.identityhub.spi.participantcontext.model.ParticipantContext} object
* to {@link StsAccount} entries. That means, when a participant is created, this provisioner takes care of creating a corresponding
Expand Down Expand Up @@ -81,7 +83,9 @@ public <E extends Event> void on(EventEnvelope<E> event) {
@Override
public ServiceResult<AccountInfo> create(ParticipantManifest manifest) {
return transactionContext.execute(() -> {
var secretAlias = manifest.getParticipantId() + "-sts-client-secret";
var secretAlias = ofNullable(manifest.getProperty(CLIENT_SECRET_PROPERTY))
.map(Object::toString)
.orElseGet(() -> manifest.getParticipantId() + "-sts-client-secret");

var client = StsAccount.Builder.newInstance()
.id(manifest.getParticipantId())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.edc.identityhub.spi.keypair.events.KeyPairRevoked;
import org.eclipse.edc.identityhub.spi.keypair.events.KeyPairRotated;
import org.eclipse.edc.identityhub.spi.keypair.model.KeyPairResource;
import org.eclipse.edc.identityhub.spi.participantcontext.AccountProvisioner;
import org.eclipse.edc.identityhub.spi.participantcontext.events.ParticipantContextDeleted;
import org.eclipse.edc.identityhub.spi.participantcontext.model.KeyDescriptor;
import org.eclipse.edc.identityhub.spi.participantcontext.model.ParticipantManifest;
Expand All @@ -40,6 +41,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
Expand Down Expand Up @@ -68,10 +70,28 @@ void create() {
assertThat(accountProvisioner.create(createManifest().build())).isSucceeded();

verify(stsAccountStore).create(any());
verify(vault).storeSecret(anyString(), argThat(secret -> UUID.fromString(secret) != null));
verify(vault).storeSecret(anyString(), argThat(secret -> {
UUID.fromString(secret);
return true;
}));
verifyNoInteractions(keyPairService, didDocumentService);
}

@Test
void create_withCustomSecretAlias() {
when(stsAccountStore.create(any())).thenReturn(StoreResult.success(createStsClient().build()));
when(vault.storeSecret(anyString(), anyString())).thenReturn(Result.success());

assertThat(accountProvisioner.create(createManifest()
.property(AccountProvisioner.CLIENT_SECRET_PROPERTY, "test-alias")
.build())).isSucceeded();

verify(stsAccountStore).create(any());
verify(vault).storeSecret(eq("test-alias"), anyString());
verifyNoInteractions(keyPairService, didDocumentService);
verifyNoMoreInteractions(vault);
}

@Test
void create_whenClientAlreadyExists() {
when(stsAccountStore.create(any())).thenReturn(StoreResult.alreadyExists("foo"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@
import org.eclipse.edc.spi.result.ServiceResult;

public interface AccountProvisioner {
String CLIENT_SECRET_PROPERTY = "clientSecret";

ServiceResult<AccountInfo> create(ParticipantManifest manifest);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@
import org.eclipse.edc.iam.did.spi.document.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Manifest (=recipe) for creating the {@link ParticipantContext}.
*/
@JsonDeserialize(builder = ParticipantManifest.Builder.class)
public class ParticipantManifest {
private Map<String, Object> additionalProperties = new HashMap<>();
private List<String> roles = new ArrayList<>();
private Set<Service> serviceEndpoints = new HashSet<>();
private boolean isActive;
Expand All @@ -39,6 +42,10 @@ public class ParticipantManifest {
private ParticipantManifest() {
}

public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
}

/**
* An optional list of service endpoints that should get published in the DID document, e.g. resolution endpoints, storage endpoints, etc.
*/
Expand Down Expand Up @@ -80,6 +87,10 @@ public List<String> getRoles() {
return roles;
}

public Object getProperty(String key) {
return additionalProperties.get(key);
}

@JsonPOJOBuilder(withPrefix = "")
public static final class Builder {

Expand Down Expand Up @@ -129,6 +140,16 @@ public Builder did(String did) {
return this;
}

public Builder property(String key, Object value) {
manifest.additionalProperties.put(key, value);
return this;
}

public Builder additionalProperties(Map<String, Object> properties) {
manifest.additionalProperties = properties;
return this;
}

public ParticipantManifest build() {
return manifest;
}
Expand Down

0 comments on commit 887bde7

Please sign in to comment.