-
Notifications
You must be signed in to change notification settings - Fork 245
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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 <[email protected]> * fix javadoc * DEPENDENCIES * --amend --------- Co-authored-by: Enrico Risa <[email protected]>
- Loading branch information
1 parent
dfa1be6
commit f3b322a
Showing
54 changed files
with
1,695 additions
and
419 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
...on/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/build.gradle.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
...-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApi.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<StsAccount> 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); | ||
} |
90 changes: 90 additions & 0 deletions
90
.../eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiConfigurationExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
...main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
Oops, something went wrong.