Skip to content

Commit d4f52fa

Browse files
authored
feat: flag for skipping the sts account provisioning (#1030)
feat: flag for skipping the sts account provisioning when creating the participant context
1 parent 0a03125 commit d4f52fa

6 files changed

Lines changed: 79 additions & 10 deletions

File tree

core/identity-hub-participants/src/main/java/org/eclipse/edc/identityhub/participantcontext/IdentityHubParticipantContextServiceImpl.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,19 @@ public ServiceResult<CreateParticipantContextResponse> createParticipantContext(
9898

9999
return createParticipantContext(context)
100100
.compose(this::createTokenAndStoreInVault)
101-
.compose((Function<String, ServiceResult<CreateParticipantContextResponse>>) apiKey -> stsAccountProvisioner.create(manifest)
102-
.map(accountInfo -> {
103-
if (accountInfo == null) {
104-
return new CreateParticipantContextResponse(apiKey, null, null);
105-
} else {
106-
return new CreateParticipantContextResponse(apiKey, accountInfo.clientId(), accountInfo.clientSecret());
107-
}
108-
}))
101+
.compose((Function<String, ServiceResult<CreateParticipantContextResponse>>) apiKey -> {
102+
if (!manifest.isProvisionStsAccount()) {
103+
return success(new CreateParticipantContextResponse(apiKey, null, null));
104+
}
105+
return stsAccountProvisioner.create(manifest)
106+
.map(accountInfo -> {
107+
if (accountInfo == null) {
108+
return new CreateParticipantContextResponse(apiKey, null, null);
109+
} else {
110+
return new CreateParticipantContextResponse(apiKey, accountInfo.clientId(), accountInfo.clientSecret());
111+
}
112+
});
113+
})
109114
.onSuccess(apiToken -> observable.invokeForEach(l -> l.created(context, manifest)));
110115
});
111116
}

core/identity-hub-participants/src/test/java/org/eclipse/edc/identityhub/participantcontext/IdentityHubIdentityHubParticipantContextServiceImplTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import static org.mockito.ArgumentMatchers.argThat;
5757
import static org.mockito.ArgumentMatchers.eq;
5858
import static org.mockito.Mockito.mock;
59+
import static org.mockito.Mockito.never;
5960
import static org.mockito.Mockito.times;
6061
import static org.mockito.Mockito.verify;
6162
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -142,6 +143,25 @@ void shouldCreateParticipantContext_withAccountInfo(boolean isActive) {
142143
});
143144
}
144145

146+
@Test
147+
void shouldSkipStsProvisioning_whenProvisionStsAccountIsFalse() {
148+
when(participantContextStore.create(any())).thenReturn(StoreResult.success());
149+
when(vault.storeSecret(anyString(), anyString(), anyString())).thenReturn(Result.success());
150+
151+
var ctx = createManifest()
152+
.provisionStsAccount(false)
153+
.build();
154+
155+
var result = participantContextService.createParticipantContext(ctx);
156+
157+
assertThat(result).isSucceeded().satisfies(response -> {
158+
assertThat(response.apiKey()).isNotBlank();
159+
assertThat(response.clientId()).isNull();
160+
assertThat(response.clientSecret()).isNull();
161+
});
162+
verify(stsAccountProvisioner, never()).create(any());
163+
}
164+
145165
@ParameterizedTest(name = "isActive: {0}")
146166
@ValueSource(booleans = {true, false})
147167
void createParticipantContext_withPublicKeyJwk(boolean isActive) {

e2e-tests/identity-api-tests/src/test/java/org/eclipse/edc/identityhub/tests/IdentityHubParticipantContextApiEndToEndTest.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import static org.hamcrest.Matchers.anyOf;
7676
import static org.hamcrest.Matchers.equalTo;
7777
import static org.hamcrest.Matchers.notNullValue;
78+
import static org.hamcrest.Matchers.nullValue;
7879
import static org.mockito.ArgumentMatchers.anyString;
7980
import static org.mockito.ArgumentMatchers.argThat;
8081
import static org.mockito.Mockito.mock;
@@ -173,6 +174,34 @@ void createNewUser_principalIsSuperuser(IdentityHub identityHub, EventRouter rou
173174
.allSatisfy(dd -> assertThat(dd.getVerificationMethod()).hasSize(1));
174175
}
175176

177+
178+
@Test
179+
void createNewUser_skipStsClientProvisioning(IdentityHub identityHub, EventRouter router) {
180+
var subscriber = mock(EventSubscriber.class);
181+
router.registerSync(ParticipantContextCreated.class, subscriber);
182+
183+
var manifest = createNewParticipant().provisionStsAccount(false).build();
184+
185+
identityHub.getIdentityEndpoint().baseRequest()
186+
.header(authorizeUser(SUPER_USER, identityHub))
187+
.contentType(ContentType.JSON)
188+
.body(manifest)
189+
.post("/v1beta/participants/")
190+
.then()
191+
.log().ifError()
192+
.statusCode(anyOf(equalTo(200), equalTo(204)))
193+
.body("clientId", nullValue())
194+
.body("apiKey", notNullValue())
195+
.body("clientSecret", nullValue());
196+
197+
verify(subscriber).on(argThat(env -> ((ParticipantContextCreated) env.getPayload()).getParticipantContextId().equals(manifest.getParticipantContextId())));
198+
199+
assertThat(identityHub.getKeyPairsForParticipant(manifest.getParticipantContextId())).hasSize(1);
200+
assertThat(identityHub.getDidForParticipant(manifest.getParticipantContextId())).hasSize(1)
201+
.allSatisfy(dd -> assertThat(dd.getVerificationMethod()).hasSize(1));
202+
}
203+
204+
176205
@Test
177206
void createNewUser_whenKeyPairActive(IdentityHub identityHub, EventRouter router) {
178207
var subscriber = mock(EventSubscriber.class);
@@ -519,7 +548,7 @@ void updateScopes(IdentityHub identityHub) {
519548
}
520549

521550
@ParameterizedTest(name = "Expect 403, role = {0}")
522-
@ValueSource(strings = { "some-role", "admin" })
551+
@ValueSource(strings = {"some-role", "admin"})
523552
void updateScopes_whenNotSuperuser(String role, IdentityHub identityHub) {
524553
var participantContextId = "some-user";
525554
var userAuth = authorizeUser(participantContextId, identityHub);

extensions/api/identity-api/identity-api-configuration/src/main/resources/identity-api-version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
"version": "1.0.0-alpha",
44
"urlPath": "/v1beta",
5-
"lastUpdated": "2026-06-12T13:00:00Z",
5+
"lastUpdated": "2026-06-30T13:00:00Z",
66
"maturity": null
77
}
88
]

extensions/api/identity-api/participant-context-api/src/test/java/org/eclipse/edc/identityhub/api/verifiablecredential/v1/unstable/model/ParticipantManifestTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ void verify_deserialize_singleValueAsArray() throws JsonProcessingException {
8585
assertThat(manifest.getKeys()).hasSize(1)
8686
.allSatisfy(kd -> assertThat(kd.getKeyId()).isEqualTo("key-1"));
8787
assertThat(manifest.getApiKeyAlias()).isEqualTo("test-alias");
88+
assertThat(manifest.isProvisionStsAccount()).isTrue();
8889
}
8990

9091
@Test

spi/participant-context-spi/src/main/java/org/eclipse/edc/identityhub/spi/participantcontext/model/ParticipantManifest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public class ParticipantManifest {
4040
private List<String> scopes = new ArrayList<>();
4141
private Set<Service> serviceEndpoints = new HashSet<>();
4242
private boolean isActive;
43+
private boolean provisionStsAccount = true;
4344
private String participantContextId;
4445
private String did;
4546
private String apiKeyAlias;
@@ -70,6 +71,14 @@ public boolean isActive() {
7071
return isActive;
7172
}
7273

74+
/**
75+
* Indicates whether an STS account should be provisioned for this participant during creation. When {@code false}, the STS account provisioning
76+
* phase is skipped entirely. Defaults to {@code true}.
77+
*/
78+
public boolean isProvisionStsAccount() {
79+
return provisionStsAccount;
80+
}
81+
7382
/**
7483
* The ID of the participant context. It is different from the stable DSP - dataspace ID or a DID. This could be a random ID.
7584
*/
@@ -133,6 +142,11 @@ public Builder active(boolean isActive) {
133142
return this;
134143
}
135144

145+
public Builder provisionStsAccount(boolean provisionStsAccount) {
146+
manifest.provisionStsAccount = provisionStsAccount;
147+
return this;
148+
}
149+
136150
public Builder participantContextId(String participantContextId) {
137151
manifest.participantContextId = participantContextId;
138152
return this;

0 commit comments

Comments
 (0)