Skip to content

Commit

Permalink
feat: Standalone STS
Browse files Browse the repository at this point in the history
  • Loading branch information
wolf4ood committed Oct 20, 2023
1 parent 2ea0ecc commit 7e05e61
Show file tree
Hide file tree
Showing 22 changed files with 912 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ plugins {

dependencies {
api(project(":spi:common:web-spi"))
api(project(":spi:common:identity-trust-sts-spi"))


implementation(libs.jakarta.rsApi)
implementation(libs.swagger.annotations.jakarta)
Expand All @@ -29,5 +31,6 @@ dependencies {

testImplementation(project(":core:common:junit"))
testImplementation(testFixtures(project(":extensions:common:http:jersey-core")))
testImplementation(libs.restAssured)
}

Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ public interface SecureTokenServiceApi {
content = @Content(array = @ArraySchema(schema = @Schema(implementation = StsTokenErrorResponse.class))))
})
StsTokenResponse token(@BeanParam StsTokenRequest request);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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.connector.api.sts;

import org.eclipse.edc.connector.api.sts.configuration.StsApiConfiguration;
import org.eclipse.edc.connector.api.sts.controller.SecureTokenServiceApiController;
import org.eclipse.edc.connector.api.sts.validation.StsTokenRequestValidator;
import org.eclipse.edc.iam.identitytrust.sts.service.StsClientService;
import org.eclipse.edc.iam.identitytrust.sts.service.StsClientTokenGeneratorService;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.web.spi.WebService;

@Extension(SecureTokenServiceApiExtension.NAME)
public class SecureTokenServiceApiExtension implements ServiceExtension {

public static final String NAME = "Secure Token Service API";


@Inject
private StsApiConfiguration stsApiConfiguration;

@Inject
private StsClientService clientService;

@Inject
private StsClientTokenGeneratorService tokenService;

@Inject
private Monitor monitor;

@Inject
private WebService webService;

@Override
public String name() {
return NAME;
}

@Override
public void initialize(ServiceExtensionContext context) {
webService.registerResource(stsApiConfiguration.getContextAlias(), new SecureTokenServiceApiController(clientService, tokenService, new StsTokenRequestValidator()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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.connector.api.sts;

import org.eclipse.edc.connector.api.sts.configuration.StsApiConfiguration;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Provides;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.web.spi.WebServer;
import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
import org.eclipse.edc.web.spi.configuration.WebServiceSettings;

@Extension(value = StsApiConfigurationExtension.NAME)
@Provides({ StsApiConfiguration.class })
public class StsApiConfigurationExtension implements ServiceExtension {

public static final String NAME = "Secure Token Service API configuration";
public static final String STS_CONTEXT_ALIAS = "sts";
private static final String WEB_SERVICE_NAME = "STS API";
private static final int DEFAULT_STS_API_PORT = 9292;
private static final String DEFAULT_STS_API_CONTEXT_PATH = "/api/v1/sts";
public static final WebServiceSettings SETTINGS = WebServiceSettings.Builder.newInstance()
.apiConfigKey("web.http." + STS_CONTEXT_ALIAS)
.contextAlias(STS_CONTEXT_ALIAS)
.defaultPath(DEFAULT_STS_API_CONTEXT_PATH)
.defaultPort(DEFAULT_STS_API_PORT)
.useDefaultContext(true)
.name(WEB_SERVICE_NAME)
.build();
@Inject
private WebServer webServer;
@Inject
private WebServiceConfigurer configurator;

@Override
public String name() {
return NAME;
}

@Override
public void initialize(ServiceExtensionContext context) {
var config = configurator.configure(context, webServer, SETTINGS);
context.registerService(StsApiConfiguration.class, new StsApiConfiguration(config));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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.connector.api.sts.configuration;

import org.eclipse.edc.web.spi.configuration.WebServiceConfiguration;

public class StsApiConfiguration extends WebServiceConfiguration {

public StsApiConfiguration(WebServiceConfiguration webServiceConfiguration) {
this.contextAlias = webServiceConfiguration.getContextAlias();
this.path = webServiceConfiguration.getPath();
this.port = webServiceConfiguration.getPort();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,55 @@
import org.eclipse.edc.connector.api.sts.SecureTokenServiceApi;
import org.eclipse.edc.connector.api.sts.model.StsTokenRequest;
import org.eclipse.edc.connector.api.sts.model.StsTokenResponse;
import org.eclipse.edc.iam.identitytrust.sts.model.StsClient;
import org.eclipse.edc.iam.identitytrust.sts.model.StsClientTokenAdditionalParams;
import org.eclipse.edc.iam.identitytrust.sts.service.StsClientService;
import org.eclipse.edc.iam.identitytrust.sts.service.StsClientTokenGeneratorService;
import org.eclipse.edc.spi.iam.TokenRepresentation;
import org.eclipse.edc.validator.spi.Validator;
import org.eclipse.edc.web.spi.exception.ValidationFailureException;

import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.exceptionMapper;

@Path("/")
public class SecureTokenServiceApiController implements SecureTokenServiceApi {

private final StsClientService clientService;

private final StsClientTokenGeneratorService tokenService;

private final Validator<StsTokenRequest> tokenRequestValidator;

public SecureTokenServiceApiController(StsClientService clientService, StsClientTokenGeneratorService tokenService, Validator<StsTokenRequest> tokenRequestValidator) {
this.clientService = clientService;
this.tokenService = tokenService;
this.tokenRequestValidator = tokenRequestValidator;
}

@Consumes({ MediaType.APPLICATION_FORM_URLENCODED })
@Produces({ MediaType.APPLICATION_JSON })
@Path("token")
@POST
@Override
public StsTokenResponse token(@BeanParam StsTokenRequest request) {
return null;
tokenRequestValidator.validate(request).orElseThrow(ValidationFailureException::new);
return clientService.findById(request.getClientId())
.compose(client -> clientService.authenticate(client, request.getClientSecret()))
.compose(client -> tokenService.tokenFor(client, additionalParams(request)))
.map(this::mapToken)
.orElseThrow(exceptionMapper(StsClient.class, request.getClientId()));

}

private StsClientTokenAdditionalParams additionalParams(StsTokenRequest request) {
return StsClientTokenAdditionalParams.Builder.newInstance()
.audience(request.getAudience())
.accessToken(request.getAccessToken())
.bearerAccessScope(request.getBearerAccessScope())
.build();
}

private StsTokenResponse mapToken(TokenRepresentation tokenRepresentation) {
return new StsTokenResponse(tokenRepresentation.getToken(), tokenRepresentation.getExpiresIn());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,108 @@
/**
* OAuth2 Client Credentials <a href="https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2">Access Token Request</a>
*
* @param grantType Type of grant. Must be client_credentials.
* @param clientId Client ID identifier.
* @param clientSecret Authorization secret for the client.
* @param bearerAccessScope Space-delimited scopes to be included in the access_token claim.
* @param accessToken VP/VC Access Token to be included as access_token claim.
* <ul>
* <li>grantType: Type of grant. Must be client_credentials.</li>
* <li>clientId: Client ID identifier.</li>
* <li>clientSecret: Authorization secret for the client/</li>
* <li>audience: Audience according to the <a href="https://datatracker.ietf.org/doc/html/draft-tschofenig-oauth-audience-00#section-3">spec</a>.</li>
* <li>bearerAccessScope: Space-delimited scopes to be included in the access_token claim.</li>
* <li>accessToken: VP/VC Access Token to be included as access_token claim.</li>
* <li>grantType: Type of grant. Must be client_credentials.</li>
* </ul>
*/
public record StsTokenRequest(@FormParam("grant_type") String grantType,
@FormParam("client_id") String clientId,
@FormParam("client_secret") String clientSecret,
@FormParam("bearer_access_scope") String bearerAccessScope,
@FormParam("access_token") String accessToken) {

public final class StsTokenRequest {
@FormParam("grant_type")
private String grantType;
@FormParam("client_id")
private String clientId;
@FormParam("audience")
private String audience;
@FormParam("bearer_access_scope")
private String bearerAccessScope;
@FormParam("access_token")
private String accessToken;

@FormParam("client_secret")
private String clientSecret;


public StsTokenRequest() {

}

public String getGrantType() {
return grantType;
}

public String getClientId() {
return clientId;
}

public String getClientSecret() {
return clientSecret;
}


public String getAudience() {
return audience;
}

public String getBearerAccessScope() {
return bearerAccessScope;
}

public String getAccessToken() {
return accessToken;
}


public static class Builder {

private final StsTokenRequest request;

protected Builder(StsTokenRequest request) {
this.request = request;
}

public static Builder newInstance() {
return new Builder(new StsTokenRequest());
}

public Builder grantType(String grantType) {
this.request.grantType = grantType;
return this;
}

public Builder clientId(String clientId) {
this.request.clientId = clientId;
return this;
}

public Builder audience(String audience) {
this.request.audience = audience;
return this;
}

public Builder bearerAccessScope(String bearerAccessScope) {
this.request.bearerAccessScope = bearerAccessScope;
return this;
}

public Builder accessToken(String accessToken) {
this.request.accessToken = accessToken;
return this;
}

public Builder clientSecret(String clientSecret) {
this.request.clientSecret = clientSecret;
return this;
}

public StsTokenRequest build() {
return request;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
* @param tokenType Token type.
*/
public record StsTokenResponse(@JsonProperty("access_token") String accessToken,
@JsonProperty("expires_in") long expiresIn,
@JsonProperty("expires_in") Long expiresIn,
@JsonProperty("token_type") String tokenType) {

public StsTokenResponse(String accessToken, long expiresIn) {
public StsTokenResponse(String accessToken, Long expiresIn) {
this(accessToken, expiresIn, "Bearer");
}

Expand Down
Loading

0 comments on commit 7e05e61

Please sign in to comment.