From 17c3f23e43a42d6cd1c3a8b84463204e8dbd9cc6 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 13 Mar 2024 08:26:08 +0100 Subject: [PATCH 1/3] feat: implement TokenRefreshhandler --- edc-extensions/README.md | 47 ---- .../api/TokenRefreshApiExtension.java | 2 +- .../tokenrefresh/api/v1/TokenRefreshApi.java | 2 +- .../api/v1/TokenRefreshApiController.java | 4 +- .../api/v1/TokenRefreshApiControllerTest.java | 4 +- ...DataPlaneTokenRefreshServiceExtension.java | 38 ++- .../DataPlaneTokenRefreshServiceImpl.java | 4 +- .../build.gradle.kts | 24 +- .../TokenRefreshHandlerExtension.java | 63 +++++ .../tokenrefresh/TokenRefreshHandlerImpl.java | 160 ++++++++++++ ...rg.eclipse.edc.spi.system.ServiceExtension | 20 ++ .../TokenRefreshHandlerImplTest.java | 228 ++++++++++++++++++ .../DataPlaneTokenRefreshEndToEndTest.java | 2 +- gradle/libs.versions.toml | 1 + settings.gradle.kts | 1 + .../common/TokenRefreshHandler.java | 38 +++ .../DataPlaneTokenRefreshService.java | 4 +- .../dataplane}/model/TokenResponse.java | 2 +- 18 files changed, 571 insertions(+), 73 deletions(-) delete mode 100644 edc-extensions/README.md rename edc-extensions/{ => tokenrefresh-handler}/build.gradle.kts (57%) create mode 100644 edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerExtension.java create mode 100644 edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImpl.java create mode 100644 edc-extensions/tokenrefresh-handler/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension create mode 100644 edc-extensions/tokenrefresh-handler/src/test/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImplTest.java create mode 100644 spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/common/TokenRefreshHandler.java rename spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/{dataplane/tokenrefresh/spi => spi/tokenrefresh/dataplane}/DataPlaneTokenRefreshService.java (93%) rename spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/{dataplane/tokenrefresh/spi => spi/tokenrefresh/dataplane}/model/TokenResponse.java (95%) diff --git a/edc-extensions/README.md b/edc-extensions/README.md deleted file mode 100644 index e2c2855f9..000000000 --- a/edc-extensions/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# EDC Extensions Overview - -The following extensions provide additional functionality to the core EDC. -They are currently only available in Tractus-X EDC. - -## Business Partner Validation - -This extension allows for validation of business partners within the access policy. - -## Control Plane EDR APIs - -The goal of this extension is to simplify the process of retrieving data out of EDC. -It returns `EndpointDataReference` object, hiding all the communication details for contract offers, -contract negotiation, transfer process and retrieving the underlying data through the data-planes. - -## Data Encryption - -The EDC encrypts sensitive information inside a token it sends to other applications (potentially cross-company). -This extension implements the encryption of this data and should be used with secure keys and algorithms at all times. - -## Data Plane Selector - -This control plane extension makes it possible to configure one or more data plane instances. -During a transfer the control plane will look for an instance with matching capabilities to transfer data. - -## Hashicorp Vault - -This extension allows for usage of Hashicorp Vault for secret storage. -It is the default used in Tractus-X EDC. - -## PostrgreSQL Migration - -While the core EDC is able to interact with PostgreSQL databases, -it does not automate migrations between schema versions. -This extension adds that functionality. - -## Transfer Process SFTP - -This extension allows for the use of SFTP backends for the data plane (but is not included in the provided control- and data plane). - -## NOTICE - -This work is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). - -- SPDX-License-Identifier: Apache-2.0 -- SPDX-FileCopyrightText: 2021,2022,2023 Contributors to the Eclipse Foundation -- Source URL: diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/TokenRefreshApiExtension.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/TokenRefreshApiExtension.java index 3fb4a88b6..907e50169 100644 --- a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/TokenRefreshApiExtension.java +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/TokenRefreshApiExtension.java @@ -25,7 +25,7 @@ import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.web.spi.WebService; import org.eclipse.tractusx.edc.dataplane.tokenrefresh.api.v1.TokenRefreshApiController; -import org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.DataPlaneTokenRefreshService; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.DataPlaneTokenRefreshService; import static org.eclipse.tractusx.edc.dataplane.tokenrefresh.api.TokenRefreshApiExtension.NAME; diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApi.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApi.java index f0a85a0ed..abaf84e3a 100644 --- a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApi.java +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApi.java @@ -31,7 +31,7 @@ import io.swagger.v3.oas.annotations.security.SecurityScheme; import io.swagger.v3.oas.annotations.tags.Tag; import org.eclipse.edc.web.spi.ApiErrorDetail; -import org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.model.TokenResponse; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; @SecurityScheme(name = "Authentication", description = "Self-Issued ID token containing an access_token", diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiController.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiController.java index c2e08290f..4757fcae5 100644 --- a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiController.java +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiController.java @@ -28,8 +28,8 @@ import jakarta.ws.rs.core.MediaType; import org.eclipse.edc.web.spi.exception.AuthenticationFailedException; import org.eclipse.edc.web.spi.exception.InvalidRequestException; -import org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.DataPlaneTokenRefreshService; -import org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.model.TokenResponse; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.DataPlaneTokenRefreshService; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION; diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiControllerTest.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiControllerTest.java index bb2b805c2..d8fc82d13 100644 --- a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiControllerTest.java +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-api/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/api/v1/TokenRefreshApiControllerTest.java @@ -23,8 +23,8 @@ import io.restassured.specification.RequestSpecification; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; -import org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.DataPlaneTokenRefreshService; -import org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.model.TokenResponse; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.DataPlaneTokenRefreshService; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; import org.hamcrest.Matchers; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceExtension.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceExtension.java index 553ea6665..cbffbbbf7 100644 --- a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceExtension.java +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceExtension.java @@ -26,14 +26,16 @@ import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provider; import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.security.PrivateKeyResolver; import org.eclipse.edc.spi.security.Vault; +import org.eclipse.edc.spi.system.Hostname; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.token.JwtGenerationService; import org.eclipse.edc.token.spi.TokenValidationService; -import org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.DataPlaneTokenRefreshService; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.DataPlaneTokenRefreshService; import org.jetbrains.annotations.NotNull; import java.security.PrivateKey; @@ -48,7 +50,11 @@ public class DataPlaneTokenRefreshServiceExtension implements ServiceExtension { public static final String NAME = "DataPlane Token Refresh Service extension"; public static final int DEFAULT_TOKEN_EXPIRY_TOLERANCE_SECONDS = 5; @Setting(value = "Token expiry tolerance period in seconds to allow for clock skew", defaultValue = "" + DEFAULT_TOKEN_EXPIRY_TOLERANCE_SECONDS) - public static final String TOKEN_EXPIRY_TOLERANCE_SECONDS_PROPERTY = "edc.dataplane.api.token.expiry.tolerance"; + public static final String TOKEN_EXPIRY_TOLERANCE_SECONDS_PROPERTY = "edc.dataplane.token.expiry.tolerance"; + + @Setting(value = "The HTTP endpoint where clients can request a renewal of their access token for the public dataplane API") + public static final String REFRESH_ENDPOINT_PROPERTY = "edc.dataplane.token.refresh.endpoint"; + @Inject private TokenValidationService tokenValidationService; @Inject @@ -63,6 +69,8 @@ public class DataPlaneTokenRefreshServiceExtension implements ServiceExtension { private Vault vault; @Inject private TypeManager typeManager; + @Inject + private Hostname hostname; private DataPlaneTokenRefreshServiceImpl tokenRefreshService; @@ -83,16 +91,36 @@ public DataPlaneTokenRefreshService createRefreshTokenService(ServiceExtensionCo return getTokenRefreshService(context); } + private Integer getExpiryToleranceConfig(ServiceExtensionContext context) { + return context.getConfig().getInteger(TOKEN_EXPIRY_TOLERANCE_SECONDS_PROPERTY, DEFAULT_TOKEN_EXPIRY_TOLERANCE_SECONDS); + } + @NotNull private DataPlaneTokenRefreshServiceImpl getTokenRefreshService(ServiceExtensionContext context) { if (tokenRefreshService == null) { - var epsilon = context.getConfig().getInteger(TOKEN_EXPIRY_TOLERANCE_SECONDS_PROPERTY, DEFAULT_TOKEN_EXPIRY_TOLERANCE_SECONDS); - tokenRefreshService = new DataPlaneTokenRefreshServiceImpl(clock, tokenValidationService, didPkResolver, accessTokenDataStore, new JwtGenerationService(), getPrivateKeySupplier(context), context.getMonitor(), null, - epsilon, vault, typeManager.getMapper()); + var monitor = context.getMonitor().withPrefix("DataPlane Token Refresh"); + var expiryTolerance = getExpiryToleranceConfig(context); + var refreshEndpoint = getRefreshEndpointConfig(context, monitor); + monitor.debug("Token refresh endpoint: %s".formatted(refreshEndpoint)); + monitor.debug("Token refresh time tolerance: %ds".formatted(expiryTolerance)); + tokenRefreshService = new DataPlaneTokenRefreshServiceImpl(clock, tokenValidationService, didPkResolver, accessTokenDataStore, new JwtGenerationService(), + getPrivateKeySupplier(context), context.getMonitor(), refreshEndpoint, expiryTolerance, + vault, typeManager.getMapper()); } return tokenRefreshService; } + private String getRefreshEndpointConfig(ServiceExtensionContext context, Monitor monitor) { + var refreshEndpoint = context.getConfig().getString(REFRESH_ENDPOINT_PROPERTY, null); + if (refreshEndpoint == null) { + var port = context.getConfig().getInteger("web.http.public.port", 8185); + var path = context.getConfig().getString("web.http.public.path", "/api/v2/public"); + refreshEndpoint = "http://%s:%d%s".formatted(hostname.get(), port, path); + monitor.warning("Config property '%s' was not specified, the default '%s' will be used.".formatted(REFRESH_ENDPOINT_PROPERTY, refreshEndpoint)); + } + return refreshEndpoint; + } + @NotNull private Supplier getPrivateKeySupplier(ServiceExtensionContext context) { return () -> { diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImpl.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImpl.java index 11769cd62..4c3cd8cb5 100644 --- a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImpl.java +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceImpl.java @@ -42,8 +42,8 @@ import org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.rules.ClaimIsPresentRule; import org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.rules.IssuerEqualsSubjectRule; import org.eclipse.tractusx.edc.dataplane.tokenrefresh.core.rules.RefreshTokenValidationRule; -import org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.DataPlaneTokenRefreshService; -import org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.model.TokenResponse; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.DataPlaneTokenRefreshService; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; import java.security.PrivateKey; import java.time.Clock; diff --git a/edc-extensions/build.gradle.kts b/edc-extensions/tokenrefresh-handler/build.gradle.kts similarity index 57% rename from edc-extensions/build.gradle.kts rename to edc-extensions/tokenrefresh-handler/build.gradle.kts index a62e89ac7..c2612c77d 100644 --- a/edc-extensions/build.gradle.kts +++ b/edc-extensions/tokenrefresh-handler/build.gradle.kts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -18,16 +18,22 @@ ********************************************************************************/ plugins { + `maven-publish` `java-library` } dependencies { - implementation(project(":edc-extensions:bpn-validation")) - implementation(project(":edc-extensions:data-encryption")) - implementation(project(":edc-extensions:dataplane:dataplane-selector-configuration")) - implementation(project(":edc-extensions:postgresql-migration")) - implementation(project(":edc-extensions:provision-additional-headers")) - implementation(project(":edc-extensions:transferprocess-sftp-client")) - implementation(project(":edc-extensions:transferprocess-sftp-common")) - implementation(project(":edc-extensions:transferprocess-sftp-provisioner")) + implementation(project(":spi:tokenrefresh-spi")) + implementation(libs.edc.spi.core) + implementation(libs.edc.spi.edrstore) + implementation(libs.edc.spi.http) + implementation(libs.edc.spi.token) + implementation(libs.edc.spi.jwt) + implementation(libs.edc.spi.identitytrust) + implementation(libs.edc.util) + implementation(libs.nimbus.jwt) + + + testImplementation(libs.edc.junit) + testImplementation(libs.restAssured) } diff --git a/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerExtension.java b/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerExtension.java new file mode 100644 index 000000000..52d57b88b --- /dev/null +++ b/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerExtension.java @@ -0,0 +1,63 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +package org.eclipse.tractusx.edc.common.tokenrefresh; + +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.identitytrust.SecureTokenService; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.spi.http.EdcHttpClient; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.tractusx.edc.spi.tokenrefresh.common.TokenRefreshHandler; + +import static org.eclipse.tractusx.edc.common.tokenrefresh.TokenRefreshHandlerExtension.NAME; + + +@Extension(value = NAME) +public class TokenRefreshHandlerExtension implements ServiceExtension { + public static final String NAME = "Token Refresh Handler Extension"; + // this setting is defined by the IdentityAndTrustExtension + private static final String PARTICIPANT_DID_PROPERTY = "edc.iam.issuer.id"; + @Inject + private EndpointDataReferenceStore edrStore; + @Inject + private EdcHttpClient httpClient; + @Inject + private SecureTokenService secureTokenService; + @Inject + private TypeManager typeManager; + + @Override + public String name() { + return NAME; + } + + @Provider + public TokenRefreshHandler createTokenRefreshHander(ServiceExtensionContext context) { + return new TokenRefreshHandlerImpl(edrStore, httpClient, getOwnDid(context), context.getMonitor(), secureTokenService, typeManager.getMapper()); + } + + private String getOwnDid(ServiceExtensionContext context) { + return context.getConfig().getString(PARTICIPANT_DID_PROPERTY); + } +} diff --git a/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImpl.java b/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImpl.java new file mode 100644 index 000000000..b64904c30 --- /dev/null +++ b/edc-extensions/tokenrefresh-handler/src/main/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImpl.java @@ -0,0 +1,160 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +package org.eclipse.tractusx.edc.common.tokenrefresh; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jwt.SignedJWT; +import okhttp3.HttpUrl; +import okhttp3.Request; +import okhttp3.RequestBody; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.identitytrust.SecureTokenService; +import org.eclipse.edc.spi.http.EdcHttpClient; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.util.string.StringUtils; +import org.eclipse.tractusx.edc.spi.tokenrefresh.common.TokenRefreshHandler; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; + +import java.io.IOException; +import java.text.ParseException; +import java.util.Map; + +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.JWT_ID; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SUBJECT; +import static org.eclipse.edc.util.string.StringUtils.isNullOrBlank; + +public class TokenRefreshHandlerImpl implements TokenRefreshHandler { + public static final String PROPERTY_AUTHORIZATION = "authorization"; + public static final String PROPERTY_REFRESH_TOKEN = "refreshToken"; + public static final String PROPERTY_REFRESH_ENDPOINT = "refreshEndpoint"; + private final EndpointDataReferenceStore edrStore; + private final EdcHttpClient httpClient; + private final String ownDid; + private final Monitor monitor; + private final SecureTokenService secureTokenService; + private final ObjectMapper objectMapper; + + /** + * Creates a new TokenRefreshHandler + * + * @param edrStore a persistent storage where {@link org.eclipse.edc.spi.types.domain.edr.EndpointDataReference} objects are stored. + * @param httpClient needed to make the actual refresh call against the refresh endpoint + * @param ownDid the DID of this connector + * @param secureTokenService Service to generate the authentication token + * @param objectMapper ObjectMapper to interpret JSON responses + */ + public TokenRefreshHandlerImpl(EndpointDataReferenceStore edrStore, + EdcHttpClient httpClient, + String ownDid, + Monitor monitor, + SecureTokenService secureTokenService, + ObjectMapper objectMapper) { + this.edrStore = edrStore; + this.httpClient = httpClient; + this.ownDid = ownDid; + this.monitor = monitor; + this.secureTokenService = secureTokenService; + this.objectMapper = objectMapper; + } + + @Override + public Result refreshToken(String tokenId) { + var edrResult = edrStore.resolveByTransferProcess(tokenId); + if (edrResult.failed()) { + return Result.failure(edrResult.getFailureDetail()); + } + var edr = edrResult.getContent(); + var accessToken = edr.getStringProperty(PROPERTY_AUTHORIZATION); + var refreshToken = edr.getProperties().get(PROPERTY_REFRESH_TOKEN); + var refreshEndpoint = edr.getProperties().get(PROPERTY_REFRESH_ENDPOINT); + + if (isNullOrBlank(accessToken)) { + return Result.failure("Cannot perform token refresh: required property 'authorization' not found on EDR."); + } + if (isNullOrBlank(StringUtils.toString(refreshToken))) { + return Result.failure("Cannot perform token refresh: required property 'refreshToken' not found on EDR."); + } + if (isNullOrBlank(StringUtils.toString(refreshEndpoint))) { + return Result.failure("Cannot perform token refresh: required property 'refreshEndpoint' not found on EDR."); + } + + return getStringClaim(accessToken, ISSUER) + .map(audience -> Map.of( + JWT_ID, tokenId, + ISSUER, ownDid, + SUBJECT, ownDid, + AUDIENCE, audience + )) + .compose(claims -> secureTokenService.createToken(claims, accessToken)) + .compose(authToken -> createTokenRefreshRequest(refreshEndpoint.toString(), refreshToken.toString(), "Bearer %s".formatted(authToken.getToken()))) + .compose(this::executeRequest); + } + + private Result executeRequest(Request tokenRefreshRequest) { + try (var response = httpClient.execute(tokenRefreshRequest)) { + if (response.isSuccessful()) { + if (response.body() != null) { + + var jsonBody = response.body().string(); + if (!StringUtils.isNullOrEmpty(jsonBody)) { + var tokenResponse = objectMapper.readValue(jsonBody, TokenResponse.class); + return Result.success(tokenResponse); + } + } + return Result.failure("Token refresh successful, but body was empty."); + } + return Result.failure("Token refresh not successful: %d, message: %s".formatted(response.code(), response.message())); + } catch (IOException e) { + monitor.warning("Error executing token refresh request", e); + return Result.failure("Error executing token refresh request: %s".formatted(e)); + } + } + + private Result createTokenRefreshRequest(String refreshEndpoint, String refreshToken, String bearerToken) { + // see https://github.com/eclipse-tractusx/tractusx-profiles/blob/main/tx/refresh/refresh.token.grant.profile.md#3-the-refresh-request + if (!refreshEndpoint.endsWith("/token")) { + refreshToken += "/token"; + } + var url = HttpUrl.parse(refreshEndpoint) + .newBuilder() + .addQueryParameter("grant_type", "refresh_token") + .addQueryParameter("refresh_token", refreshToken) + .build(); + + return Result.success(new Request.Builder() + .addHeader("Authorization", bearerToken) + .url(url) + .post(RequestBody.create(new byte[0])) + .build()); + } + + private Result getStringClaim(String accessToken, String claimName) { + try { + return Result.success(SignedJWT.parse(accessToken).getJWTClaimsSet().getStringClaim(claimName)); + } catch (ParseException e) { + monitor.warning("Failed to get string claim '%s'".formatted(claimName), e); + return Result.failure("Failed to parse string claim '%s': %s".formatted(claimName, e)); + } + } + +} diff --git a/edc-extensions/tokenrefresh-handler/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/tokenrefresh-handler/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..4c237a271 --- /dev/null +++ b/edc-extensions/tokenrefresh-handler/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,20 @@ +################################################################################# +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +################################################################################# + +org.eclipse.tractusx.edc.common.tokenrefresh.TokenRefreshHandlerExtension \ No newline at end of file diff --git a/edc-extensions/tokenrefresh-handler/src/test/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImplTest.java b/edc-extensions/tokenrefresh-handler/src/test/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImplTest.java new file mode 100644 index 000000000..362e81fbc --- /dev/null +++ b/edc-extensions/tokenrefresh-handler/src/test/java/org/eclipse/tractusx/edc/common/tokenrefresh/TokenRefreshHandlerImplTest.java @@ -0,0 +1,228 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +package org.eclipse.tractusx.edc.common.tokenrefresh; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import okhttp3.MediaType; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.identitytrust.SecureTokenService; +import org.eclipse.edc.spi.http.EdcHttpClient; +import org.eclipse.edc.spi.iam.TokenRepresentation; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +import java.io.IOException; +import java.security.PrivateKey; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +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; + +class TokenRefreshHandlerImplTest { + public static final String REFRESH_ENDPOINT = "http://fizz.buzz/quazz"; + private static final String CONSUMER_DID = "did:web:bob"; + private static final String PUBLIC_KEY_ID = CONSUMER_DID + "#key-1"; + private static final String PROVIDER_DID = "did:web:alice"; + private final EndpointDataReferenceStore edrStore = mock(); + private final EdcHttpClient mockedHttpClient = mock(); + private final SecureTokenService mockedTokenService = mock(); + private TokenRefreshHandlerImpl tokenRefreshHandler; + private PrivateKey consumerKey; + private ObjectMapper objectMapper; + + private static String createJwt() { + try { + var providerKey = new ECKeyGenerator(Curve.P_256).generate(); + var signedJwt = new SignedJWT(new JWSHeader(JWSAlgorithm.ES256), new JWTClaimsSet.Builder().issuer(PROVIDER_DID).build()); + signedJwt.sign(new ECDSASigner(providerKey)); + return signedJwt.serialize(); + } catch (JOSEException e) { + throw new RuntimeException(e); + } + } + + @BeforeEach + void setup() throws JOSEException { + consumerKey = new ECKeyGenerator(Curve.P_256).generate().toPrivateKey(); + objectMapper = new ObjectMapper(); + tokenRefreshHandler = new TokenRefreshHandlerImpl(edrStore, mockedHttpClient, CONSUMER_DID, mock(), + mockedTokenService, objectMapper); + } + + @Test + void refresh_validateCorrectRequest() throws IOException { + when(edrStore.resolveByTransferProcess(anyString())).thenReturn(StoreResult.success(createEdr().build())); + when(mockedTokenService.createToken(anyMap(), anyString())).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-auth-token").build())); + var tokenResponse = new TokenResponse("new-access-token", "new-refresh-token", 60 * 5L, "bearer"); + var successResponse = createResponse(tokenResponse, 200, ""); + when(mockedHttpClient.execute(any())).thenReturn(successResponse); + var res = tokenRefreshHandler.refreshToken("token-id"); + assertThat(res).isSucceeded() + .satisfies(tr -> { + assertThat(tr.accessToken()).isEqualTo("new-access-token"); + assertThat(tr.refreshToken()).isEqualTo("new-refresh-token"); + assertThat(tr.expiresInSeconds()).isEqualTo(300L); + assertThat(tr.tokenType()).isEqualTo("bearer"); + }); + } + + @Test + void refresh_edrNotFound() { + when(edrStore.resolveByTransferProcess(anyString())).thenReturn(StoreResult.notFound("foo")); + + assertThat(tokenRefreshHandler.refreshToken("token-id")).isFailed() + .detail().isEqualTo("foo"); + + verify(edrStore).resolveByTransferProcess(eq("token-id")); + verifyNoMoreInteractions(edrStore); + verifyNoInteractions(mockedHttpClient, mockedTokenService); + } + + @ParameterizedTest(name = "{3}") + @ArgumentsSource(InvalidEdrProvider.class) + void refresh_edrLacksRequiredProperties(String authorization, String refreshToken, String refreshEndpoint, String desc) { + var invalidEdr = DataAddress.Builder.newInstance().type("test-type") + .property("authorization", authorization) + .property("refreshToken", refreshToken) + .property("refreshEndpoint", refreshEndpoint) + .build(); + when(edrStore.resolveByTransferProcess(anyString())).thenReturn(StoreResult.success(invalidEdr)); + + assertThat(tokenRefreshHandler.refreshToken("token-id")).isFailed() + .detail() + .matches("^Cannot perform token refresh: required property '(authorization|refreshToken|refreshEndpoint)' not found on EDR.$"); + } + + @Test + void refresh_endpointReturnsFailure() throws IOException { + when(edrStore.resolveByTransferProcess(anyString())).thenReturn(StoreResult.success(createEdr().build())); + when(mockedTokenService.createToken(anyMap(), anyString())).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-auth-token").build())); + var response401 = createResponse(null, 401, "Not authorized"); + + when(mockedHttpClient.execute(any())).thenReturn(response401); + + var res = tokenRefreshHandler.refreshToken("token-id"); + assertThat(res).isFailed() + .detail().isEqualTo("Token refresh not successful: 401, message: Not authorized"); + } + + @Test + void refresh_endpointReturnsEmptyBody() throws IOException { + when(edrStore.resolveByTransferProcess(anyString())).thenReturn(StoreResult.success(createEdr().build())); + when(mockedTokenService.createToken(anyMap(), anyString())).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-auth-token").build())); + var successResponse = createResponse(null, 200, ""); + when(mockedHttpClient.execute(any())).thenReturn(successResponse); + var res = tokenRefreshHandler.refreshToken("token-id"); + assertThat(res).isFailed() + .detail().isEqualTo("Token refresh successful, but body was empty."); + } + + @Test + void refresh_ioException() throws IOException { + when(edrStore.resolveByTransferProcess(anyString())).thenReturn(StoreResult.success(createEdr().build())); + when(mockedTokenService.createToken(anyMap(), anyString())).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token("foo-auth-token").build())); + when(mockedHttpClient.execute(any())).thenThrow(new IOException("test exception")); + + assertThat(tokenRefreshHandler.refreshToken("token-id")).isFailed() + .detail().isEqualTo("Error executing token refresh request: java.io.IOException: test exception"); + } + + @Test + void refresh_accessTokenIsNotJwt() { + when(edrStore.resolveByTransferProcess(anyString())).thenReturn(StoreResult.success(createEdr().property("authorization", "not-jwt").build())); + assertThat(tokenRefreshHandler.refreshToken("token-id")).isFailed() + .detail().startsWith("Failed to parse string claim 'iss'"); + } + + @Test + void refresh_tokenGenerationFailed() { + when(edrStore.resolveByTransferProcess(anyString())).thenReturn(StoreResult.success(createEdr().build())); + when(mockedTokenService.createToken(anyMap(), anyString())).thenReturn(Result.failure("foobar")); + assertThat(tokenRefreshHandler.refreshToken("token-id")).isFailed() + .detail().isEqualTo("foobar"); + } + + @NotNull + private Response createResponse(Object responseBodyObject, int statusCode, String message) throws JsonProcessingException { + var body = responseBodyObject == null ? new byte[0] : objectMapper.writeValueAsBytes(responseBodyObject); + return new Response.Builder() + .code(statusCode) + .protocol(Protocol.HTTP_1_1) + .message(message) + .request(new Request.Builder().url(REFRESH_ENDPOINT).build()) + .body(ResponseBody.create(body, MediaType.parse("application/json"))) + .build(); + } + + private DataAddress.Builder createEdr() { + return DataAddress.Builder.newInstance() + .type("HttpData") + .property("authorization", createJwt()) + .property("refreshToken", "foo-refresh-token") + .property("refreshEndpoint", REFRESH_ENDPOINT); + } + + private static class InvalidEdrProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) { + return Stream.of( + Arguments.of(createJwt(), "foo-refresh-token", null, "refresh endpoint is null"), + Arguments.of(createJwt(), "foo-refresh-token", "", "refresh endpoint is empty"), + Arguments.of(createJwt(), "foo-refresh-token", " ", "refresh endpoint is blank"), + Arguments.of(createJwt(), null, REFRESH_ENDPOINT, "refresh token is null"), + Arguments.of(createJwt(), "", REFRESH_ENDPOINT, "refresh token is empty"), + Arguments.of(createJwt(), " ", REFRESH_ENDPOINT, "refresh token is blank"), + Arguments.of(null, "foo-refresh-token", REFRESH_ENDPOINT, "access token is null") + ); + } + } +} \ No newline at end of file diff --git a/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/e2e/DataPlaneTokenRefreshEndToEndTest.java b/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/e2e/DataPlaneTokenRefreshEndToEndTest.java index 2432700f7..23f2629e4 100644 --- a/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/e2e/DataPlaneTokenRefreshEndToEndTest.java +++ b/edc-tests/edc-dataplane/edc-dataplane-tokenrefresh-tests/src/test/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/e2e/DataPlaneTokenRefreshEndToEndTest.java @@ -37,7 +37,7 @@ import org.eclipse.edc.spi.types.domain.DataAddress; import org.eclipse.edc.spi.types.domain.transfer.DataFlowStartMessage; import org.eclipse.edc.spi.types.domain.transfer.FlowType; -import org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.model.TokenResponse; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c36742228..fae161a3a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -45,6 +45,7 @@ edc-spi-jwt = { module = "org.eclipse.edc:jwt-spi", version.ref = "edc" } edc-spi-token = { module = "org.eclipse.edc:token-spi", version.ref = "edc" } edc-spi-transform = { module = "org.eclipse.edc:transform-spi", version.ref = "edc" } edc-spi-identity-did = { module = "org.eclipse.edc:identity-did-spi", version.ref = "edc" } +edc-spi-edrstore = { module = "org.eclipse.edc:edr-store-spi", version.ref = "edc" } edc-token-core = { module = "org.eclipse.edc:token-core", version.ref = "edc" } edc-spi-oauth2 = { module = "org.eclipse.edc:oauth2-spi", version.ref = "edc" } edc-util = { module = "org.eclipse.edc:util", version.ref = "edc" } diff --git a/settings.gradle.kts b/settings.gradle.kts index dffc3350d..93bdd0340 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -41,6 +41,7 @@ include(":edc-extensions:bpn-validation:bpn-validation-core") include(":edc-extensions:bpn-validation:business-partner-store-sql") include(":edc-extensions:data-encryption") include(":edc-extensions:postgresql-migration") +include(":edc-extensions:tokenrefresh-handler") include(":edc-extensions:provision-additional-headers") include(":edc-extensions:transferprocess-sftp-client") include(":edc-extensions:transferprocess-sftp-common") diff --git a/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/common/TokenRefreshHandler.java b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/common/TokenRefreshHandler.java new file mode 100644 index 000000000..bbfa871ef --- /dev/null +++ b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/common/TokenRefreshHandler.java @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +package org.eclipse.tractusx.edc.spi.tokenrefresh.common; + +import org.eclipse.edc.spi.result.Result; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; + +/** + * Handles token refreshing against an OAuth2-compliant token refresh endpoint. + */ +@FunctionalInterface +public interface TokenRefreshHandler { + /** + * Refreshes a token identified by the token ID and returns the updated token. If tokens are kept in persistent storage or + * a HSM, implementors must update that entry. + * + * @param tokenId The ID of the token, e.g. a {@code jti} claim in JWT tokens. + * @return An updated access+refresh token pair. + */ + Result refreshToken(String tokenId); +} diff --git a/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/spi/DataPlaneTokenRefreshService.java b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/DataPlaneTokenRefreshService.java similarity index 93% rename from spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/spi/DataPlaneTokenRefreshService.java rename to spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/DataPlaneTokenRefreshService.java index a5f855de3..a5d04b2bf 100644 --- a/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/spi/DataPlaneTokenRefreshService.java +++ b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/DataPlaneTokenRefreshService.java @@ -17,10 +17,10 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi; +package org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane; import org.eclipse.edc.spi.result.Result; -import org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.model.TokenResponse; +import org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model.TokenResponse; /** * This service receives an incoming token refresh request, validates it and generates a new token pair (access token + refresh token). diff --git a/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/spi/model/TokenResponse.java b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/model/TokenResponse.java similarity index 95% rename from spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/spi/model/TokenResponse.java rename to spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/model/TokenResponse.java index cf64c5c23..148bf95d8 100644 --- a/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/spi/model/TokenResponse.java +++ b/spi/tokenrefresh-spi/src/main/java/org/eclipse/tractusx/edc/spi/tokenrefresh/dataplane/model/TokenResponse.java @@ -17,7 +17,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.edc.dataplane.tokenrefresh.spi.model; +package org.eclipse.tractusx.edc.spi.tokenrefresh.dataplane.model; import com.fasterxml.jackson.annotation.JsonProperty; From c152939bae7a36fe5b78254227f06b98d131a9b4 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Thu, 14 Mar 2024 16:10:07 +0100 Subject: [PATCH 2/3] DEPENDENCIES --- DEPENDENCIES | 1 + 1 file changed, 1 insertion(+) diff --git a/DEPENDENCIES b/DEPENDENCIES index bb4a66ac1..cb852668b 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -324,6 +324,7 @@ maven/mavencentral/org.eclipse.edc/dsp-transfer-process-transform/0.5.2-SNAPSHOT maven/mavencentral/org.eclipse.edc/dsp-transfer-process/0.5.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/dsp-version-api/0.5.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/dsp/0.5.2-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/edr-store-spi/0.5.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/http-spi/0.5.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/http/0.5.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/iam-mock/0.5.2-SNAPSHOT, Apache-2.0, approved, technology.edc From 362782554176710359d39b76ab77dd7af61fa496 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Thu, 14 Mar 2024 16:12:02 +0100 Subject: [PATCH 3/3] checkstyle, sonar --- .../core/DataPlaneTokenRefreshServiceExtension.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceExtension.java b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceExtension.java index cbffbbbf7..e60aecca1 100644 --- a/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceExtension.java +++ b/edc-extensions/dataplane/dataplane-token-refresh/token-refresh-core/src/main/java/org/eclipse/tractusx/edc/dataplane/tokenrefresh/core/DataPlaneTokenRefreshServiceExtension.java @@ -91,7 +91,7 @@ public DataPlaneTokenRefreshService createRefreshTokenService(ServiceExtensionCo return getTokenRefreshService(context); } - private Integer getExpiryToleranceConfig(ServiceExtensionContext context) { + private int getExpiryToleranceConfig(ServiceExtensionContext context) { return context.getConfig().getInteger(TOKEN_EXPIRY_TOLERANCE_SECONDS_PROPERTY, DEFAULT_TOKEN_EXPIRY_TOLERANCE_SECONDS); } @@ -102,7 +102,7 @@ private DataPlaneTokenRefreshServiceImpl getTokenRefreshService(ServiceExtension var expiryTolerance = getExpiryToleranceConfig(context); var refreshEndpoint = getRefreshEndpointConfig(context, monitor); monitor.debug("Token refresh endpoint: %s".formatted(refreshEndpoint)); - monitor.debug("Token refresh time tolerance: %ds".formatted(expiryTolerance)); + monitor.debug("Token refresh time tolerance: %d s".formatted(expiryTolerance)); tokenRefreshService = new DataPlaneTokenRefreshServiceImpl(clock, tokenValidationService, didPkResolver, accessTokenDataStore, new JwtGenerationService(), getPrivateKeySupplier(context), context.getMonitor(), refreshEndpoint, expiryTolerance, vault, typeManager.getMapper());