From 5bae31cf41792631c9dc7ae89fc679d5eabba195 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Mon, 22 Apr 2024 15:41:49 +0200 Subject: [PATCH] feat: map expires_in in TokenRepresentation (#4131) * feat: map expires_in in TokenRepresentation * chore: deps file * chore: fix checkstyle --- .../iam/oauth2/client/Oauth2ClientImpl.java | 48 ++++++++++---- .../oauth2/client/Oauth2ClientImplTest.java | 65 +++++++++++++++++++ 2 files changed, 101 insertions(+), 12 deletions(-) diff --git a/extensions/common/iam/oauth2/oauth2-client/src/main/java/org/eclipse/edc/iam/oauth2/client/Oauth2ClientImpl.java b/extensions/common/iam/oauth2/oauth2-client/src/main/java/org/eclipse/edc/iam/oauth2/client/Oauth2ClientImpl.java index ed897e2e7de..63cb1989d79 100644 --- a/extensions/common/iam/oauth2/oauth2-client/src/main/java/org/eclipse/edc/iam/oauth2/client/Oauth2ClientImpl.java +++ b/extensions/common/iam/oauth2/oauth2-client/src/main/java/org/eclipse/edc/iam/oauth2/client/Oauth2ClientImpl.java @@ -26,8 +26,10 @@ import org.jetbrains.annotations.NotNull; import java.io.IOException; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import static org.eclipse.edc.http.spi.FallbackFactories.retryWhenStatusIsNot; @@ -38,6 +40,7 @@ public class Oauth2ClientImpl implements Oauth2Client { private static final String FORM_URLENCODED = "application/x-www-form-urlencoded"; private static final String APPLICATION_JSON = "application/json"; private static final String RESPONSE_ACCESS_TOKEN_CLAIM = "access_token"; + private static final String RESPONSE_EXPIRES_IN_CLAIM = "expires_in"; private final EdcHttpClient httpClient; private final TypeManager typeManager; @@ -47,18 +50,6 @@ public Oauth2ClientImpl(EdcHttpClient httpClient, TypeManager typeManager) { this.typeManager = typeManager; } - @Override - public Result requestToken(Oauth2CredentialsRequest request) { - return httpClient.execute(toRequest(request), List.of(retryWhenStatusIsNot(200)), this::handleResponse); - } - - private Result handleResponse(Response response) { - return getStringBody(response) - .map(it -> typeManager.readValue(it, Map.class)) - .map(it -> it.get(RESPONSE_ACCESS_TOKEN_CLAIM).toString()) - .map(it -> TokenRepresentation.Builder.newInstance().token(it).build()); - } - private static Request toRequest(Oauth2CredentialsRequest request) { return new Request.Builder() .url(request.getUrl()) @@ -76,6 +67,39 @@ private static FormBody createRequestBody(Oauth2CredentialsRequest request) { return builder.build(); } + @Override + public Result requestToken(Oauth2CredentialsRequest request) { + return httpClient.execute(toRequest(request), List.of(retryWhenStatusIsNot(200)), this::handleResponse); + } + + private Result handleResponse(Response response) { + return getStringBody(response) + .map(it -> typeManager.readValue(it, Map.class)) + .map(this::mapResponse); + } + + private TokenRepresentation mapResponse(Map response) { + var builder = TokenRepresentation.Builder.newInstance(); + builder.token(response.get(RESPONSE_ACCESS_TOKEN_CLAIM).toString()); + + Optional.ofNullable(response.get(RESPONSE_EXPIRES_IN_CLAIM)) + .flatMap(expiresIn -> { + if (expiresIn instanceof Number n) { + return Optional.of(n.longValue()); + } + return Optional.empty(); + }) + .ifPresent(builder::expiresIn); + + var additional = new HashMap<>(response); + additional.remove(RESPONSE_EXPIRES_IN_CLAIM); + additional.remove(RESPONSE_ACCESS_TOKEN_CLAIM); + + builder.additional(additional); + + return builder.build(); + } + @NotNull private Result getStringBody(Response response) { try (var body = response.body()) { diff --git a/extensions/common/iam/oauth2/oauth2-client/src/test/java/org/eclipse/edc/iam/oauth2/client/Oauth2ClientImplTest.java b/extensions/common/iam/oauth2/oauth2-client/src/test/java/org/eclipse/edc/iam/oauth2/client/Oauth2ClientImplTest.java index 2dd3a99239b..d8fbd38ccfc 100644 --- a/extensions/common/iam/oauth2/oauth2-client/src/test/java/org/eclipse/edc/iam/oauth2/client/Oauth2ClientImplTest.java +++ b/extensions/common/iam/oauth2/oauth2-client/src/test/java/org/eclipse/edc/iam/oauth2/client/Oauth2ClientImplTest.java @@ -69,6 +69,71 @@ void verifyRequestTokenSuccess() { assertThat(result.getContent().getToken()).isEqualTo("token"); } + @Test + void verifyRequestTokenSuccess_withExpiresIn() { + var request = createRequest(); + + var formParameters = new Parameters( + request.getParams().entrySet().stream() + .map(entry -> Parameter.param(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()) + ); + + var expectedRequest = HttpRequest.request().withBody(new ParameterBody(formParameters)); + var responseBody = typeManager.writeValueAsString(Map.of("access_token", "token", "expires_in", 1800)); + server.when(expectedRequest).respond(HttpResponse.response().withBody(responseBody, APPLICATION_JSON)); + + var result = client.requestToken(request); + + assertThat(result.succeeded()).isTrue(); + assertThat(result.getContent().getToken()).isEqualTo("token"); + assertThat(result.getContent().getExpiresIn()).isEqualTo(1800); + assertThat(result.getContent().getAdditional()).doesNotContainKeys("token", "expires_in"); + } + + @Test + void verifyRequestTokenSuccess_withExpiresIn_whenNotNumber() { + var request = createRequest(); + + var formParameters = new Parameters( + request.getParams().entrySet().stream() + .map(entry -> Parameter.param(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()) + ); + + var expectedRequest = HttpRequest.request().withBody(new ParameterBody(formParameters)); + var responseBody = typeManager.writeValueAsString(Map.of("access_token", "token", "expires_in", "wrong")); + server.when(expectedRequest).respond(HttpResponse.response().withBody(responseBody, APPLICATION_JSON)); + + var result = client.requestToken(request); + + assertThat(result.succeeded()).isTrue(); + assertThat(result.getContent().getToken()).isEqualTo("token"); + assertThat(result.getContent().getExpiresIn()).isNull(); + } + + @Test + void verifyRequestTokenSuccess_withAdditionalProperties() { + var request = createRequest(); + + var formParameters = new Parameters( + request.getParams().entrySet().stream() + .map(entry -> Parameter.param(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()) + ); + + var expectedRequest = HttpRequest.request().withBody(new ParameterBody(formParameters)); + var responseBody = typeManager.writeValueAsString(Map.of("access_token", "token", "expires_in", 1800, "scope", "test")); + server.when(expectedRequest).respond(HttpResponse.response().withBody(responseBody, APPLICATION_JSON)); + + var result = client.requestToken(request); + + assertThat(result.succeeded()).isTrue(); + assertThat(result.getContent().getToken()).isEqualTo("token"); + assertThat(result.getContent().getExpiresIn()).isEqualTo(1800); + assertThat(result.getContent().getAdditional()).containsEntry("scope", "test"); + } + @Test void verifyFailureIfServerCallFails() { var request = createRequest();