diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenService.java index eee35000a..9159e2aa9 100644 --- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenService.java +++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenService.java @@ -27,6 +27,7 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.util.AbstractMap; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -108,6 +109,7 @@ private OAuth2TokenResponse executeRequest(HttpPost httpPost) throws OAuth2Servi throw OAuth2ServiceException.builder("Error retrieving JWT token") .withStatusCode(statusCode) .withUri(httpPost.getURI()) + .withHeaders(Arrays.toString(response.getAllHeaders())) .withResponseBody(responseBodyAsString) .build(); } diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2ServiceException.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2ServiceException.java index 06a7fd76f..b323bc01b 100644 --- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2ServiceException.java +++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2ServiceException.java @@ -7,6 +7,9 @@ import java.io.IOException; import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -18,6 +21,7 @@ public class OAuth2ServiceException extends IOException { private static final long serialVersionUID = 1L; private Integer httpStatusCode = 0; + private final List headers = new ArrayList<>(); public OAuth2ServiceException(String message) { super(message); @@ -41,9 +45,15 @@ public OAuth2ServiceException(String message, Integer httpStatusCode) { * * @param message * the error message + * @param httpStatusCode + * the status code of the HTTP service request + * @param headers + * the headers of the HTTP service request */ - public static Builder builder(String message) { - return new Builder(message); + + OAuth2ServiceException(String message, Integer httpStatusCode, List headers) { + this(message, httpStatusCode); + this.headers.addAll(headers); } /** @@ -56,20 +66,39 @@ public Integer getHttpStatusCode() { return httpStatusCode; } + /** + * Returns the HTTP headers of the failed OAuth2 service request + * @return list of HTTP headers + */ + public List getHeaders() { + return this.headers; + } + + /** + * Creates an exception. + * + * @param message + * the error message + */ + public static Builder builder(String message) { + return new Builder(message); + } + public static class Builder { - private String message; + private final String message; private Integer httpStatusCode; private URI serverUri; private String responseBody; - private String headers; + private final List headers = new ArrayList<>(); + private String headersString; public Builder(String message) { this.message = message; } /** - * Parameterizes the Exception with a HTTP status code. - * + * Parameterizes the Exception with an HTTP status code. + * * @param httpStatusCode * the http status code * @return the builder @@ -90,21 +119,21 @@ public Builder withResponseBody(String responseBody) { } public Builder withHeaders(String... headers) { - this.headers = "["; - for (String header : headers) { - this.headers += header; - } - this.headers += "]"; + List headerList = Arrays.stream(headers).filter(Objects::nonNull).collect(Collectors.toList()); + + this.headers.addAll(headerList); + this.headersString = headerList.stream().collect(Collectors.joining(", ", "[", "]")); + return this; } public OAuth2ServiceException build() { - String message = Stream + String m = Stream .of(this.message, createUriMessage(), createStatusCodeMessage(), createResponseBodyMessage(), createHeaderMessage()) .filter(Objects::nonNull) .collect(Collectors.joining(". ")); - return new OAuth2ServiceException(message, httpStatusCode); + return new OAuth2ServiceException(m, httpStatusCode, headers); } private String createResponseBodyMessage() { @@ -120,7 +149,7 @@ private String createUriMessage() { } private String createHeaderMessage() { - return headers == null ? null : "Headers " + headers; + return headersString == null ? null : "Headers " + headersString; } } diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenService.java index 58200f014..453a74b0b 100644 --- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenService.java +++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenService.java @@ -20,7 +20,10 @@ import javax.annotation.Nonnull; import java.net.URI; +import java.util.Collections; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static com.sap.cloud.security.xsuaa.Assertions.assertNotNull; import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.*; @@ -87,12 +90,12 @@ protected OAuth2TokenResponse requestAccessToken(URI tokenEndpointUri, HttpHeade String warningMsg = String.format( "Error retrieving JWT token. Received status code %s. Call to XSUAA was not successful: %s", ex.getStatusCode(), ex.getResponseBodyAsString()); - throw new OAuth2ServiceException(warningMsg); + throw new OAuth2ServiceException(warningMsg, ex.getStatusCode().value(), getHeaders(ex.getResponseHeaders())); } catch (HttpServerErrorException ex) { String warningMsg = String.format("Server error while obtaining access token from XSUAA (%s): %s", ex.getStatusCode(), ex.getResponseBodyAsString()); LOGGER.error(warningMsg, ex); - throw new OAuth2ServiceException(warningMsg); + throw new OAuth2ServiceException(warningMsg, ex.getStatusCode().value(), getHeaders(ex.getResponseHeaders())); } catch (ResourceAccessException ex) { String warningMsg = String.format( "RestClient isn't configured properly - Error while obtaining access token from XSUAA (%s): %s", @@ -112,15 +115,22 @@ protected OAuth2TokenResponse requestAccessToken(URI tokenEndpointUri, HttpHeade return new OAuth2TokenResponse(accessToken, expiresIn, refreshToken, tokenType); } + private static List getHeaders(org.springframework.http.HttpHeaders ex) { + if (ex != null){ + return ex.toSingleValueMap().entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect( + Collectors.toList()); + } + return Collections.emptyList(); + } + /** - * Creates a copy of the given map or an new empty map of type MultiValueMap. + * Creates a copy of the given map or a new empty map of type MultiValueMap. * * @return a new @link{MultiValueMap} that contains all entries of the optional * map. */ private MultiValueMap copyIntoForm(Map parameters) { - @SuppressWarnings("unchecked") - MultiValueMap formData = new LinkedMultiValueMap(); + MultiValueMap formData = new LinkedMultiValueMap<>(); if (parameters != null) { parameters.forEach(formData::add); } diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/OAuth2ServiceExceptionTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/OAuth2ServiceExceptionTest.java new file mode 100644 index 000000000..5f75c6c6a --- /dev/null +++ b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/OAuth2ServiceExceptionTest.java @@ -0,0 +1,38 @@ +package com.sap.cloud.security.xsuaa.client; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class OAuth2ServiceExceptionTest { + public static final String SERVICE_EXCEPTION = "Service Exception"; + private static List headers; + private static OAuth2ServiceException builtWithHeaders; + private static OAuth2ServiceException createdWithHeaders; + + @BeforeAll + static void setup() { + headers = new ArrayList<>(); + headers.add("header1=value1"); + headers.add("header2=value2"); + builtWithHeaders = OAuth2ServiceException.builder(SERVICE_EXCEPTION).withHeaders(headers.toArray(new String[0])).withStatusCode(400).build(); + createdWithHeaders = new OAuth2ServiceException(SERVICE_EXCEPTION, 400, headers); + } + + @Test + void testWithHeaders() { + assertIterableEquals(headers, builtWithHeaders.getHeaders()); + assertTrue(builtWithHeaders.getMessage().contains(SERVICE_EXCEPTION)); + assertTrue(builtWithHeaders.getMessage().contains("[header1=value1, header2=value2]")); + assertEquals(400, builtWithHeaders.getHttpStatusCode()); + + assertIterableEquals(headers, createdWithHeaders.getHeaders()); + assertTrue(createdWithHeaders.getMessage().contains(SERVICE_EXCEPTION)); + assertFalse(createdWithHeaders.getMessage().contains("[header1=value1, header2=value2]")); + assertEquals(400, createdWithHeaders.getHttpStatusCode()); + } +} \ No newline at end of file