From f3b322a1015dfc9f9a5654f05af080ae9309222a Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:14:11 +0200 Subject: [PATCH] feat: add STS Accounts API (#4493) * rename `StsClient` -> `StsAccount` * add scaffolding for new StsAccountsApi + controller * add tests for StsAccountController * Add required methods to the `StsAccountServiceImpl` * add extension test + auth * update copyright [skip ci] * javadoc [skip ci] * set proper version * Apply suggestions from code review Co-authored-by: Enrico Risa * fix javadoc * DEPENDENCIES * --amend --------- Co-authored-by: Enrico Risa --- .../identity-trust-sts/build.gradle.kts | 1 + .../build.gradle.kts | 42 +++ .../sts/accounts/StsAccountsApi.java | 108 +++++++ .../StsAccountsApiConfigurationExtension.java | 90 ++++++ .../sts/accounts/StsAccountsApiExtension.java | 75 +++++ .../controller/StsAccountsApiController.java | 90 ++++++ .../accounts/model/StsAccountCreation.java | 28 ++ .../accounts/model/UpdateClientSecret.java | 26 ++ ...rg.eclipse.edc.spi.system.ServiceExtension | 16 ++ .../resources/sts-accounts-api-version.json | 7 + .../accounts/StsAccountsApiExtensionTest.java | 101 +++++++ .../StsAccountsApiControllerTest.java | 270 ++++++++++++++++++ .../sts/SecureTokenServiceApiExtension.java | 4 +- .../SecureTokenServiceApiController.java | 12 +- .../sts/StsApiConfigurationExtensionTest.java | 1 - .../SecureServiceTokenApiControllerTest.java | 8 +- .../StsClientConfigurationExtension.java | 8 +- ...StsAccountConfigurationExtensionTest.java} | 16 +- .../sts/defaults/RandomStringGenerator.java | 51 ++++ .../defaults/StsDefaultServicesExtension.java | 26 +- .../defaults/StsDefaultStoresExtension.java | 8 +- .../service/StsAccountServiceImpl.java | 140 +++++++++ .../service/StsClientServiceImpl.java | 57 ---- .../StsClientTokenGeneratorServiceImpl.java | 14 +- ...tore.java => InMemoryStsAccountStore.java} | 34 +-- ...sAccountTokenIssuanceIntegrationTest.java} | 50 ++-- .../StsDefaultServicesExtensionTest.java | 4 +- .../StsDefaultStoresExtensionTest.java | 4 +- .../service/StsAccountServiceImplTest.java | 252 ++++++++++++++++ ...AccountTokenGeneratorServiceImplTest.java} | 8 +- .../service/StsClientServiceImplTest.java | 83 ------ ....java => InMemoryStsAccountStoreTest.java} | 12 +- .../embedded/EmbeddedSecureTokenService.java | 2 +- ...ientStore.java => SqlStsAccountStore.java} | 56 ++-- .../sts/store/SqlStsClientStoreExtension.java | 10 +- .../sts/store/schema/StsClientStatements.java | 6 +- .../schema/postgres/StsClientMapping.java | 4 +- ...a => SqlStsAccountStoreExtensionTest.java} | 8 +- ...eTest.java => SqlStsAccountStoreTest.java} | 12 +- resources/openapi/sts-accounts-api.version | 1 + settings.gradle.kts | 1 + .../model/{StsClient.java => StsAccount.java} | 26 +- ...a => StsAccountTokenAdditionalParams.java} | 16 +- .../sts/spi/service/StsAccountService.java | 123 ++++++++ .../spi/service/StsClientSecretGenerator.java | 33 +++ .../sts/spi/service/StsClientService.java | 53 ---- .../StsClientTokenGeneratorService.java | 12 +- .../service/StsTokenGenerationProvider.java | 8 +- ...sClientStore.java => StsAccountStore.java} | 34 +-- ...Base.java => StsAccountStoreTestBase.java} | 32 +-- .../sts/spi/store/fixtures/TestFunctions.java | 14 +- .../edc/web/spi/configuration/ApiContext.java | 1 + .../test/e2e/sts/api/StsEndToEndTestBase.java | 12 +- .../edc/test/e2e/versionapi/Runtimes.java | 4 +- 54 files changed, 1695 insertions(+), 419 deletions(-) create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/build.gradle.kts create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApi.java create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiConfigurationExtension.java create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiExtension.java create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/controller/StsAccountsApiController.java create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/model/StsAccountCreation.java create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/model/UpdateClientSecret.java create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/resources/sts-accounts-api-version.json create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiExtensionTest.java create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/controller/StsAccountsApiControllerTest.java rename extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-client-configuration/src/test/java/org/eclipse/edc/iam/identitytrust/sts/client/configuration/{StsClientConfigurationExtensionTest.java => StsAccountConfigurationExtensionTest.java} (87%) create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/RandomStringGenerator.java create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsAccountServiceImpl.java delete mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientServiceImpl.java rename extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/{InMemoryStsClientStore.java => InMemoryStsAccountStore.java} (63%) rename extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/{StsClientTokenIssuanceIntegrationTest.java => StsAccountTokenIssuanceIntegrationTest.java} (78%) create mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsAccountServiceImplTest.java rename extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/{StsClientTokenGeneratorServiceImplTest.java => StsAccountTokenGeneratorServiceImplTest.java} (87%) delete mode 100644 extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientServiceImplTest.java rename extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/{InMemoryStsClientStoreTest.java => InMemoryStsAccountStoreTest.java} (68%) rename extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/{SqlStsClientStore.java => SqlStsAccountStore.java} (77%) rename extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/{SqlStsClientStoreExtensionTest.java => SqlStsAccountStoreExtensionTest.java} (88%) rename extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/{SqlStsClientStoreTest.java => SqlStsAccountStoreTest.java} (84%) create mode 100644 resources/openapi/sts-accounts-api.version rename spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/{StsClient.java => StsAccount.java} (83%) rename spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/{StsClientTokenAdditionalParams.java => StsAccountTokenAdditionalParams.java} (75%) create mode 100644 spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsAccountService.java create mode 100644 spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientSecretGenerator.java delete mode 100644 spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientService.java rename spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/{StsClientStore.java => StsAccountStore.java} (69%) rename spi/common/identity-trust-sts-spi/src/testFixtures/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/fixtures/{StsClientStoreTestBase.java => StsAccountStoreTestBase.java} (92%) diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/build.gradle.kts b/extensions/common/iam/identity-trust/identity-trust-sts/build.gradle.kts index 1985d621759..51b3bad6847 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/build.gradle.kts +++ b/extensions/common/iam/identity-trust/identity-trust-sts/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { api(project(":extensions:common:iam:identity-trust:identity-trust-sts:identity-trust-sts-core")) api(project(":extensions:common:iam:identity-trust:identity-trust-sts:identity-trust-sts-remote-client")) api(project(":extensions:common:iam:identity-trust:identity-trust-sts:identity-trust-sts-api")) + api(project(":extensions:common:iam:identity-trust:identity-trust-sts:identity-trust-sts-accounts-api")) api(project(":extensions:common:iam:identity-trust:identity-trust-sts:identity-trust-sts-client-configuration")) api(project(":extensions:common:iam:identity-trust:identity-trust-sts:lib:identity-trust-sts-remote-lib")) } diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/build.gradle.kts b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/build.gradle.kts new file mode 100644 index 00000000000..587727a7d3c --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/build.gradle.kts @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 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` + id(libs.plugins.swagger.get().pluginId) +} + +dependencies { + api(project(":spi:common:web-spi")) + api(project(":spi:common:auth-spi")) + api(project(":spi:common:identity-trust-sts-spi")) + api(project(":extensions:common:auth:auth-tokenbased")) + + implementation(libs.jakarta.rsApi) + + testImplementation(libs.jersey.common) + testImplementation(libs.jersey.server) + + testImplementation(project(":core:common:junit")) + testImplementation(testFixtures(project(":extensions:common:http:jersey-core"))) + testImplementation(testFixtures(project(":spi:common:identity-trust-sts-spi"))) + testImplementation(libs.restAssured) +} + +edcBuild { + swagger { + apiGroup.set("sts-accounts-api") + } +} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApi.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApi.java new file mode 100644 index 00000000000..e8031954d28 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApi.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024 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.api.iam.identitytrust.sts.accounts; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.eclipse.edc.api.iam.identitytrust.sts.accounts.model.StsAccountCreation; +import org.eclipse.edc.api.iam.identitytrust.sts.accounts.model.UpdateClientSecret; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.web.spi.ApiErrorDetail; + +import java.util.Collection; + +@OpenAPIDefinition +@Tag(name = "Secure Token Service Accounts Api") +public interface StsAccountsApi { + + @Operation(description = "Creates a new STS Account with the given parameters", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = StsAccountCreation.class))), + responses = { + @ApiResponse(responseCode = "200", description = "The newly created STS Account including the client_secret.", + content = @Content(schema = @Schema(implementation = StsAccountCreation.class))), + @ApiResponse(responseCode = "400", description = "Invalid Request", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "401", description = "Not authenticated: principal could not be identified", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) + }) + StsAccountCreation createAccount(StsAccountCreation request); + + @Operation(description = "Updates an existing STS account with new values. To update the client secret, please use the relevant API endpoint.", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = StsAccount.class))), + responses = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "400", description = "Invalid Request", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "401", description = "Not authenticated: principal could not be identified", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "404", description = "An account with the given ID was not found") + }) + void updateAccount(StsAccount updatedAccount); + + @Operation(description = "Gets the STS Account for the given ID", + responses = { + @ApiResponse(responseCode = "200", description = "The STS Account.", + content = @Content(schema = @Schema(implementation = StsAccount.class))), + @ApiResponse(responseCode = "400", description = "Invalid Request", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "401", description = "Not authenticated: principal could not be identified", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "404", description = "An account with the given ID was not found") + }) + StsAccount getAccount(String accountId); + + @Operation(description = "Queries for STS Account conforming to the given query object", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = QuerySpec.class))), + responses = { + @ApiResponse(responseCode = "200", description = "The STS Accounts.", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = StsAccount.class)))), + @ApiResponse(responseCode = "400", description = "Invalid Request", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "401", description = "Not authenticated: principal could not be identified", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) + }) + Collection queryAccounts(QuerySpec querySpec); + + @Operation(description = "Updates the client secret for an account. If the secret is null, then one will be generated.", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = QuerySpec.class))), + responses = { + @ApiResponse(responseCode = "200", description = "The secret alias that is now used by the account."), + @ApiResponse(responseCode = "400", description = "Invalid Request", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "401", description = "Not authenticated: principal could not be identified", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "404", description = "An account with the given ID was not found") + }) + String updateClientSecret(String accountId, UpdateClientSecret request); + + + @Operation(description = "Deletes an STS Account", + responses = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "400", description = "Invalid Request", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "401", description = "Not authenticated: principal could not be identified", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "404", description = "An account with the given ID was not found") + }) + void deleteAccount(String accountId); +} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiConfigurationExtension.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiConfigurationExtension.java new file mode 100644 index 00000000000..017149254d0 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiConfigurationExtension.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2024 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.api.iam.identitytrust.sts.accounts; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.SettingContext; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.apiversion.ApiVersionService; +import org.eclipse.edc.spi.system.apiversion.VersionRecord; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.web.spi.WebServer; +import org.eclipse.edc.web.spi.configuration.ApiContext; +import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer; +import org.eclipse.edc.web.spi.configuration.WebServiceSettings; + +import java.io.IOException; +import java.util.stream.Stream; + +@Extension(value = StsAccountsApiConfigurationExtension.NAME) +public class StsAccountsApiConfigurationExtension implements ServiceExtension { + + public static final String NAME = "Secure Token Service Accounts API configuration"; + private static final String WEB_SERVICE_NAME = "STS Accounts API"; + private static final int DEFAULT_STS_API_PORT = 9393; + private static final String DEFAULT_STS_API_CONTEXT_PATH = "/api/sts"; + + @SettingContext("Sts API context setting key") + private static final String STS_ACCOUNTS_CONFIG_KEY = "web.http." + ApiContext.STS_ACCOUNTS; + + public static final WebServiceSettings SETTINGS = WebServiceSettings.Builder.newInstance() + .apiConfigKey(STS_ACCOUNTS_CONFIG_KEY) + .contextAlias(ApiContext.STS_ACCOUNTS) + .defaultPath(DEFAULT_STS_API_CONTEXT_PATH) + .defaultPort(DEFAULT_STS_API_PORT) + .useDefaultContext(false) + .name(WEB_SERVICE_NAME) + .build(); + private static final String API_VERSION_JSON_FILE = "sts-accounts-api-version.json"; + + @Inject + private WebServer webServer; + @Inject + private WebServiceConfigurer configurator; + @Inject + private TypeManager typeManager; + @Inject + private ApiVersionService apiVersionService; + + @Override + public String name() { + return NAME; + } + + @Override + public void initialize(ServiceExtensionContext context) { + var config = context.getConfig(STS_ACCOUNTS_CONFIG_KEY); + configurator.configure(config, webServer, SETTINGS); + registerVersionInfo(getClass().getClassLoader()); + } + + private void registerVersionInfo(ClassLoader resourceClassLoader) { + try (var versionContent = resourceClassLoader.getResourceAsStream(API_VERSION_JSON_FILE)) { + if (versionContent == null) { + throw new EdcException("Version file not found or not readable."); + } + Stream.of(typeManager.getMapper() + .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .readValue(versionContent, VersionRecord[].class)) + .forEach(vr -> apiVersionService.addRecord(ApiContext.STS_ACCOUNTS, vr)); + } catch (IOException e) { + throw new EdcException(e); + } + } +} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiExtension.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiExtension.java new file mode 100644 index 00000000000..3944bb31adb --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiExtension.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024 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.api.iam.identitytrust.sts.accounts; + +import org.eclipse.edc.api.auth.spi.AuthenticationRequestFilter; +import org.eclipse.edc.api.auth.spi.registry.ApiAuthenticationRegistry; +import org.eclipse.edc.api.auth.token.TokenBasedAuthenticationService; +import org.eclipse.edc.api.iam.identitytrust.sts.accounts.controller.StsAccountsApiController; +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsAccountService; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +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.web.spi.WebService; +import org.eclipse.edc.web.spi.configuration.ApiContext; + +import static java.util.Optional.ofNullable; + +@Extension(value = StsAccountsApiExtension.NAME, categories = { "sts", "dcp", "api" }) +public class StsAccountsApiExtension implements ServiceExtension { + + public static final String NAME = "Secure Token Service Accounts API Extension"; + public static final String STS_ACCOUNTS_API_CONTEXT = "sts-accounts-api"; + + @Setting(value = "API key (or Vault alias) for the STS Accounts API's default authentication mechanism (token-based).") + public static final String STS_ACCOUNTS_API_KEY = "edc.api.accounts.key"; + + @Inject + private StsAccountService clientService; + + @Inject + private WebService webService; + @Inject + private ApiAuthenticationRegistry authenticationRegistry; + + @Inject + private Vault vault; + + @Override + public String name() { + return NAME; + } + + @Override + public void initialize(ServiceExtensionContext context) { + + if (!authenticationRegistry.hasService(STS_ACCOUNTS_API_CONTEXT)) { + authenticationRegistry.register(STS_ACCOUNTS_API_CONTEXT, new TokenBasedAuthenticationService(resolveApiKey(context))); + } + var authenticationFilter = new AuthenticationRequestFilter(authenticationRegistry, STS_ACCOUNTS_API_CONTEXT); + + webService.registerResource(ApiContext.STS_ACCOUNTS, new StsAccountsApiController(clientService)); + webService.registerResource(ApiContext.STS_ACCOUNTS, authenticationFilter); + } + + private String resolveApiKey(ServiceExtensionContext context) { + var keyOrAlias = context.getConfig().getString(STS_ACCOUNTS_API_KEY); + return ofNullable(vault.resolveSecret(keyOrAlias)) + .orElse(keyOrAlias); + } +} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/controller/StsAccountsApiController.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/controller/StsAccountsApiController.java new file mode 100644 index 00000000000..d7430a0a6b7 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/controller/StsAccountsApiController.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2024 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.api.iam.identitytrust.sts.accounts.controller; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.edc.api.iam.identitytrust.sts.accounts.StsAccountsApi; +import org.eclipse.edc.api.iam.identitytrust.sts.accounts.model.StsAccountCreation; +import org.eclipse.edc.api.iam.identitytrust.sts.accounts.model.UpdateClientSecret; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsAccountService; +import org.eclipse.edc.spi.query.QuerySpec; + +import java.util.Collection; + +import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.exceptionMapper; + +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Path("/v1alpha/accounts") +public class StsAccountsApiController implements StsAccountsApi { + + private final StsAccountService accountService; + + public StsAccountsApiController(StsAccountService accountService) { + this.accountService = accountService; + } + + @Path("/") + @POST + @Override + public StsAccountCreation createAccount(StsAccountCreation request) { + var secret = accountService.create(request.account(), request.clientSecret()).orElseThrow(exceptionMapper(StsAccount.class)); + return new StsAccountCreation(request.account(), secret); + } + + @Path("/") + @PUT + @Override + public void updateAccount(StsAccount updatedAccount) { + accountService.update(updatedAccount).orElseThrow(exceptionMapper(StsAccount.class, updatedAccount.getId())); + } + + @GET + @Path("/{id}") + @Override + public StsAccount getAccount(@PathParam("id") String accountId) { + return accountService.findById(accountId).orElseThrow(exceptionMapper(StsAccount.class, accountId)); + } + + @POST + @Path("/query") + @Override + public Collection queryAccounts(QuerySpec querySpec) { + return accountService.queryAccounts(querySpec); + } + + @PUT + @Path("/{id}/secret") + @Override + public String updateClientSecret(@PathParam("id") String accountId, UpdateClientSecret request) { + return accountService.updateSecret(accountId, request.newAlias(), request.newSecret()).orElseThrow(exceptionMapper(StsAccount.class, accountId)); + } + + @DELETE + @Path("/{id}") + @Override + public void deleteAccount(@PathParam("id") String accountId) { + accountService.deleteById(accountId).orElseThrow(exceptionMapper(StsAccount.class, accountId)); + } +} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/model/StsAccountCreation.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/model/StsAccountCreation.java new file mode 100644 index 00000000000..bb3ae7194be --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/model/StsAccountCreation.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 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.api.iam.identitytrust.sts.accounts.model; + +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.jetbrains.annotations.Nullable; + +/** + * This container object serves as request DTO for creating {@link StsAccount} entries. + * + * @param account The STS Account to be created + * @param clientSecret If present, this value will be used as {@code client_secret} for the account. If null, a client secret + * will be generated. + */ +public record StsAccountCreation(StsAccount account, @Nullable String clientSecret) { +} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/model/UpdateClientSecret.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/model/UpdateClientSecret.java new file mode 100644 index 00000000000..7919bea6e62 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/model/UpdateClientSecret.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 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.api.iam.identitytrust.sts.accounts.model; + +import org.jetbrains.annotations.Nullable; + +/** + * Container object to update the {@code client_secret} of an STS Account. + * + * @param newAlias the alias under which the client secret should be stored in the {@link org.eclipse.edc.spi.security.Vault} + * @param newSecret the new client_secret. If null, one will be generated. + */ +public record UpdateClientSecret(String newAlias, @Nullable String newSecret) { +} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 00000000000..d9e542e07cb --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,16 @@ +# +# Copyright (c) 2024 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 +# +# + +org.eclipse.edc.api.iam.identitytrust.sts.accounts.StsAccountsApiConfigurationExtension +org.eclipse.edc.api.iam.identitytrust.sts.accounts.StsAccountsApiExtension \ No newline at end of file diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/resources/sts-accounts-api-version.json b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/resources/sts-accounts-api-version.json new file mode 100644 index 00000000000..28e0a452048 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/resources/sts-accounts-api-version.json @@ -0,0 +1,7 @@ +[ + { + "version": "1.0.0-alpha", + "urlPath": "/v1alpha", + "lastUpdated": "2024-09-24T14:14:00Z" + } +] \ No newline at end of file diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiExtensionTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiExtensionTest.java new file mode 100644 index 00000000000..2b9864c2705 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiExtensionTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2024 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.api.iam.identitytrust.sts.accounts; + +import org.eclipse.edc.api.auth.spi.AuthenticationRequestFilter; +import org.eclipse.edc.api.auth.spi.registry.ApiAuthenticationRegistry; +import org.eclipse.edc.api.auth.token.TokenBasedAuthenticationService; +import org.eclipse.edc.api.iam.identitytrust.sts.accounts.controller.StsAccountsApiController; +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.web.spi.WebService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.eclipse.edc.api.iam.identitytrust.sts.accounts.StsAccountsApiExtension.STS_ACCOUNTS_API_CONTEXT; +import static org.eclipse.edc.api.iam.identitytrust.sts.accounts.StsAccountsApiExtension.STS_ACCOUNTS_API_KEY; +import static org.eclipse.edc.web.spi.configuration.ApiContext.STS_ACCOUNTS; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@ExtendWith(DependencyInjectionExtension.class) +class StsAccountsApiExtensionTest { + + private final ApiAuthenticationRegistry apiAuthenticationRegistry = mock(); + private final WebService webService = mock(); + private final Vault vault = mock(); + + @BeforeEach + void setUp(ServiceExtensionContext context) { + context.registerService(ApiAuthenticationRegistry.class, apiAuthenticationRegistry); + context.registerService(WebService.class, webService); + context.registerService(Vault.class, vault); + } + + @Test + void initialize_noAuthServicePresent_withApiKeyInVault(StsAccountsApiExtension extension, ServiceExtensionContext context) { + var config = mock(Config.class); + when(config.getString(eq(STS_ACCOUNTS_API_KEY))) + .thenReturn("test-api-key"); + when(context.getConfig()).thenReturn(config); + when(apiAuthenticationRegistry.hasService(eq(STS_ACCOUNTS_API_CONTEXT))).thenReturn(false); + + extension.initialize(context); + + verify(vault).resolveSecret(eq("test-api-key")); + verify(apiAuthenticationRegistry).register(eq(STS_ACCOUNTS_API_CONTEXT), isA(TokenBasedAuthenticationService.class)); + verify(webService).registerResource(eq(STS_ACCOUNTS), isA(StsAccountsApiController.class)); + verify(webService).registerResource(eq(STS_ACCOUNTS), isA(AuthenticationRequestFilter.class)); + } + + @Test + void initialize_noAuthServicePresent_withApiKeyInConfig(StsAccountsApiExtension extension, ServiceExtensionContext context) { + var config = mock(Config.class); + when(config.getString(eq(STS_ACCOUNTS_API_KEY))) + .thenReturn("test-api-key"); + when(context.getConfig()).thenReturn(config); + when(apiAuthenticationRegistry.hasService(eq(STS_ACCOUNTS_API_CONTEXT))).thenReturn(false); + + extension.initialize(context); + + verify(vault).resolveSecret(eq("test-api-key")); + verify(apiAuthenticationRegistry).register(eq(STS_ACCOUNTS_API_CONTEXT), isA(TokenBasedAuthenticationService.class)); + verify(webService).registerResource(eq(STS_ACCOUNTS), isA(StsAccountsApiController.class)); + verify(webService).registerResource(eq(STS_ACCOUNTS), isA(AuthenticationRequestFilter.class)); + } + + @Test + void initialize_otherAuthServicePresent_withApiKeyInConfig(StsAccountsApiExtension extension, ServiceExtensionContext context) { + var config = mock(Config.class); + when(config.getString(eq(STS_ACCOUNTS_API_KEY))) + .thenReturn("test-api-key"); + when(context.getConfig()).thenReturn(config); + when(apiAuthenticationRegistry.hasService(eq(STS_ACCOUNTS_API_CONTEXT))).thenReturn(true); + + extension.initialize(context); + + verify(apiAuthenticationRegistry).hasService(eq(STS_ACCOUNTS_API_CONTEXT)); + verify(webService).registerResource(eq(STS_ACCOUNTS), isA(StsAccountsApiController.class)); + verify(webService).registerResource(eq(STS_ACCOUNTS), isA(AuthenticationRequestFilter.class)); + verifyNoMoreInteractions(vault, apiAuthenticationRegistry); + } +} \ No newline at end of file diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/controller/StsAccountsApiControllerTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/controller/StsAccountsApiControllerTest.java new file mode 100644 index 00000000000..6a017d02b16 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/controller/StsAccountsApiControllerTest.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2024 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.api.iam.identitytrust.sts.accounts.controller; + +import io.restassured.specification.RequestSpecification; +import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration; +import org.eclipse.edc.api.iam.identitytrust.sts.accounts.model.StsAccountCreation; +import org.eclipse.edc.api.iam.identitytrust.sts.accounts.model.UpdateClientSecret; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsAccountService; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.ServiceResult; +import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class StsAccountsApiControllerTest extends RestControllerTestBase { + + private final StsAccountService accountServiceMock = mock(); + + private static StsAccount.Builder createAccount() { + return StsAccount.Builder.newInstance() + .id("account-id") + .did("did:web:test") + .name("test-name") + .clientId("test-client-id") + .secretAlias("test-alias") + .privateKeyAlias("test-private-key") + .publicKeyReference("test-public-key"); + } + + @Test + void createAccount_withSecret() { + var secret = "sup3r$ecr3t"; + var account = createAccount().build(); + var accountCreate = new StsAccountCreation(account, secret); + when(accountServiceMock.create(any(StsAccount.class), eq(secret))).thenReturn(ServiceResult.success(secret)); + + baseRequest() + .contentType("application/json") + .body(accountCreate) + .post() + .then() + .statusCode(200) + .body("clientSecret", equalTo(secret)); + } + + + @Test + void createAccount_withoutSecret() { + var account = createAccount().build(); + var accountCreate = new StsAccountCreation(account, null); + + when(accountServiceMock.create(any(StsAccount.class), isNull())).thenReturn(ServiceResult.success(UUID.randomUUID().toString())); + + baseRequest() + .contentType("application/json") + .body(accountCreate) + .post() + .then() + .statusCode(200) + .body("clientSecret", notNullValue()); + } + + @Test + void updateAccount() { + when(accountServiceMock.update(any())).thenReturn(ServiceResult.success()); + var account = createAccount().build(); + baseRequest() + .contentType("application/json") + .body(account) + .put() + .then() + .statusCode(204); + + verify(accountServiceMock).update(any(StsAccount.class)); + } + + @Test + void updateAccount_whenNotFound_expect404() { + when(accountServiceMock.update(any())).thenReturn(ServiceResult.notFound("foo")); + + var account = createAccount().build(); + baseRequest() + .contentType("application/json") + .body(account) + .put() + .then() + .statusCode(404) + .body(containsString("Object of type StsAccount with ID=account-id was not found")); + } + + @Test + void getAccount() { + var account = createAccount().build(); + when(accountServiceMock.findById(eq(account.getId()))).thenReturn(ServiceResult.success(account)); + + var result = baseRequest() + .contentType("application/json") + .get("/" + account.getId()) + .then() + .statusCode(200) + .extract().body().as(StsAccount.class); + + var config = RecursiveComparisonConfiguration.builder() + .withIgnoredFields("clock") + .build(); + assertThat(result).usingRecursiveComparison(config).isEqualTo(account); + + } + + @Test + void getAccount_whenNotFound_expect404() { + when(accountServiceMock.findById(anyString())).thenReturn(ServiceResult.notFound("foo")); + baseRequest() + .contentType("application/json") + .get("/notexist") + .then() + .statusCode(404); + } + + @Test + void queryAccounts() { + var query = QuerySpec.max(); + + var acc1 = createAccount().id("id1").build(); + var acc2 = createAccount().id("id2").build(); + // todo: implement service method + when(accountServiceMock.queryAccounts(any(QuerySpec.class))).thenReturn(List.of(acc1, acc2)); + + var result = baseRequest() + .contentType("application/json") + .body(query) + .post("/query") + .then() + .statusCode(200) + .extract() + .body().as(StsAccount[].class); + + assertThat(result).isNotNull().extracting(StsAccount::getId).containsExactlyInAnyOrder(acc1.getId(), acc2.getId()); + } + + @Test + void queryAccounts_whenNoResult() { + var query = QuerySpec.max(); + when(accountServiceMock.queryAccounts(any(QuerySpec.class))).thenReturn(Collections.emptyList()); + + var result = baseRequest() + .contentType("application/json") + .body(query) + .post("/query") + .then() + .statusCode(200) + .extract() + .body().as(StsAccount[].class); + + assertThat(result).isNotNull().isEmpty(); + verify(accountServiceMock).queryAccounts(eq(query)); + } + + @Test + void updateClientSecret_newAlias_noSecret() { + + when(accountServiceMock.updateSecret(anyString(), eq("new-alias"), isNull())).thenReturn(ServiceResult.success("new-alias")); + + var request = new UpdateClientSecret("new-alias", null); + baseRequest() + .contentType("application/json") + .body(request) + .put("/account-id/secret") + .then() + .statusCode(200) + .body(equalTo("new-alias")); + verify(accountServiceMock).updateSecret(eq("account-id"), eq("new-alias"), isNull()); + } + + @Test + void updateClientSecret_newAlias_withSecret() { + + when(accountServiceMock.updateSecret(anyString(), eq("new-alias"), eq("new-secret"))).thenReturn(ServiceResult.success("new-alias")); + + var request = new UpdateClientSecret("new-alias", "new-secret"); + baseRequest() + .contentType("application/json") + .body(request) + .put("/account-id/secret") + .then() + .statusCode(200) + .body(equalTo("new-alias")); + verify(accountServiceMock).updateSecret(eq("account-id"), eq("new-alias"), eq("new-secret")); + } + + @Test + void updateClientSecret_whenNotFound_expect404() { + + when(accountServiceMock.updateSecret(eq("not-exists-id"), eq("new-alias"), isNull())).thenReturn(ServiceResult.notFound("foo")); + var request = new UpdateClientSecret("new-alias", null); + baseRequest() + .contentType("application/json") + .body(request) + .put("/not-exists-id/secret") + .then() + .statusCode(404) + .body(containsString("Object of type StsAccount with ID=not-exists-id was not found")); + } + + + @Test + void deleteAccount() { + when(accountServiceMock.deleteById(eq("account-id"))).thenReturn(ServiceResult.success()); + baseRequest() + .contentType("application/json") + .delete("/account-id") + .then() + .statusCode(204); + + verify(accountServiceMock).deleteById(eq("account-id")); + } + + @Test + void deleteAccount_whenNotFound_expect404() { + when(accountServiceMock.deleteById(eq("account-id"))).thenReturn(ServiceResult.notFound("foo")); + + baseRequest() + .contentType("application/json") + .delete("/account-id") + .then() + .statusCode(404); + verify(accountServiceMock).deleteById(eq("account-id")); + } + + @Override + protected Object controller() { + return new StsAccountsApiController(accountServiceMock); + } + + private RequestSpecification baseRequest() { + return given() + .port(port) + .baseUri("http://localhost:" + port + "/v1alpha/accounts"); + } +} \ No newline at end of file diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/SecureTokenServiceApiExtension.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/SecureTokenServiceApiExtension.java index 04c62a2f4e2..a37b9f30606 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/SecureTokenServiceApiExtension.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/SecureTokenServiceApiExtension.java @@ -17,7 +17,7 @@ import org.eclipse.edc.api.iam.identitytrust.sts.controller.SecureTokenServiceApiController; import org.eclipse.edc.api.iam.identitytrust.sts.exception.StsTokenExceptionMapper; import org.eclipse.edc.api.iam.identitytrust.sts.validation.StsTokenRequestValidator; -import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientService; +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsAccountService; import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientTokenGeneratorService; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; @@ -32,7 +32,7 @@ public class SecureTokenServiceApiExtension implements ServiceExtension { public static final String NAME = "Secure Token Service API"; @Inject - private StsClientService clientService; + private StsAccountService clientService; @Inject private StsClientTokenGeneratorService tokenService; diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/controller/SecureTokenServiceApiController.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/controller/SecureTokenServiceApiController.java index 4343cd90e60..0a8befc5da1 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/controller/SecureTokenServiceApiController.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/controller/SecureTokenServiceApiController.java @@ -24,8 +24,8 @@ import org.eclipse.edc.api.iam.identitytrust.sts.exception.StsTokenException; import org.eclipse.edc.api.iam.identitytrust.sts.model.StsTokenRequest; import org.eclipse.edc.api.iam.identitytrust.sts.model.StsTokenResponse; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClientTokenAdditionalParams; -import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientService; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccountTokenAdditionalParams; +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsAccountService; import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientTokenGeneratorService; import org.eclipse.edc.spi.iam.TokenRepresentation; import org.eclipse.edc.validator.spi.Validator; @@ -33,13 +33,13 @@ @Path("/") public class SecureTokenServiceApiController implements SecureTokenServiceApi { - private final StsClientService clientService; + private final StsAccountService clientService; private final StsClientTokenGeneratorService tokenService; private final Validator tokenRequestValidator; - public SecureTokenServiceApiController(StsClientService clientService, StsClientTokenGeneratorService tokenService, Validator tokenRequestValidator) { + public SecureTokenServiceApiController(StsAccountService clientService, StsClientTokenGeneratorService tokenService, Validator tokenRequestValidator) { this.clientService = clientService; this.tokenService = tokenService; this.tokenRequestValidator = tokenRequestValidator; @@ -60,8 +60,8 @@ public StsTokenResponse token(@BeanParam StsTokenRequest request) { } - private StsClientTokenAdditionalParams additionalParams(StsTokenRequest request) { - return StsClientTokenAdditionalParams.Builder.newInstance() + private StsAccountTokenAdditionalParams additionalParams(StsTokenRequest request) { + return StsAccountTokenAdditionalParams.Builder.newInstance() .audience(request.getAudience()) .accessToken(request.getToken()) .bearerAccessScope(request.getBearerAccessScope()) diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtensionTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtensionTest.java index 36d0ea08f89..5aac58af969 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtensionTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtensionTest.java @@ -43,7 +43,6 @@ public class StsApiConfigurationExtensionTest { private final WebServiceConfigurer configurer = mock(); private final Monitor monitor = mock(Monitor.class); private final WebService webService = mock(WebService.class); - private StsApiConfigurationExtension extension; @BeforeEach void setUp(ServiceExtensionContext context) { diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/controller/SecureServiceTokenApiControllerTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/controller/SecureServiceTokenApiControllerTest.java index 4f17a841297..b4d1711221a 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/controller/SecureServiceTokenApiControllerTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/controller/SecureServiceTokenApiControllerTest.java @@ -17,8 +17,8 @@ import io.restassured.specification.RequestSpecification; import org.eclipse.edc.api.iam.identitytrust.sts.exception.StsTokenExceptionMapper; import org.eclipse.edc.api.iam.identitytrust.sts.model.StsTokenRequest; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientService; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsAccountService; import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientTokenGeneratorService; import org.eclipse.edc.junit.annotations.ApiTest; import org.eclipse.edc.spi.iam.TokenRepresentation; @@ -41,7 +41,7 @@ class SecureServiceTokenApiControllerTest extends RestControllerTestBase { private static final String GRANT_TYPE = "client_credentials"; - private final StsClientService clientService = mock(); + private final StsAccountService clientService = mock(); private final StsClientTokenGeneratorService tokenService = mock(); private final Validator validator = mock(); @@ -59,7 +59,7 @@ void token() { var name = "Name"; var expiresIn = 3600; - var client = StsClient.Builder.newInstance() + var client = StsAccount.Builder.newInstance() .id(id) .clientId(clientId) .name(name) diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-client-configuration/src/main/java/org/eclipse/edc/iam/identitytrust/sts/client/configuration/StsClientConfigurationExtension.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-client-configuration/src/main/java/org/eclipse/edc/iam/identitytrust/sts/client/configuration/StsClientConfigurationExtension.java index 967a549f788..d7a2854b84a 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-client-configuration/src/main/java/org/eclipse/edc/iam/identitytrust/sts/client/configuration/StsClientConfigurationExtension.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-client-configuration/src/main/java/org/eclipse/edc/iam/identitytrust/sts/client/configuration/StsClientConfigurationExtension.java @@ -14,8 +14,8 @@ package org.eclipse.edc.iam.identitytrust.sts.client.configuration; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.spi.monitor.Monitor; @@ -43,7 +43,7 @@ public class StsClientConfigurationExtension implements ServiceExtension { private Monitor monitor; @Inject - private StsClientStore clientStore; + private StsAccountStore clientStore; @Override @@ -68,7 +68,7 @@ private void configureClient(Config config) { var clientPrivateKeyAlias = config.getString(CLIENT_PRIVATE_KEY_ALIAS); var publicKeyRef = config.getString(CLIENT_PUBLIC_KEY_REFERENCE, null); - var client = StsClient.Builder.newInstance() + var client = StsAccount.Builder.newInstance() .id(id) .clientId(clientId) .secretAlias(clientSecretAlias) diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-client-configuration/src/test/java/org/eclipse/edc/iam/identitytrust/sts/client/configuration/StsClientConfigurationExtensionTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-client-configuration/src/test/java/org/eclipse/edc/iam/identitytrust/sts/client/configuration/StsAccountConfigurationExtensionTest.java similarity index 87% rename from extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-client-configuration/src/test/java/org/eclipse/edc/iam/identitytrust/sts/client/configuration/StsClientConfigurationExtensionTest.java rename to extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-client-configuration/src/test/java/org/eclipse/edc/iam/identitytrust/sts/client/configuration/StsAccountConfigurationExtensionTest.java index 11131dd5d6c..eacfe450bab 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-client-configuration/src/test/java/org/eclipse/edc/iam/identitytrust/sts/client/configuration/StsClientConfigurationExtensionTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-client-configuration/src/test/java/org/eclipse/edc/iam/identitytrust/sts/client/configuration/StsAccountConfigurationExtensionTest.java @@ -14,8 +14,8 @@ package org.eclipse.edc.iam.identitytrust.sts.client.configuration; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; @@ -42,13 +42,13 @@ import static org.mockito.Mockito.when; @ExtendWith(DependencyInjectionExtension.class) -public class StsClientConfigurationExtensionTest implements ServiceExtension { +public class StsAccountConfigurationExtensionTest implements ServiceExtension { - private final StsClientStore clientStore = mock(); + private final StsAccountStore clientStore = mock(); @BeforeEach void setUp(ServiceExtensionContext context) { - context.registerService(StsClientStore.class, clientStore); + context.registerService(StsAccountStore.class, clientStore); } @Test @@ -59,7 +59,7 @@ void initialize_noClients(ServiceExtensionContext context, StsClientConfiguratio @Test void initialize_withClient(ServiceExtensionContext context, StsClientConfigurationExtension extension) { - var client = StsClient.Builder.newInstance() + var client = StsAccount.Builder.newInstance() .id("id") .name("name") .clientId("client_id") @@ -73,7 +73,7 @@ void initialize_withClient(ServiceExtensionContext context, StsClientConfigurati when(context.getConfig(CONFIG_PREFIX)).thenReturn(config); extension.initialize(context); - var capture = ArgumentCaptor.forClass(StsClient.class); + var capture = ArgumentCaptor.forClass(StsAccount.class); verify(clientStore).create(capture.capture()); assertThat(capture.getValue()).usingRecursiveComparison() @@ -81,7 +81,7 @@ void initialize_withClient(ServiceExtensionContext context, StsClientConfigurati .isEqualTo(client); } - private Map clientConfig(StsClient client, String clientAlias) { + private Map clientConfig(StsAccount client, String clientAlias) { return Map.of( clientAlias + "." + ID, client.getId(), clientAlias + "." + CLIENT_NAME, client.getName(), diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/RandomStringGenerator.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/RandomStringGenerator.java new file mode 100644 index 00000000000..10b0d8c0c6c --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/RandomStringGenerator.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 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.sts.defaults; + +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientSecretGenerator; +import org.jetbrains.annotations.Nullable; + +import java.security.SecureRandom; + +/** + * Default client secret generator that creates an alphanumeric string of length {@link RandomStringGenerator#DEFAULT_CLIENT_SECRET_LENGTH} + * (16). + */ +public class RandomStringGenerator implements StsClientSecretGenerator { + public static final int DEFAULT_CLIENT_SECRET_LENGTH = 16; + private final int length; + + public RandomStringGenerator() { + length = DEFAULT_CLIENT_SECRET_LENGTH; + } + + public RandomStringGenerator(int length) { + this.length = length; + } + + @Override + public String generateClientSecret(@Nullable Object parameters) { + // algorithm taken from https://www.baeldung.com/java-random-string + int leftLimit = 48; // numeral '0' + int rightLimit = 122; // letter 'z' + var random = new SecureRandom(); + + return random.ints(leftLimit, rightLimit + 1) + .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)) + .limit(length) + .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) + .toString(); + } +} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtension.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtension.java index 5ce08a9e8f3..eea5446a77e 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtension.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtension.java @@ -14,12 +14,13 @@ package org.eclipse.edc.iam.identitytrust.sts.defaults; -import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsClientServiceImpl; +import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsAccountServiceImpl; import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsClientTokenGeneratorServiceImpl; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientService; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsAccountService; +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientSecretGenerator; import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientTokenGeneratorService; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; @@ -34,6 +35,8 @@ import java.time.Clock; import java.util.concurrent.TimeUnit; +import static java.util.Optional.ofNullable; + @Extension(StsDefaultServicesExtension.NAME) public class StsDefaultServicesExtension implements ServiceExtension { @@ -45,7 +48,7 @@ public class StsDefaultServicesExtension implements ServiceExtension { private static final int DEFAULT_STS_TOKEN_EXPIRATION_MIN = 5; @Inject - private StsClientStore clientStore; + private StsAccountStore clientStore; @Inject private TransactionContext transactionContext; @@ -58,6 +61,8 @@ public class StsDefaultServicesExtension implements ServiceExtension { @Inject private Clock clock; + @Inject(required = false) + private StsClientSecretGenerator stsClientSecretGenerator; @Override public String name() { @@ -69,13 +74,18 @@ public StsClientTokenGeneratorService clientTokenService(ServiceExtensionContext var tokenExpiration = context.getSetting(STS_TOKEN_EXPIRATION, DEFAULT_STS_TOKEN_EXPIRATION_MIN); return new StsClientTokenGeneratorServiceImpl( (client) -> new JwtGenerationService(jwsSignerProvider), - StsClient::getPrivateKeyAlias, + StsAccount::getPrivateKeyAlias, clock, TimeUnit.MINUTES.toSeconds(tokenExpiration)); } @Provider - public StsClientService clientService() { - return new StsClientServiceImpl(clientStore, vault, transactionContext); + public StsAccountService clientService() { + return new StsAccountServiceImpl(clientStore, vault, transactionContext, stsClientSecretGenerator()); + } + + private StsClientSecretGenerator stsClientSecretGenerator() { + return ofNullable(stsClientSecretGenerator) + .orElseGet(RandomStringGenerator::new); } } diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultStoresExtension.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultStoresExtension.java index 14d7b3308f3..571c8906613 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultStoresExtension.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultStoresExtension.java @@ -14,8 +14,8 @@ package org.eclipse.edc.iam.identitytrust.sts.defaults; -import org.eclipse.edc.iam.identitytrust.sts.defaults.store.InMemoryStsClientStore; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.defaults.store.InMemoryStsAccountStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provider; @@ -36,7 +36,7 @@ public String name() { } @Provider(isDefault = true) - public StsClientStore clientStore() { - return new InMemoryStsClientStore(criterionOperatorRegistry); + public StsAccountStore clientStore() { + return new InMemoryStsAccountStore(criterionOperatorRegistry); } } diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsAccountServiceImpl.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsAccountServiceImpl.java new file mode 100644 index 00000000000..8860dee022b --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsAccountServiceImpl.java @@ -0,0 +1,140 @@ +/* + * 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.sts.defaults.service; + +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsAccountService; +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientSecretGenerator; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.result.ServiceResult; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.transaction.spi.TransactionContext; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +import static java.lang.String.format; +import static java.util.Optional.ofNullable; +import static org.eclipse.edc.spi.result.ServiceResult.from; + +public class StsAccountServiceImpl implements StsAccountService { + + private final StsAccountStore stsAccountStore; + private final TransactionContext transactionContext; + private final Vault vault; + private final StsClientSecretGenerator stsClientSecretGenerator; + + public StsAccountServiceImpl(StsAccountStore stsAccountStore, Vault vault, TransactionContext transactionContext, StsClientSecretGenerator stsClientSecretGenerator) { + this.stsAccountStore = stsAccountStore; + this.vault = vault; + this.transactionContext = transactionContext; + this.stsClientSecretGenerator = stsClientSecretGenerator; + } + + @Override + public ServiceResult create(StsAccount client) { + return create(client, null) + .map(newSecret -> client); + } + + @Override + public ServiceResult create(StsAccount client, @Nullable String clientSecret) { + var result = transactionContext.execute(() -> from(stsAccountStore.create(client))); + clientSecret = ofNullable(clientSecret).orElseGet(this::generateSecret); + if (result.succeeded()) { + var vaultResult = vault.storeSecret(client.getSecretAlias(), clientSecret); + if (vaultResult.failed()) { + return ServiceResult.unexpected("Error storing client secret. Manual intervention is required for alias %s. %s".formatted(client.getSecretAlias(), vaultResult.getFailureDetail())); + } + return ServiceResult.success(clientSecret); + } + return result.mapFailure(); + } + + @Override + public ServiceResult findByClientId(String clientId) { + return transactionContext.execute(() -> from(stsAccountStore.findByClientId(clientId))); + } + + @Override + public ServiceResult update(StsAccount client) { + return transactionContext.execute(() -> from(stsAccountStore.update(client))); + } + + @Override + public ServiceResult updateSecret(String id, String newSecretAlias, @Nullable String newSecret) { + Objects.requireNonNull(newSecretAlias); + + var oldAlias = new AtomicReference(); + // generate new secret if needed + newSecret = ofNullable(newSecret).orElseGet(this::generateSecret); + + var updateResult = transactionContext.execute(() -> stsAccountStore.findById(id) + .compose(stsAccount -> { + oldAlias.set(stsAccount.getSecretAlias()); + stsAccount.updateSecretAlias(newSecretAlias); + return stsAccountStore.update(stsAccount); + })); + + if (updateResult.succeeded()) { + var oldSecretAlias = oldAlias.get(); + Result vaultInteractionResult = Result.success(); + + if (!oldSecretAlias.equals(newSecretAlias)) { + vaultInteractionResult = vaultInteractionResult.merge(vault.deleteSecret(oldSecretAlias)); + } + + var finalNewSecret = newSecret; + vaultInteractionResult = vaultInteractionResult.compose(v -> vault.storeSecret(newSecretAlias, finalNewSecret)); + return vaultInteractionResult.succeeded() + ? ServiceResult.success(newSecretAlias) + : ServiceResult.unexpected(vaultInteractionResult.getFailureDetail()); + } + return ServiceResult.fromFailure(updateResult); + } + + @Override + public ServiceResult deleteById(String id) { + return transactionContext.execute(() -> ServiceResult.from(stsAccountStore.deleteById(id)) + .onSuccess(stsAccount -> vault.deleteSecret(stsAccount.getSecretAlias())) + .mapEmpty()); + } + + @Override + public Collection queryAccounts(QuerySpec querySpec) { + return transactionContext.execute(() -> stsAccountStore.findAll(querySpec).toList()); + } + + @Override + public ServiceResult authenticate(StsAccount client, String secret) { + return ofNullable(vault.resolveSecret(client.getSecretAlias())) + .filter(vaultSecret -> vaultSecret.equals(secret)) + .map(s -> ServiceResult.success(client)) + .orElseGet(() -> ServiceResult.unauthorized(format("Failed to authenticate client with id %s", client.getId()))); + } + + @Override + public ServiceResult findById(String accountId) { + return transactionContext.execute(() -> from(stsAccountStore.findById(accountId))); + } + + private String generateSecret() { + return stsClientSecretGenerator.generateClientSecret(null); + } +} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientServiceImpl.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientServiceImpl.java deleted file mode 100644 index f685d3c492f..00000000000 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientServiceImpl.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.sts.defaults.service; - -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientService; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; -import org.eclipse.edc.spi.result.ServiceResult; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.transaction.spi.TransactionContext; - -import java.util.Optional; - -import static java.lang.String.format; - -public class StsClientServiceImpl implements StsClientService { - - private final StsClientStore stsClientStore; - private final TransactionContext transactionContext; - private final Vault vault; - - public StsClientServiceImpl(StsClientStore stsClientStore, Vault vault, TransactionContext transactionContext) { - this.stsClientStore = stsClientStore; - this.vault = vault; - this.transactionContext = transactionContext; - } - - @Override - public ServiceResult create(StsClient client) { - return transactionContext.execute(() -> ServiceResult.from(stsClientStore.create(client))); - } - - @Override - public ServiceResult findByClientId(String clientId) { - return transactionContext.execute(() -> ServiceResult.from(stsClientStore.findByClientId(clientId))); - } - - @Override - public ServiceResult authenticate(StsClient client, String secret) { - return Optional.ofNullable(vault.resolveSecret(client.getSecretAlias())) - .filter(vaultSecret -> vaultSecret.equals(secret)) - .map(s -> ServiceResult.success(client)) - .orElseGet(() -> ServiceResult.unauthorized(format("Failed to authenticate client with id %s", client.getId()))); - } -} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientTokenGeneratorServiceImpl.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientTokenGeneratorServiceImpl.java index 75ce0c58e2f..cdb27211e7f 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientTokenGeneratorServiceImpl.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientTokenGeneratorServiceImpl.java @@ -15,8 +15,8 @@ package org.eclipse.edc.iam.identitytrust.sts.defaults.service; import org.eclipse.edc.iam.identitytrust.sts.embedded.EmbeddedSecureTokenService; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClientTokenAdditionalParams; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccountTokenAdditionalParams; import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientTokenGeneratorService; import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsTokenGenerationProvider; import org.eclipse.edc.spi.iam.TokenRepresentation; @@ -36,15 +36,15 @@ public class StsClientTokenGeneratorServiceImpl implements StsClientTokenGeneratorService { - private static final Map> CLAIM_MAPPERS = Map.of( - PRESENTATION_TOKEN_CLAIM, StsClientTokenAdditionalParams::getAccessToken); + private static final Map> CLAIM_MAPPERS = Map.of( + PRESENTATION_TOKEN_CLAIM, StsAccountTokenAdditionalParams::getAccessToken); private final long tokenExpiration; private final StsTokenGenerationProvider tokenGenerationProvider; - private final Function keyFunction; + private final Function keyFunction; private final Clock clock; - public StsClientTokenGeneratorServiceImpl(StsTokenGenerationProvider tokenGenerationProvider, Function keyFunction, Clock clock, long tokenExpiration) { + public StsClientTokenGeneratorServiceImpl(StsTokenGenerationProvider tokenGenerationProvider, Function keyFunction, Clock clock, long tokenExpiration) { this.tokenGenerationProvider = tokenGenerationProvider; this.keyFunction = keyFunction; this.clock = clock; @@ -52,7 +52,7 @@ public StsClientTokenGeneratorServiceImpl(StsTokenGenerationProvider tokenGenera } @Override - public ServiceResult tokenFor(StsClient client, StsClientTokenAdditionalParams additionalParams) { + public ServiceResult tokenFor(StsAccount client, StsAccountTokenAdditionalParams additionalParams) { var embeddedTokenGenerator = new EmbeddedSecureTokenService(tokenGenerationProvider.tokenGeneratorFor(client), () -> keyFunction.apply(client), client::getPublicKeyReference, clock, tokenExpiration); diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsClientStore.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsAccountStore.java similarity index 63% rename from extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsClientStore.java rename to extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsAccountStore.java index 41e1f0f013c..198b907976a 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsClientStore.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsAccountStore.java @@ -14,8 +14,8 @@ package org.eclipse.edc.iam.identitytrust.sts.defaults.store; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; import org.eclipse.edc.spi.query.CriterionOperatorRegistry; import org.eclipse.edc.spi.query.QueryResolver; import org.eclipse.edc.spi.query.QuerySpec; @@ -31,41 +31,41 @@ import static java.lang.String.format; /** - * In memory implementation of {@link StsClientStore} + * In memory implementation of {@link StsAccountStore} */ -public class InMemoryStsClientStore implements StsClientStore { +public class InMemoryStsAccountStore implements StsAccountStore { // we store it by clientId - private final Map clients = new ConcurrentHashMap<>(); - private final QueryResolver queryResolver; + private final Map clients = new ConcurrentHashMap<>(); + private final QueryResolver queryResolver; - public InMemoryStsClientStore(CriterionOperatorRegistry criterionOperatorRegistry) { - queryResolver = new ReflectionBasedQueryResolver<>(StsClient.class, criterionOperatorRegistry); + public InMemoryStsAccountStore(CriterionOperatorRegistry criterionOperatorRegistry) { + queryResolver = new ReflectionBasedQueryResolver<>(StsAccount.class, criterionOperatorRegistry); } @Override - public StoreResult create(StsClient client) { + public StoreResult create(StsAccount client) { return Optional.ofNullable(clients.putIfAbsent(client.getClientId(), client)) - .map(old -> StoreResult.alreadyExists(format(CLIENT_EXISTS_TEMPLATE, client.getClientId()))) + .map(old -> StoreResult.alreadyExists(format(CLIENT_EXISTS_TEMPLATE, client.getClientId()))) .orElseGet(() -> StoreResult.success(client)); } @Override - public StoreResult update(StsClient stsClient) { - var prev = clients.replace(stsClient.getClientId(), stsClient); + public StoreResult update(StsAccount stsAccount) { + var prev = clients.replace(stsAccount.getClientId(), stsAccount); return Optional.ofNullable(prev) .map(a -> StoreResult.success()) - .orElse(StoreResult.notFound(format(CLIENT_NOT_FOUND_BY_ID_TEMPLATE, stsClient.getId()))); + .orElse(StoreResult.notFound(format(CLIENT_NOT_FOUND_BY_ID_TEMPLATE, stsAccount.getId()))); } @Override - public @NotNull Stream findAll(QuerySpec spec) { + public @NotNull Stream findAll(QuerySpec spec) { return queryResolver.query(clients.values().stream(), spec); } @Override - public StoreResult findById(String id) { + public StoreResult findById(String id) { return clients.values().stream() .filter(client -> client.getId().equals(id)) .findFirst() @@ -74,14 +74,14 @@ public StoreResult findById(String id) { } @Override - public StoreResult findByClientId(String clientId) { + public StoreResult findByClientId(String clientId) { return Optional.ofNullable(clients.get(clientId)) .map(StoreResult::success) .orElseGet(() -> StoreResult.notFound(format(CLIENT_NOT_FOUND_BY_CLIENT_ID_TEMPLATE, clientId))); } @Override - public StoreResult deleteById(String id) { + public StoreResult deleteById(String id) { return findById(id) .onSuccess(client -> clients.remove(client.getClientId())); } diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsClientTokenIssuanceIntegrationTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsAccountTokenIssuanceIntegrationTest.java similarity index 78% rename from extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsClientTokenIssuanceIntegrationTest.java rename to extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsAccountTokenIssuanceIntegrationTest.java index 8a50c244961..d77759f323b 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsClientTokenIssuanceIntegrationTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsAccountTokenIssuanceIntegrationTest.java @@ -17,11 +17,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.nimbusds.jwt.SignedJWT; import org.eclipse.edc.boot.vault.InMemoryVault; -import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsClientServiceImpl; +import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsAccountServiceImpl; import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsClientTokenGeneratorServiceImpl; -import org.eclipse.edc.iam.identitytrust.sts.defaults.store.InMemoryStsClientStore; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClientTokenAdditionalParams; +import org.eclipse.edc.iam.identitytrust.sts.defaults.store.InMemoryStsAccountStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccountTokenAdditionalParams; import org.eclipse.edc.junit.annotations.ComponentTest; import org.eclipse.edc.keys.KeyParserRegistryImpl; import org.eclipse.edc.keys.VaultPrivateKeyResolver; @@ -55,18 +55,18 @@ import static org.mockito.Mockito.mock; @ComponentTest -public class StsClientTokenIssuanceIntegrationTest { +public class StsAccountTokenIssuanceIntegrationTest { - private final InMemoryStsClientStore clientStore = new InMemoryStsClientStore(CriterionOperatorRegistryImpl.ofDefaults()); + private final InMemoryStsAccountStore clientStore = new InMemoryStsAccountStore(CriterionOperatorRegistryImpl.ofDefaults()); private final Vault vault = new InMemoryVault(mock()); private final KeyParserRegistry keyParserRegistry = new KeyParserRegistryImpl(); - private StsClientServiceImpl clientService; + private StsAccountServiceImpl clientService; private StsClientTokenGeneratorServiceImpl tokenGeneratorService; private PrivateKeyResolver privateKeyResolver; @BeforeEach void setup() { - clientService = new StsClientServiceImpl(clientStore, vault, new NoopTransactionContext()); + clientService = new StsAccountServiceImpl(clientStore, vault, new NoopTransactionContext(), new RandomStringGenerator()); keyParserRegistry.register(new PemParser(mock())); keyParserRegistry.register(new JwkParser(new ObjectMapper(), mock())); @@ -74,7 +74,7 @@ void setup() { tokenGeneratorService = new StsClientTokenGeneratorServiceImpl( client -> new JwtGenerationService(new DefaultJwsSignerProvider(privateKeyResolver)), - StsClient::getPrivateKeyAlias, + StsAccount::getPrivateKeyAlias, Clock.systemUTC(), 60 * 5); } @@ -83,21 +83,21 @@ void setup() { void authenticateAndGenerateToken() throws Exception { var id = "id"; var clientId = "client_id"; - var secretAlias = "client_id"; - var privateKeyAlis = "client_id"; + var secretAlias = "client_secret_alias"; + var privateKeyAlias = "client_id"; var audience = "aud"; var did = "did:example:subject"; var client = createClientBuilder(id) .clientId(clientId) - .privateKeyAlias(privateKeyAlis) + .privateKeyAlias(privateKeyAlias) .secretAlias(secretAlias) .publicKeyReference("public-key") .did(did) .build(); - var additional = StsClientTokenAdditionalParams.Builder.newInstance().audience(audience).build(); + var additional = StsAccountTokenAdditionalParams.Builder.newInstance().audience(audience).build(); - vault.storeSecret(privateKeyAlis, loadResourceFile("ec-privatekey.pem")); + vault.storeSecret(privateKeyAlias, loadResourceFile("ec-privatekey.pem")); var createResult = clientService.create(client); assertThat(createResult.succeeded()).isTrue(); @@ -118,22 +118,22 @@ void authenticateAndGenerateToken() throws Exception { void authenticateAndGenerateToken_withBearerAccessScope() throws Exception { var id = "id"; var clientId = "client_id"; - var secretAlias = "client_id"; - var privateKeyAlis = "client_id"; + var secretAlias = "client_secret_alias"; + var privateKeyAlias = "client_id"; var did = "did:example:subject"; var audience = "aud"; var scope = "scope:test"; var client = createClientBuilder(id) .clientId(clientId) - .privateKeyAlias(privateKeyAlis) + .privateKeyAlias(privateKeyAlias) .secretAlias(secretAlias) .did(did) .publicKeyReference("public-key") .build(); - var additional = StsClientTokenAdditionalParams.Builder.newInstance().audience(audience).bearerAccessScope(scope).build(); + var additional = StsAccountTokenAdditionalParams.Builder.newInstance().audience(audience).bearerAccessScope(scope).build(); - vault.storeSecret(privateKeyAlis, loadResourceFile("ec-privatekey.pem")); + vault.storeSecret(privateKeyAlias, loadResourceFile("ec-privatekey.pem")); var createResult = clientService.create(client); assertThat(createResult.succeeded()).isTrue(); @@ -154,23 +154,23 @@ void authenticateAndGenerateToken_withBearerAccessScope() throws Exception { void authenticateAndGenerateToken_withAccessToken() throws Exception { var id = "id"; var clientId = "client_id"; - var secretAlias = "client_id"; - var privateKeyAlis = "client_id"; + var secretAlias = "client_secret_alias"; + var privateKeyAlias = "client_id"; var audience = "aud"; var accessToken = "tokenTest"; var did = "did:example:subject"; var client = createClientBuilder(id) .clientId(clientId) - .privateKeyAlias(privateKeyAlis) + .privateKeyAlias(privateKeyAlias) .secretAlias(secretAlias) .publicKeyReference("public-key") .did(did) .build(); - var additional = StsClientTokenAdditionalParams.Builder.newInstance().audience(audience).accessToken(accessToken).build(); + var additional = StsAccountTokenAdditionalParams.Builder.newInstance().audience(audience).accessToken(accessToken).build(); - vault.storeSecret(privateKeyAlis, loadResourceFile("ec-privatekey.pem")); + vault.storeSecret(privateKeyAlias, loadResourceFile("ec-privatekey.pem")); var createResult = clientService.create(client); assertThat(createResult.succeeded()).isTrue(); @@ -192,7 +192,7 @@ void authenticateAndGenerateToken_withAccessToken() throws Exception { * Load content from a resource file. */ private String loadResourceFile(String file) throws IOException { - try (var resourceAsStream = StsClientTokenIssuanceIntegrationTest.class.getClassLoader().getResourceAsStream(file)) { + try (var resourceAsStream = StsAccountTokenIssuanceIntegrationTest.class.getClassLoader().getResourceAsStream(file)) { return new String(Objects.requireNonNull(resourceAsStream).readAllBytes()); } } diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtensionTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtensionTest.java index 6cbfa7c36fc..d607e802683 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtensionTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtensionTest.java @@ -14,7 +14,7 @@ package org.eclipse.edc.iam.identitytrust.sts.defaults; -import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsClientServiceImpl; +import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsAccountServiceImpl; import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsClientTokenGeneratorServiceImpl; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; @@ -30,7 +30,7 @@ public class StsDefaultServicesExtensionTest { void initialize(StsDefaultServicesExtension extension, ServiceExtensionContext context) { extension.initialize(context); - assertThat(extension.clientService()).isInstanceOf(StsClientServiceImpl.class); + assertThat(extension.clientService()).isInstanceOf(StsAccountServiceImpl.class); assertThat(extension.clientTokenService(context)).isInstanceOf(StsClientTokenGeneratorServiceImpl.class); } } diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultStoresExtensionTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultStoresExtensionTest.java index eee883f8835..1af07f2ebdf 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultStoresExtensionTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultStoresExtensionTest.java @@ -14,7 +14,7 @@ package org.eclipse.edc.iam.identitytrust.sts.defaults; -import org.eclipse.edc.iam.identitytrust.sts.defaults.store.InMemoryStsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.defaults.store.InMemoryStsAccountStore; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.junit.jupiter.api.Test; @@ -29,6 +29,6 @@ public class StsDefaultStoresExtensionTest { void initialize(StsDefaultStoresExtension extension, ServiceExtensionContext context) { extension.initialize(context); - assertThat(extension.clientStore()).isInstanceOf(InMemoryStsClientStore.class); + assertThat(extension.clientStore()).isInstanceOf(InMemoryStsAccountStore.class); } } diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsAccountServiceImplTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsAccountServiceImplTest.java new file mode 100644 index 00000000000..98a0ef85be8 --- /dev/null +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsAccountServiceImplTest.java @@ -0,0 +1,252 @@ +/* + * 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.sts.defaults.service; + +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.transaction.spi.NoopTransactionContext; +import org.eclipse.edc.transaction.spi.TransactionContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.eclipse.edc.iam.identitytrust.sts.spi.store.fixtures.TestFunctions.createClient; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class StsAccountServiceImplTest { + + private final StsAccountStore store = mock(); + private final Vault vault = mock(); + private final TransactionContext transactionContext = new NoopTransactionContext(); + private StsAccountServiceImpl clientService; + + @BeforeEach + void setup() { + clientService = new StsAccountServiceImpl(store, vault, transactionContext, parameters -> UUID.randomUUID().toString()); + } + + @Test + void create() { + var client = createClient("clientId"); + when(store.create(client)).thenReturn(StoreResult.success(client)); + when(vault.storeSecret(any(), any())).thenReturn(Result.success()); + + var inserted = clientService.create(client, "some-secret"); + + assertThat(inserted).isSucceeded(); + verify(store).create(client); + verifyNoMoreInteractions(store); + } + + @Test + void create_whenAlreadyExists() { + when(store.create(any())).thenReturn(StoreResult.alreadyExists("foo")); + + var client = createClient("clientId"); + assertThat(clientService.create(client, "some-secret")) + .isFailed() + .detail().contains("foo"); + + verifyNoMoreInteractions(vault); + } + + @Test + void findByClientId() { + var clientId = "clientId"; + var client = createClient(clientId); + + when(store.findByClientId(clientId)).thenReturn(StoreResult.success(client)); + + var inserted = clientService.findByClientId(clientId); + + assertThat(inserted).isSucceeded().isEqualTo(client); + verify(store).findByClientId(clientId); + verifyNoMoreInteractions(store); + } + + @Test + void authenticate() { + var clientId = "clientId"; + var secret = "secret"; + var client = createClient(clientId); + when(vault.resolveSecret(client.getSecretAlias())).thenReturn(secret); + + var inserted = clientService.authenticate(client, secret); + + assertThat(inserted).isSucceeded(); + verify(vault).resolveSecret(client.getSecretAlias()); + } + + @Test + void update() { + when(store.update(any())).thenReturn(StoreResult.success()); + + var client = createClient("clientId"); + assertThat(clientService.update(client)).isSucceeded(); + verify(store).update(client); + verifyNoInteractions(vault); + } + + @Test + void update_whenNotExists() { + when(store.update(any())).thenReturn(StoreResult.notFound("foo")); + + var client = createClient("clientId"); + assertThat(clientService.update(client)).isFailed() + .detail().contains("foo"); + verify(store).update(client); + verifyNoMoreInteractions(vault, store); + } + + @Test + void updateSecret() { + var client = createClient("clientId"); + var oldAlias = client.getSecretAlias(); + when(store.findById(any())).thenReturn(StoreResult.success(client)); + when(store.update(any())).thenReturn(StoreResult.success()); + when(vault.deleteSecret(eq(oldAlias))).thenReturn(Result.success()); + when(vault.storeSecret(eq("new-alias"), eq("new-secret"))).thenReturn(Result.success()); + + assertThat(clientService.updateSecret(client.getId(), "new-alias", "new-secret")).isSucceeded(); + + + verify(store).findById(client.getId()); + verify(vault).deleteSecret(oldAlias); + verify(vault).storeSecret("new-alias", "new-secret"); + verify(store).update(any()); + verifyNoMoreInteractions(store, vault); + } + + @Test + void updateSecret_aliasNull() { + var client = createClient("clientId"); + + assertThatThrownBy(() -> clientService.updateSecret(client.getId(), null, "some-secret")) + .isInstanceOf(NullPointerException.class); + verifyNoInteractions(store, vault); + } + + @Test + void updateSecret_secretNull() { + var client = createClient("clientId"); + var oldAlias = client.getSecretAlias(); + when(store.findById(any())).thenReturn(StoreResult.success(client)); + when(store.update(any())).thenReturn(StoreResult.success()); + when(vault.deleteSecret(eq(oldAlias))).thenReturn(Result.success()); + when(vault.storeSecret(eq("new-alias"), anyString())).thenReturn(Result.success()); + + assertThat(clientService.updateSecret(client.getId(), "new-alias")).isSucceeded(); + + verify(store).findById(client.getId()); + verify(vault).deleteSecret(oldAlias); + verify(vault).storeSecret(eq("new-alias"), anyString()); + verify(store).update(any()); + verifyNoMoreInteractions(store, vault); + } + + @Test + void updateSecret_whenNotExists() { + var client = createClient("clientId"); + when(store.findById(any())).thenReturn(StoreResult.notFound("foo")); + + assertThat(clientService.updateSecret(client.getId(), "new-alias")).isFailed().detail() + .isEqualTo("foo"); + + verify(store).findById(client.getId()); + verifyNoMoreInteractions(store, vault); + } + + @Test + void updateSecret_vaultFailsToDelete() { + var client = createClient("clientId"); + var oldAlias = client.getSecretAlias(); + when(store.findById(any())).thenReturn(StoreResult.success(client)); + when(store.update(any())).thenReturn(StoreResult.success()); + when(vault.deleteSecret(eq(oldAlias))).thenReturn(Result.failure("foo")); + + assertThat(clientService.updateSecret(client.getId(), "new-alias")).isFailed(); + + verify(store).findById(client.getId()); + verify(vault).deleteSecret(oldAlias); + verify(store).update(any()); + verifyNoMoreInteractions(store, vault); + } + + @Test + void deleteById() { + when(store.deleteById(any())).thenReturn(StoreResult.success(createClient("test-id"))); + when(vault.deleteSecret(any())).thenReturn(Result.success()); + assertThat(clientService.deleteById("test-id")).isSucceeded(); + verify(store).deleteById("test-id"); + verify(vault).deleteSecret(any()); + verifyNoMoreInteractions(store, vault); + } + + @Test + void deleteById_whenNotExists() { + when(store.deleteById(any())).thenReturn(StoreResult.notFound("foo")); + assertThat(clientService.deleteById("test-id")).isFailed().detail().isEqualTo("foo"); + verify(store).deleteById("test-id"); + verifyNoMoreInteractions(store, vault); + } + + @Test + void query() { + var id1 = createClient("id1"); + var id2 = createClient("id2"); + when(store.findAll(any())).thenReturn(Stream.of(id1, id2)); + + assertThat(clientService.queryAccounts(QuerySpec.max())) + .containsExactlyInAnyOrder(id1, id2); + } + + @Test + void query_noResults() { + when(store.findAll(any())).thenReturn(Stream.of()); + assertThat(clientService.queryAccounts(QuerySpec.max())) + .isEmpty(); + } + + @Test + void findById() { + var client = createClient("test-id"); + when(store.findById(anyString())).thenReturn(StoreResult.success(client)); + assertThat(clientService.findById("test-id")).isSucceeded() + .usingRecursiveComparison() + .isEqualTo(client); + } + + @Test + void findById_whenNotExists() { + when(store.findById(anyString())).thenReturn(StoreResult.notFound("foo")); + assertThat(clientService.findById("test-id")).isFailed() + .detail().isEqualTo("foo"); + } +} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientTokenGeneratorServiceImplTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsAccountTokenGeneratorServiceImplTest.java similarity index 87% rename from extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientTokenGeneratorServiceImplTest.java rename to extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsAccountTokenGeneratorServiceImplTest.java index 23dc0a86c44..d02b0cb0f2b 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientTokenGeneratorServiceImplTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsAccountTokenGeneratorServiceImplTest.java @@ -15,7 +15,7 @@ package org.eclipse.edc.iam.identitytrust.sts.defaults.service; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClientTokenAdditionalParams; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccountTokenAdditionalParams; import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsTokenGenerationProvider; import org.eclipse.edc.spi.iam.TokenRepresentation; import org.eclipse.edc.spi.result.Result; @@ -34,7 +34,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class StsClientTokenGeneratorServiceImplTest { +public class StsAccountTokenGeneratorServiceImplTest { public static final long TOKEN_EXPIRATION = 60 * 5; private final StsTokenGenerationProvider tokenGenerationProvider = mock(); @@ -53,7 +53,7 @@ void tokenFor() { when(tokenGenerationProvider.tokenGeneratorFor(client)).thenReturn(tokenGenerator); when(tokenGenerator.generate(any(), any(TokenDecorator[].class))).thenReturn(Result.success(token)); - var inserted = clientTokenService.tokenFor(client, StsClientTokenAdditionalParams.Builder.newInstance().audience("aud").build()); + var inserted = clientTokenService.tokenFor(client, StsAccountTokenAdditionalParams.Builder.newInstance().audience("aud").build()); assertThat(inserted).isSucceeded().usingRecursiveComparison().isEqualTo(token); } @@ -64,7 +64,7 @@ void tokenFor_error_whenGeneratorFails() { when(tokenGenerationProvider.tokenGeneratorFor(client)).thenReturn(tokenGenerator); when(tokenGenerator.generate(any(), any(TokenDecorator[].class))).thenReturn(Result.failure("failure")); - var inserted = clientTokenService.tokenFor(client, StsClientTokenAdditionalParams.Builder.newInstance().audience("aud").build()); + var inserted = clientTokenService.tokenFor(client, StsAccountTokenAdditionalParams.Builder.newInstance().audience("aud").build()); assertThat(inserted).isFailed() .satisfies(serviceFailure -> { diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientServiceImplTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientServiceImplTest.java deleted file mode 100644 index cbde0899eb4..00000000000 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientServiceImplTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.sts.defaults.service; - -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; -import org.eclipse.edc.spi.result.StoreResult; -import org.eclipse.edc.spi.security.Vault; -import org.eclipse.edc.transaction.spi.NoopTransactionContext; -import org.eclipse.edc.transaction.spi.TransactionContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.eclipse.edc.iam.identitytrust.sts.spi.store.fixtures.TestFunctions.createClient; -import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -public class StsClientServiceImplTest { - - private final StsClientStore store = mock(); - private final Vault vault = mock(); - private final TransactionContext transactionContext = new NoopTransactionContext(); - private StsClientServiceImpl clientService; - - @BeforeEach - void setup() { - clientService = new StsClientServiceImpl(store, vault, transactionContext); - } - - @Test - void create() { - var client = createClient("clientId"); - when(store.create(client)).thenReturn(StoreResult.success(client)); - - var inserted = clientService.create(client); - - assertThat(inserted).isSucceeded(); - verify(store).create(client); - verifyNoMoreInteractions(store); - } - - @Test - void findById() { - var clientId = "clientId"; - var client = createClient(clientId); - - when(store.findByClientId(clientId)).thenReturn(StoreResult.success(client)); - - var inserted = clientService.findByClientId(clientId); - - assertThat(inserted).isSucceeded().isEqualTo(client); - verify(store).findByClientId(clientId); - verifyNoMoreInteractions(store); - } - - @Test - void authenticate() { - var clientId = "clientId"; - var secret = "secret"; - var client = createClient(clientId); - when(vault.resolveSecret(client.getSecretAlias())).thenReturn(secret); - - var inserted = clientService.authenticate(client, secret); - - assertThat(inserted).isSucceeded(); - verify(vault).resolveSecret(client.getSecretAlias()); - } - -} diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsClientStoreTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsAccountStoreTest.java similarity index 68% rename from extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsClientStoreTest.java rename to extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsAccountStoreTest.java index 7401194a258..4f7f636d944 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsClientStoreTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/store/InMemoryStsAccountStoreTest.java @@ -14,22 +14,22 @@ package org.eclipse.edc.iam.identitytrust.sts.defaults.store; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.fixtures.StsClientStoreTestBase; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.fixtures.StsAccountStoreTestBase; import org.eclipse.edc.query.CriterionOperatorRegistryImpl; import org.junit.jupiter.api.BeforeEach; -public class InMemoryStsClientStoreTest extends StsClientStoreTestBase { +public class InMemoryStsAccountStoreTest extends StsAccountStoreTestBase { - private InMemoryStsClientStore store; + private InMemoryStsAccountStore store; @BeforeEach void setUp() { - store = new InMemoryStsClientStore(CriterionOperatorRegistryImpl.ofDefaults()); + store = new InMemoryStsAccountStore(CriterionOperatorRegistryImpl.ofDefaults()); } @Override - protected StsClientStore getStsClientStore() { + protected StsAccountStore getStsClientStore() { return store; } } diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/main/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenService.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/main/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenService.java index de2b4386666..a224475e10e 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/main/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenService.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/main/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenService.java @@ -78,7 +78,7 @@ private Result createAndAcceptAccessToken(Map claims, Stri return createAccessToken(claims, scope) .compose(tokenRepresentation -> success(tokenRepresentation.getToken())) .onSuccess(withClaim(PRESENTATION_TOKEN_CLAIM, consumer)) - .mapTo(); + .mapEmpty(); } private Result createAccessToken(Map claims, String bearerAccessScope) { diff --git a/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStore.java b/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsAccountStore.java similarity index 77% rename from extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStore.java rename to extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsAccountStore.java index 1242eb4d4cf..fdeaa80560a 100644 --- a/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStore.java +++ b/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsAccountStore.java @@ -15,8 +15,8 @@ package org.eclipse.edc.iam.identitytrust.sts.store; import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; import org.eclipse.edc.iam.identitytrust.sts.store.schema.StsClientStatements; import org.eclipse.edc.spi.persistence.EdcPersistenceException; import org.eclipse.edc.spi.query.QuerySpec; @@ -35,18 +35,18 @@ import static java.lang.String.format; -public class SqlStsClientStore extends AbstractSqlStore implements StsClientStore { +public class SqlStsAccountStore extends AbstractSqlStore implements StsAccountStore { private final StsClientStatements statements; - public SqlStsClientStore(DataSourceRegistry dataSourceRegistry, String dataSourceName, TransactionContext transactionContext, - ObjectMapper objectMapper, StsClientStatements statements, QueryExecutor queryExecutor) { + public SqlStsAccountStore(DataSourceRegistry dataSourceRegistry, String dataSourceName, TransactionContext transactionContext, + ObjectMapper objectMapper, StsClientStatements statements, QueryExecutor queryExecutor) { super(dataSourceRegistry, dataSourceName, transactionContext, objectMapper, queryExecutor); this.statements = statements; } @Override - public StoreResult create(StsClient client) { + public StoreResult create(StsAccount client) { return transactionContext.execute(() -> { try (var connection = getConnection()) { if (findById(connection, client.getId()) != null) { @@ -73,14 +73,14 @@ public StoreResult create(StsClient client) { } @Override - public StoreResult update(StsClient stsClient) { + public StoreResult update(StsAccount stsAccount) { return transactionContext.execute(() -> { try (var connection = getConnection()) { - if (findById(connection, stsClient.getId()) != null) { - updateInternal(connection, stsClient); + if (findById(connection, stsAccount.getId()) != null) { + updateInternal(connection, stsAccount); return StoreResult.success(); } else { - return StoreResult.notFound(format(CLIENT_NOT_FOUND_BY_ID_TEMPLATE, stsClient.getId())); + return StoreResult.notFound(format(CLIENT_NOT_FOUND_BY_ID_TEMPLATE, stsAccount.getId())); } } catch (Exception e) { throw new EdcPersistenceException(e.getMessage(), e); @@ -89,7 +89,7 @@ public StoreResult update(StsClient stsClient) { } @Override - public @NotNull Stream findAll(QuerySpec spec) { + public @NotNull Stream findAll(QuerySpec spec) { return transactionContext.execute(() -> { Objects.requireNonNull(spec); @@ -103,7 +103,7 @@ public StoreResult update(StsClient stsClient) { } @Override - public StoreResult findById(String id) { + public StoreResult findById(String id) { return transactionContext.execute(() -> { try (var connection = getConnection()) { var client = findById(connection, id); @@ -118,7 +118,7 @@ public StoreResult findById(String id) { } @Override - public StoreResult findByClientId(String clientId) { + public StoreResult findByClientId(String clientId) { return transactionContext.execute(() -> { try (var connection = getConnection()) { var client = findByClientIdId(connection, clientId); @@ -133,7 +133,7 @@ public StoreResult findByClientId(String clientId) { } @Override - public StoreResult deleteById(String id) { + public StoreResult deleteById(String id) { return transactionContext.execute(() -> { try (var connection = getConnection()) { var entity = findById(connection, id); @@ -150,31 +150,31 @@ public StoreResult deleteById(String id) { }); } - private void updateInternal(Connection connection, StsClient stsClient) { + private void updateInternal(Connection connection, StsAccount stsAccount) { queryExecutor.execute(connection, statements.getUpdateTemplate(), - stsClient.getId(), - stsClient.getName(), - stsClient.getClientId(), - stsClient.getDid(), - stsClient.getSecretAlias(), - stsClient.getPrivateKeyAlias(), - stsClient.getPublicKeyReference(), - stsClient.getCreatedAt(), - stsClient.getId()); + stsAccount.getId(), + stsAccount.getName(), + stsAccount.getClientId(), + stsAccount.getDid(), + stsAccount.getSecretAlias(), + stsAccount.getPrivateKeyAlias(), + stsAccount.getPublicKeyReference(), + stsAccount.getCreatedAt(), + stsAccount.getId()); } - private StsClient findById(Connection connection, String id) { + private StsAccount findById(Connection connection, String id) { var sql = statements.getFindByTemplate(); return queryExecutor.single(connection, false, this::mapResultSet, sql, id); } - private StsClient findByClientIdId(Connection connection, String id) { + private StsAccount findByClientIdId(Connection connection, String id) { var sql = statements.getFindByClientIdTemplate(); return queryExecutor.single(connection, false, this::mapResultSet, sql, id); } - private StsClient mapResultSet(ResultSet resultSet) throws Exception { - return StsClient.Builder.newInstance() + private StsAccount mapResultSet(ResultSet resultSet) throws Exception { + return StsAccount.Builder.newInstance() .id(resultSet.getString(statements.getIdColumn())) .did(resultSet.getString(statements.getDidColumn())) .name(resultSet.getString(statements.getNameColumn())) diff --git a/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStoreExtension.java b/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStoreExtension.java index a805a2614d4..8522beff987 100644 --- a/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStoreExtension.java +++ b/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStoreExtension.java @@ -15,7 +15,7 @@ package org.eclipse.edc.iam.identitytrust.sts.store; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; import org.eclipse.edc.iam.identitytrust.sts.store.schema.StsClientStatements; import org.eclipse.edc.iam.identitytrust.sts.store.schema.postgres.PostgresDialectStatements; import org.eclipse.edc.runtime.metamodel.annotation.Extension; @@ -32,8 +32,8 @@ import static org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry.DEFAULT_DATASOURCE; -@Provides({ StsClientStore.class }) -@Extension(value = "SQL sts client store") +@Provides({ StsAccountStore.class }) +@Extension(value = "SQL sts accounts store") public class SqlStsClientStoreExtension implements ServiceExtension { @Setting(value = "The datasource to be used", defaultValue = DEFAULT_DATASOURCE) @@ -61,10 +61,10 @@ public class SqlStsClientStoreExtension implements ServiceExtension { public void initialize(ServiceExtensionContext context) { var dataSourceName = context.getSetting(DATASOURCE_NAME, DEFAULT_DATASOURCE); - var sqlStore = new SqlStsClientStore(dataSourceRegistry, dataSourceName, transactionContext, typeManager.getMapper(), + var sqlStore = new SqlStsAccountStore(dataSourceRegistry, dataSourceName, transactionContext, typeManager.getMapper(), getStatementImpl(), queryExecutor); - context.registerService(StsClientStore.class, sqlStore); + context.registerService(StsAccountStore.class, sqlStore); sqlSchemaBootstrapper.addStatementFromResource(dataSourceName, "sts-client-schema.sql"); } diff --git a/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/schema/StsClientStatements.java b/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/schema/StsClientStatements.java index 5a71e407ae3..9c764ab56fd 100644 --- a/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/schema/StsClientStatements.java +++ b/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/schema/StsClientStatements.java @@ -14,16 +14,16 @@ package org.eclipse.edc.iam.identitytrust.sts.store.schema; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.sql.statement.SqlStatements; import org.eclipse.edc.sql.translation.SqlQueryStatement; /** - * Defines all statements that are needed for the {@link StsClient} store + * Defines all statements that are needed for the {@link StsAccount} store */ public interface StsClientStatements extends SqlStatements { - + default String getIdColumn() { return "id"; } diff --git a/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/schema/postgres/StsClientMapping.java b/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/schema/postgres/StsClientMapping.java index 3f9a4397b7a..8d26c385bfb 100644 --- a/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/schema/postgres/StsClientMapping.java +++ b/extensions/common/store/sql/sts-client-store-sql/src/main/java/org/eclipse/edc/iam/identitytrust/sts/store/schema/postgres/StsClientMapping.java @@ -14,12 +14,12 @@ package org.eclipse.edc.iam.identitytrust.sts.store.schema.postgres; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; import org.eclipse.edc.iam.identitytrust.sts.store.schema.StsClientStatements; import org.eclipse.edc.sql.translation.TranslationMapping; /** - * Maps fields of a {@link StsClient} onto the + * Maps fields of a {@link StsAccount} onto the * corresponding SQL schema (= column names) */ public class StsClientMapping extends TranslationMapping { diff --git a/extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStoreExtensionTest.java b/extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsAccountStoreExtensionTest.java similarity index 88% rename from extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStoreExtensionTest.java rename to extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsAccountStoreExtensionTest.java index a3ca991da53..25787206563 100644 --- a/extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStoreExtensionTest.java +++ b/extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsAccountStoreExtensionTest.java @@ -14,7 +14,7 @@ package org.eclipse.edc.iam.identitytrust.sts.store; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; import org.eclipse.edc.json.JacksonTypeManager; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; @@ -33,7 +33,7 @@ import static org.mockito.Mockito.when; @ExtendWith(DependencyInjectionExtension.class) -public class SqlStsClientStoreExtensionTest { +public class SqlStsAccountStoreExtensionTest { @BeforeEach void setUp(ServiceExtensionContext context) { @@ -48,8 +48,8 @@ void shouldInitializeTheStore(SqlStsClientStoreExtension extension, ServiceExten extension.initialize(context); - var service = context.getService(StsClientStore.class); - assertThat(service).isInstanceOf(SqlStsClientStore.class); + var service = context.getService(StsAccountStore.class); + assertThat(service).isInstanceOf(SqlStsAccountStore.class); verify(config).getString(eq(DATASOURCE_NAME), any()); } diff --git a/extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStoreTest.java b/extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsAccountStoreTest.java similarity index 84% rename from extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStoreTest.java rename to extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsAccountStoreTest.java index dab16bb1f77..e9f51ce7b0b 100644 --- a/extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsClientStoreTest.java +++ b/extensions/common/store/sql/sts-client-store-sql/src/test/java/org/eclipse/edc/iam/identitytrust/sts/store/SqlStsAccountStoreTest.java @@ -15,8 +15,8 @@ package org.eclipse.edc.iam.identitytrust.sts.store; import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.fixtures.StsClientStoreTestBase; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.fixtures.StsAccountStoreTestBase; import org.eclipse.edc.iam.identitytrust.sts.store.schema.BaseSqlDialectStatements; import org.eclipse.edc.iam.identitytrust.sts.store.schema.postgres.PostgresDialectStatements; import org.eclipse.edc.json.JacksonTypeManager; @@ -31,18 +31,18 @@ @ComponentTest @ExtendWith(PostgresqlStoreSetupExtension.class) -public class SqlStsClientStoreTest extends StsClientStoreTestBase { +public class SqlStsAccountStoreTest extends StsAccountStoreTestBase { private final BaseSqlDialectStatements sqlStatements = new PostgresDialectStatements(); - private SqlStsClientStore stsClientStore; + private SqlStsAccountStore stsClientStore; @BeforeEach void setUp(PostgresqlStoreSetupExtension setupExtension, QueryExecutor queryExecutor) { var typeManager = new JacksonTypeManager(); typeManager.registerTypes(PolicyRegistrationTypes.TYPES.toArray(Class[]::new)); - stsClientStore = new SqlStsClientStore(setupExtension.getDataSourceRegistry(), setupExtension.getDatasourceName(), + stsClientStore = new SqlStsAccountStore(setupExtension.getDataSourceRegistry(), setupExtension.getDatasourceName(), setupExtension.getTransactionContext(), new ObjectMapper(), sqlStatements, queryExecutor); var schema = TestUtils.getResourceFileContentAsString("sts-client-schema.sql"); @@ -55,7 +55,7 @@ void tearDown(PostgresqlStoreSetupExtension setupExtension) { } @Override - protected StsClientStore getStsClientStore() { + protected StsAccountStore getStsClientStore() { return stsClientStore; } } diff --git a/resources/openapi/sts-accounts-api.version b/resources/openapi/sts-accounts-api.version new file mode 100644 index 00000000000..d3296346f97 --- /dev/null +++ b/resources/openapi/sts-accounts-api.version @@ -0,0 +1 @@ +extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/resources/sts-accounts-api-version.json \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 92462d48c24..0e9e09151d4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -137,6 +137,7 @@ include(":extensions:common:iam:identity-trust:identity-trust-sts:identity-trust include(":extensions:common:iam:identity-trust:identity-trust-sts:identity-trust-sts-core") include(":extensions:common:iam:identity-trust:identity-trust-sts:identity-trust-sts-remote-client") include(":extensions:common:iam:identity-trust:identity-trust-sts:identity-trust-sts-api") +include(":extensions:common:iam:identity-trust:identity-trust-sts:identity-trust-sts-accounts-api") include(":extensions:common:iam:identity-trust:identity-trust-sts:identity-trust-sts-client-configuration") include(":extensions:common:iam:identity-trust:identity-trust-sts:lib:identity-trust-sts-remote-lib") include(":extensions:common:iam:identity-trust:identity-trust-issuers-configuration") diff --git a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsClient.java b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsAccount.java similarity index 83% rename from spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsClient.java rename to spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsAccount.java index e447256d535..074dd6be799 100644 --- a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsClient.java +++ b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsAccount.java @@ -20,9 +20,9 @@ import java.util.Objects; /** - * The {@link StsClient} contains information about STS clients. + * The {@link StsAccount} contains information about STS clients. */ -public class StsClient extends Entity { +public class StsAccount extends Entity { private String clientId; private String did; private String name; @@ -30,11 +30,11 @@ public class StsClient extends Entity { private String privateKeyAlias; private String publicKeyReference; - private StsClient() { + private StsAccount() { } /** - * The alias of the {@link StsClient} secret stored in the {@link Vault} + * The alias of the {@link StsAccount} secret stored in the {@link Vault} * * @return The secret alias */ @@ -43,7 +43,7 @@ public String getSecretAlias() { } /** - * The alias of the {@link StsClient} private key stored in the {@link Vault} + * The alias of the {@link StsAccount} private key stored in the {@link Vault} * * @return The private key alias */ @@ -53,7 +53,7 @@ public String getPrivateKeyAlias() { /** - * The name of the {@link StsClient} + * The name of the {@link StsAccount} * * @return The name */ @@ -62,7 +62,7 @@ public String getName() { } /** - * The client_id of the {@link StsClient} + * The client_id of the {@link StsAccount} * * @return The clientId */ @@ -80,7 +80,7 @@ public String getDid() { } /** - * A reference, e.g. a URL, where the public key that corresponds to the {@link StsClient#getPrivateKeyAlias()} can be obtained. + * A reference, e.g. a URL, where the public key that corresponds to the {@link StsAccount#getPrivateKeyAlias()} can be obtained. * In most situations this will be a DID with a key identifier, e.g. "did:web:foo:bar#key-1". *

* This can be null, in which case there has to be an out-of-band public key exchange (PKI), for example a well-known location. @@ -91,12 +91,16 @@ public String getPublicKeyReference() { return publicKeyReference; } + public void updateSecretAlias(String secretAlias) { + this.secretAlias = secretAlias; + } + - public static class Builder extends Entity.Builder { + public static class Builder extends Entity.Builder { private Builder() { - super(new StsClient()); + super(new StsAccount()); } public static Builder newInstance() { @@ -115,7 +119,7 @@ public Builder self() { } @Override - public StsClient build() { + public StsAccount build() { Objects.requireNonNull(entity.id, "Client id"); Objects.requireNonNull(entity.clientId, "Client client_id"); Objects.requireNonNull(entity.name, "Client name"); diff --git a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsClientTokenAdditionalParams.java b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsAccountTokenAdditionalParams.java similarity index 75% rename from spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsClientTokenAdditionalParams.java rename to spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsAccountTokenAdditionalParams.java index 07501d6ccfe..15936459176 100644 --- a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsClientTokenAdditionalParams.java +++ b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/model/StsAccountTokenAdditionalParams.java @@ -19,17 +19,17 @@ import java.util.Objects; /** - * The {@link StsClientTokenAdditionalParams} contains additional parameters for the {@link StsClientTokenGeneratorService } - * when creating the Self-Issued ID token for the {@link StsClient} + * The {@link StsAccountTokenAdditionalParams} contains additional parameters for the {@link StsClientTokenGeneratorService } + * when creating the Self-Issued ID token for the {@link StsAccount} */ -public class StsClientTokenAdditionalParams { +public class StsAccountTokenAdditionalParams { private String bearerAccessScope; private String audience; private String accessToken; - private StsClientTokenAdditionalParams() { + private StsAccountTokenAdditionalParams() { } @@ -47,14 +47,14 @@ public String getAccessToken() { public static class Builder { - private final StsClientTokenAdditionalParams params; + private final StsAccountTokenAdditionalParams params; - private Builder(StsClientTokenAdditionalParams params) { + private Builder(StsAccountTokenAdditionalParams params) { this.params = params; } public static Builder newInstance() { - return new Builder(new StsClientTokenAdditionalParams()); + return new Builder(new StsAccountTokenAdditionalParams()); } @@ -73,7 +73,7 @@ public Builder accessToken(String accessToken) { return this; } - public StsClientTokenAdditionalParams build() { + public StsAccountTokenAdditionalParams build() { Objects.requireNonNull(params.audience, "Param audience missing"); return params; } diff --git a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsAccountService.java b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsAccountService.java new file mode 100644 index 00000000000..94c9659ddd8 --- /dev/null +++ b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsAccountService.java @@ -0,0 +1,123 @@ +/* + * 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.sts.spi.service; + +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.ServiceResult; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * Mediates access to, modification and authentication of {@link StsAccount}s. + */ +@ExtensionPoint +public interface StsAccountService { + + /** + * Create the {@link StsAccount} + * + * @param client The client + * @return successful when the client is created, failure otherwise + * @deprecated Use {@link StsAccountService#create(StsAccount, String)} instead + */ + + @Deprecated(since = "0.9.0") + ServiceResult create(StsAccount client); + + /** + * Create the {@link StsAccount} + * + * @param client The client + * @return successful when the client is created, failure otherwise + */ + + ServiceResult create(StsAccount client, @Nullable String clientSecret); + + /** + * Returns an {@link StsAccount} by its id + * + * @param id id of the client + * @return the client successful if found, failure otherwise + */ + ServiceResult findByClientId(String id); + + /** + * Updates an existing {@link StsAccount}, overwriting all values with the given new object. + * + * @param client the new account + * @return A successful result, or a failure indicating what went wrong. + */ + ServiceResult update(StsAccount client); + + /** + * Updates the client secret associated with this {@link StsAccount}. The old secret is removed from the {@link org.eclipse.edc.spi.security.Vault}, + * and the new secret is stored using the given alias. If the new secret is {@code null}, one is generated at random. + * + * @param id the ID of the {@link StsAccount} to update + * @param secretAlias The alias under which the new secret is stored in the {@link org.eclipse.edc.spi.security.Vault} + * @param newSecret The new client secret. If null, a new one is generated. + * @return A successful result, or a failure indicating what went wrong. + */ + ServiceResult updateSecret(String id, String secretAlias, @Nullable String newSecret); + + /** + * Updates the client secret associated with this {@link StsAccount}. The old secret is removed from the {@link org.eclipse.edc.spi.security.Vault}, + * and the new secret is stored using the given alias. A new secret is generated at random. + * + * @param id the ID of the {@link StsAccount} to update + * @param newAlias The alias under which the new secret is stored in the {@link org.eclipse.edc.spi.security.Vault} + * @return A successful result, or a failure indicating what went wrong. + */ + default ServiceResult updateSecret(String id, String newAlias) { + return updateSecret(id, newAlias, null); + } + + /** + * Deletes an {@link StsAccount} by its ID. + * + * @param id The (database) ID + * @return A successful result, or a failure indicating what went wrong. + */ + ServiceResult deleteById(String id); + + /** + * Queries the storage for a collection of {@link StsAccount} objects that conform to the given {@link QuerySpec} + * + * @param querySpec the query + * @return A collection of accounts, potentially empty. Never null. + */ + Collection queryAccounts(QuerySpec querySpec); + + /** + * Authenticate an {@link StsAccount} given the input secret + * + * @param client The client to authenticate + * @param secret The client secret in input to check + * @return the successful if authenticated, failure otherwise + */ + ServiceResult authenticate(StsAccount client, String secret); + + /** + * Finds a particular {@link StsAccount} by its (database-)ID + * + * @param accountId the (database-)ID + * @return a {@link ServiceResult} containing the account, a failure otherwise. + */ + ServiceResult findById(String accountId); + +} diff --git a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientSecretGenerator.java b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientSecretGenerator.java new file mode 100644 index 00000000000..289bb8422d2 --- /dev/null +++ b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientSecretGenerator.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 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.sts.spi.service; + +import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; +import org.jetbrains.annotations.Nullable; + +/** + * Hook point to provide custom code for generating STS Account client_secrets. + */ +@ExtensionPoint +@FunctionalInterface +public interface StsClientSecretGenerator { + /** + * Generates a client secret as string, taking an optional argument. By default, + * + * @param parameters Optional generator arguments, such as a salt value. Not all generators require this value. + * @return a randomly generated client secret. + */ + String generateClientSecret(@Nullable Object parameters); +} diff --git a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientService.java b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientService.java deleted file mode 100644 index cbb3b7ecb09..00000000000 --- a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientService.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.sts.spi.service; - -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; -import org.eclipse.edc.spi.result.ServiceResult; - -/** - * Mediates access to, modification and authentication of {@link StsClient}s. - */ -@ExtensionPoint -public interface StsClientService { - - /** - * Create the {@link StsClient} - * - * @param client The client - * @return successful when the client is created, failure otherwise - */ - - ServiceResult create(StsClient client); - - /** - * Returns an {@link StsClient} by its id - * - * @param id id of the client - * @return the client successful if found, failure otherwise - */ - ServiceResult findByClientId(String id); - - /** - * Authenticate an {@link StsClient} given the input secret - * - * @param client The client to authenticate - * @param secret The client secret in input to check - * @return the successful if authenticated, failure otherwise - */ - ServiceResult authenticate(StsClient client, String secret); - -} diff --git a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientTokenGeneratorService.java b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientTokenGeneratorService.java index fbe1e573371..27278ae39ec 100644 --- a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientTokenGeneratorService.java +++ b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsClientTokenGeneratorService.java @@ -14,8 +14,8 @@ package org.eclipse.edc.iam.identitytrust.sts.spi.service; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClientTokenAdditionalParams; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccountTokenAdditionalParams; import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; import org.eclipse.edc.spi.iam.TokenRepresentation; import org.eclipse.edc.spi.result.ServiceResult; @@ -27,12 +27,12 @@ public interface StsClientTokenGeneratorService { /** - * Mint a Self-Issued ID Token for the input {@link StsClient} with additional parameters. + * Mint a Self-Issued ID Token for the input {@link StsAccount} with additional parameters. * - * @param client The {@link StsClient} - * @param additionalParams The {@link StsClientTokenAdditionalParams} + * @param client The {@link StsAccount} + * @param additionalParams The {@link StsAccountTokenAdditionalParams} * @return The issued token if successful, failure otherwise */ - ServiceResult tokenFor(StsClient client, StsClientTokenAdditionalParams additionalParams); + ServiceResult tokenFor(StsAccount client, StsAccountTokenAdditionalParams additionalParams); } diff --git a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsTokenGenerationProvider.java b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsTokenGenerationProvider.java index 78f60481731..4b13e67fa09 100644 --- a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsTokenGenerationProvider.java +++ b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/service/StsTokenGenerationProvider.java @@ -14,7 +14,7 @@ package org.eclipse.edc.iam.identitytrust.sts.spi.service; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; import org.eclipse.edc.token.spi.TokenGenerationService; @@ -27,10 +27,10 @@ public interface StsTokenGenerationProvider { /** - * Returns a {@link TokenGenerationService} for the input {@link StsClient} + * Returns a {@link TokenGenerationService} for the input {@link StsAccount} * - * @param client The {@link StsClient} + * @param stsAccount The {@link StsAccount} * @return The {@link TokenGenerationService} */ - TokenGenerationService tokenGeneratorFor(StsClient client); + TokenGenerationService tokenGeneratorFor(StsAccount stsAccount); } diff --git a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/StsClientStore.java b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/StsAccountStore.java similarity index 69% rename from spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/StsClientStore.java rename to spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/StsAccountStore.java index 27e609ef375..b00f99f0b97 100644 --- a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/StsClientStore.java +++ b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/StsAccountStore.java @@ -15,7 +15,7 @@ package org.eclipse.edc.iam.identitytrust.sts.spi.store; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.result.StoreResult; @@ -24,63 +24,63 @@ import java.util.stream.Stream; /** - * Persists and retrieves {@link StsClient}s. + * Persists and retrieves {@link StsAccount}s. */ @ExtensionPoint -public interface StsClientStore { +public interface StsAccountStore { String CLIENT_EXISTS_TEMPLATE = "Client with ID %s already exists"; String CLIENT_NOT_FOUND_BY_CLIENT_ID_TEMPLATE = "Client with clientID %s not found"; String CLIENT_NOT_FOUND_BY_ID_TEMPLATE = "Client with id %s not found"; /** - * Stores the {@link StsClient} + * Stores the {@link StsAccount} * - * @param stsClient The client + * @param stsAccount The client * @return successful when the client is stored, failure otherwise */ - StoreResult create(StsClient stsClient); + StoreResult create(StsAccount stsAccount); /** - * Update the {@link StsClient} if am sts client with the same ID exists. + * Update the {@link StsAccount} if am sts client with the same ID exists. * - * @param stsClient {@link StsClient} to update. + * @param stsAccount {@link StsAccount} to update. * @return {@link StoreResult#success()} if the sts client was updates, {@link StoreResult#notFound(String)} if the sts client identified by the ID was not found. */ - StoreResult update(StsClient stsClient); + StoreResult update(StsAccount stsAccount); /** * Returns all the sts clients in the store that are covered by a given {@link QuerySpec}. *

- * Note: supplying a sort field that does not exist on the {@link StsClient} may cause some implementations + * Note: supplying a sort field that does not exist on the {@link StsAccount} may cause some implementations * to return an empty Stream, others will return an unsorted Stream, depending on the backing storage * implementation. */ @NotNull - Stream findAll(QuerySpec spec); + Stream findAll(QuerySpec spec); /** - * Returns an {@link StsClient} by its id + * Returns an {@link StsAccount} by its id * * @param id id of the client * @return the client successful if found, failure otherwise */ - StoreResult findById(String id); + StoreResult findById(String id); /** - * Returns an {@link StsClient} by its clientId + * Returns an {@link StsAccount} by its clientId * * @param clientId clientId of the client * @return the client successful if found, failure otherwise */ - StoreResult findByClientId(String clientId); + StoreResult findByClientId(String clientId); /** * Deletes the sts client with the given id. * - * @param id A String that represents the {@link StsClient} ID, in most cases this will be a UUID. + * @param id A String that represents the {@link StsAccount} ID, in most cases this will be a UUID. * @return {@link StoreResult#success()}} if the sts client was deleted, {@link StoreResult#notFound(String)} if the sts client was not found in the store. */ - StoreResult deleteById(String id); + StoreResult deleteById(String id); } diff --git a/spi/common/identity-trust-sts-spi/src/testFixtures/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/fixtures/StsClientStoreTestBase.java b/spi/common/identity-trust-sts-spi/src/testFixtures/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/fixtures/StsAccountStoreTestBase.java similarity index 92% rename from spi/common/identity-trust-sts-spi/src/testFixtures/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/fixtures/StsClientStoreTestBase.java rename to spi/common/identity-trust-sts-spi/src/testFixtures/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/fixtures/StsAccountStoreTestBase.java index e2ec7aab992..136dc895baa 100644 --- a/spi/common/identity-trust-sts-spi/src/testFixtures/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/fixtures/StsClientStoreTestBase.java +++ b/spi/common/identity-trust-sts-spi/src/testFixtures/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/fixtures/StsAccountStoreTestBase.java @@ -14,8 +14,8 @@ package org.eclipse.edc.iam.identitytrust.sts.spi.store.fixtures; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; import org.eclipse.edc.spi.query.Criterion; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.query.SortOrder; @@ -48,17 +48,17 @@ import static org.eclipse.edc.spi.result.StoreFailure.Reason.NOT_FOUND; /** - * Base compliance tests for implementors of {@link StsClientStore}. + * Base compliance tests for implementors of {@link StsAccountStore}. */ -public abstract class StsClientStoreTestBase { +public abstract class StsAccountStoreTestBase { - protected abstract StsClientStore getStsClientStore(); + protected abstract StsAccountStore getStsClientStore(); protected String getRandomId() { return UUID.randomUUID().toString(); } - private List createClients(int size) { + private List createClients(int size) { return IntStream.range(0, size).mapToObj(i -> createClient("id" + i)) .toList(); } @@ -71,7 +71,7 @@ private void delay(long ms) { } } - private void saveClients(List clients) { + private void saveClients(List clients) { clients.forEach(getStsClientStore()::create); } @@ -158,7 +158,7 @@ void withSpec() { @ParameterizedTest @ArgumentsSource(FilterArgumentProvider.class) - void query_withQuerySpec(String field, Function mapping) { + void query_withQuerySpec(String field, Function mapping) { var clients = createClients(10); saveClients(clients); @@ -252,7 +252,7 @@ void verifySorting() { assertThat(getStsClientStore().findAll(QuerySpec.Builder.newInstance().sortField("createdAt").sortOrder(SortOrder.ASC).build())) .hasSize(10) - .isSortedAccordingTo(Comparator.comparing(StsClient::getCreatedAt)); + .isSortedAccordingTo(Comparator.comparing(StsAccount::getCreatedAt)); assertThat(getStsClientStore().findAll(QuerySpec.Builder.newInstance().sortField("createdAt").sortOrder(SortOrder.DESC).build())) .hasSize(10) @@ -275,13 +275,13 @@ static class FilterArgumentProvider implements ArgumentsProvider { public Stream provideArguments(ExtensionContext context) { return Stream.of( - Arguments.of("id", (Function) StsClient::getId), - Arguments.of("clientId", (Function) StsClient::getClientId), - Arguments.of("name", (Function) StsClient::getName), - Arguments.of("did", (Function) StsClient::getDid), - Arguments.of("secretAlias", (Function) StsClient::getSecretAlias), - Arguments.of("privateKeyAlias", (Function) StsClient::getPrivateKeyAlias), - Arguments.of("publicKeyReference", (Function) StsClient::getPublicKeyReference) + Arguments.of("id", (Function) StsAccount::getId), + Arguments.of("clientId", (Function) StsAccount::getClientId), + Arguments.of("name", (Function) StsAccount::getName), + Arguments.of("did", (Function) StsAccount::getDid), + Arguments.of("secretAlias", (Function) StsAccount::getSecretAlias), + Arguments.of("privateKeyAlias", (Function) StsAccount::getPrivateKeyAlias), + Arguments.of("publicKeyReference", (Function) StsAccount::getPublicKeyReference) ); } } diff --git a/spi/common/identity-trust-sts-spi/src/testFixtures/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/fixtures/TestFunctions.java b/spi/common/identity-trust-sts-spi/src/testFixtures/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/fixtures/TestFunctions.java index 1ead959e0ba..c5e54f51746 100644 --- a/spi/common/identity-trust-sts-spi/src/testFixtures/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/fixtures/TestFunctions.java +++ b/spi/common/identity-trust-sts-spi/src/testFixtures/java/org/eclipse/edc/iam/identitytrust/sts/spi/store/fixtures/TestFunctions.java @@ -14,22 +14,22 @@ package org.eclipse.edc.iam.identitytrust.sts.spi.store.fixtures; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; import java.util.UUID; public class TestFunctions { - public static StsClient createClient(String id, String secretAlias) { + public static StsAccount createClient(String id, String secretAlias) { return createClient(id, secretAlias, id, secretAlias, "did:example:subject"); } - public static StsClient createClient(String id, String secretAlias, String clientId) { + public static StsAccount createClient(String id, String secretAlias, String clientId) { return createClient(id, secretAlias, clientId, secretAlias, "did:example:subject"); } - public static StsClient createClient(String id, String secretAlias, String clientId, String publicKeyReference, String did) { + public static StsAccount createClient(String id, String secretAlias, String clientId, String publicKeyReference, String did) { return createClientBuilder(id) .clientId(clientId) .name(UUID.randomUUID().toString()) @@ -39,13 +39,13 @@ public static StsClient createClient(String id, String secretAlias, String clien .privateKeyAlias(UUID.randomUUID().toString()).build(); } - public static StsClient.Builder createClientBuilder(String id) { - return StsClient.Builder.newInstance() + public static StsAccount.Builder createClientBuilder(String id) { + return StsAccount.Builder.newInstance() .id(id) .name(UUID.randomUUID().toString()); } - public static StsClient createClient(String id) { + public static StsAccount createClient(String id) { return createClient(id, UUID.randomUUID().toString()); } diff --git a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/ApiContext.java b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/ApiContext.java index 7de2b2b5c5b..68a4b59cfd3 100644 --- a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/ApiContext.java +++ b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/ApiContext.java @@ -25,6 +25,7 @@ public interface ApiContext { String PUBLIC = "public"; String VERSION = "version"; String STS = "sts"; + String STS_ACCOUNTS = "accounts"; @Deprecated(since = "0.6.4") String SIGNALING = "signaling"; diff --git a/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/StsEndToEndTestBase.java b/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/StsEndToEndTestBase.java index ca48323a491..32e10a6c43f 100644 --- a/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/StsEndToEndTestBase.java +++ b/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/StsEndToEndTestBase.java @@ -15,8 +15,8 @@ package org.eclipse.edc.test.e2e.sts.api; import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; -import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsAccount; +import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsAccountStore; import org.eclipse.edc.junit.extensions.RuntimePerClassExtension; import org.eclipse.edc.spi.security.Vault; @@ -34,7 +34,7 @@ public abstract class StsEndToEndTestBase { protected abstract RuntimePerClassExtension getRuntime(); - protected StsClient initClient(String clientId, String clientSecret) { + protected StsAccount initClient(String clientId, String clientSecret) { var store = getClientStore(); var vault = getVault(); var clientSecretAlias = "client_secret_alias"; @@ -47,7 +47,7 @@ protected StsClient initClient(String clientId, String clientSecret) { return client; } - protected StsClient initClient(String clientSecret) { + protected StsAccount initClient(String clientSecret) { return initClient(UUID.randomUUID().toString(), clientSecret); } @@ -59,8 +59,8 @@ protected Map parseClaims(String token) { } } - private StsClientStore getClientStore() { - return getRuntime().getService(StsClientStore.class); + private StsAccountStore getClientStore() { + return getRuntime().getService(StsAccountStore.class); } private Vault getVault() { diff --git a/system-tests/version-api/version-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/versionapi/Runtimes.java b/system-tests/version-api/version-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/versionapi/Runtimes.java index 11578d41cd3..0366216ea14 100644 --- a/system-tests/version-api/version-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/versionapi/Runtimes.java +++ b/system-tests/version-api/version-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/versionapi/Runtimes.java @@ -16,7 +16,7 @@ import org.eclipse.edc.connector.dataplane.selector.spi.client.DataPlaneClientFactory; import org.eclipse.edc.connector.dataplane.spi.manager.DataPlaneManager; -import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientService; +import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsAccountService; import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientTokenGeneratorService; import org.eclipse.edc.junit.extensions.EmbeddedRuntime; import org.eclipse.edc.junit.extensions.RuntimeExtension; @@ -34,7 +34,7 @@ static RuntimeExtension inMemoryRuntime() { var rt = new RuntimePerClassExtension(new EmbeddedRuntime("control-plane", inMemoryConfiguration(), ":system-tests:version-api:version-api-test-runtime")); rt.registerServiceMock(DataPlaneManager.class, mock()); rt.registerServiceMock(DataPlaneClientFactory.class, mock()); - rt.registerServiceMock(StsClientService.class, mock()); + rt.registerServiceMock(StsAccountService.class, mock()); rt.registerServiceMock(StsClientTokenGeneratorService.class, mock()); return rt; }