From 57591db8c3e944ea31cf56bb4048c17e5c25eeba Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Mon, 9 Sep 2019 16:17:00 +0200 Subject: [PATCH 01/22] remove xs2 package --- .../testservice/api/basic/TestController.java | 26 +- .../api/nohttp/MyEventHandler.java | 15 - .../testservice/api/v1/TestController.java | 16 - .../XsuaaTokenValidationTest.java | 10 - .../sap/cloud/security/xsuaa/token/Token.java | 16 - .../security/xsuaa/token/XsuaaToken.java | 33 - .../xsuaa/token/XsuaaTokenExchanger.java | 226 ------ .../security/container/SecurityContext.java | 92 --- .../sap/xs2/security/container/UserInfo.java | 657 ------------------ .../security/container/UserInfoException.java | 21 - .../container/XSTokenRequestImpl.java | 207 ------ .../security/xsuaa/token/XsuaaTokenTest.java | 65 -- .../UserInfoTestClientCredentials.java | 68 -- .../container/UserInfoTestEndUser.java | 39 -- .../security/container/UserInfoTestSAML.java | 58 -- .../security/container/UserInfoTestUser.java | 186 ----- .../security/container/UserInfoTestUtil.java | 23 - .../container/XSTokenRequestImplTest.java | 80 --- token-client/README.md | 2 +- 19 files changed, 18 insertions(+), 1822 deletions(-) delete mode 100644 spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenExchanger.java delete mode 100644 spring-xsuaa/src/main/java/com/sap/xs2/security/container/SecurityContext.java delete mode 100644 spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfo.java delete mode 100644 spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfoException.java delete mode 100644 spring-xsuaa/src/main/java/com/sap/xs2/security/container/XSTokenRequestImpl.java delete mode 100644 spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestClientCredentials.java delete mode 100644 spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestEndUser.java delete mode 100644 spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestSAML.java delete mode 100644 spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestUser.java delete mode 100644 spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestUtil.java delete mode 100644 spring-xsuaa/src/test/java/com/sap/xs2/security/container/XSTokenRequestImplTest.java diff --git a/spring-xsuaa-it/src/main/java/testservice/api/basic/TestController.java b/spring-xsuaa-it/src/main/java/testservice/api/basic/TestController.java index b7967499d3..b788ffb4a5 100644 --- a/spring-xsuaa-it/src/main/java/testservice/api/basic/TestController.java +++ b/spring-xsuaa-it/src/main/java/testservice/api/basic/TestController.java @@ -20,7 +20,6 @@ import static org.hamcrest.core.IsCollectionContaining.hasItem; import static org.junit.Assert.assertThat; -import java.net.URISyntaxException; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -33,11 +32,17 @@ import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.client.ClientCredentials; +import com.sap.cloud.security.xsuaa.client.OAuth2TokenResponse; +import com.sap.cloud.security.xsuaa.client.XsuaaDefaultEndpoints; +import com.sap.cloud.security.xsuaa.client.XsuaaOAuth2TokenService; import com.sap.cloud.security.xsuaa.token.Token; -import com.sap.xs2.security.container.XSTokenRequestImpl; -import com.sap.xsa.security.container.XSTokenRequest; +import com.sap.cloud.security.xsuaa.tokenflows.ClientCredentialsTokenFlow; +import com.sap.cloud.security.xsuaa.tokenflows.TokenFlowException; +import com.sap.cloud.security.xsuaa.tokenflows.XsuaaTokenFlows; @RestController @Profile({ "test.api.basic" }) @@ -66,14 +71,17 @@ public void checkScope(@AuthenticationPrincipal Token token) { } @GetMapping("/requesttoken") - public String requestToken(@AuthenticationPrincipal Token token) throws URISyntaxException { - XSTokenRequestImpl tokenRequest = new XSTokenRequestImpl(serviceConfiguration.getUaaUrl()); - tokenRequest.setClientId("c1").setClientSecret("s1").setType(XSTokenRequest.TYPE_CLIENT_CREDENTIALS_TOKEN); + public String requestToken(@AuthenticationPrincipal Token token) throws TokenFlowException { Map azMape = new HashMap(); azMape.put("a", "b"); azMape.put("c", "d"); - tokenRequest.setAdditionalAuthorizationAttributes(azMape); - String newToken = token.requestToken(tokenRequest); - return newToken; + + XsuaaTokenFlows tokenFlows = new XsuaaTokenFlows(new XsuaaOAuth2TokenService(new RestTemplate()), + new XsuaaDefaultEndpoints(serviceConfiguration.getUaaUrl()), new ClientCredentials("c1", "s1")); + ClientCredentialsTokenFlow ccTokenFlow = tokenFlows.clientCredentialsTokenFlow().attributes(azMape) + .subdomain(token.getSubdomain()); + + OAuth2TokenResponse newToken = ccTokenFlow.execute(); + return newToken.getAccessToken(); } } diff --git a/spring-xsuaa-it/src/main/java/testservice/api/nohttp/MyEventHandler.java b/spring-xsuaa-it/src/main/java/testservice/api/nohttp/MyEventHandler.java index 7b8eb3368a..886908b3d4 100644 --- a/spring-xsuaa-it/src/main/java/testservice/api/nohttp/MyEventHandler.java +++ b/spring-xsuaa-it/src/main/java/testservice/api/nohttp/MyEventHandler.java @@ -10,13 +10,11 @@ import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.stereotype.Service; import com.sap.cloud.security.xsuaa.extractor.LocalAuthoritiesExtractor; import com.sap.cloud.security.xsuaa.token.SpringSecurityContext; -import com.sap.xs2.security.container.SecurityContext; @Service @Profile({ "test.api.nohttp" }) @@ -29,19 +27,6 @@ public class MyEventHandler { @Autowired JwtDecoder jwtDecoder; - @Deprecated - public void onEvent_deprecated(String myEncodedJwtToken) { - if (myEncodedJwtToken != null) { - Jwt jwtToken = jwtDecoder.decode(myEncodedJwtToken); - SecurityContext.init(appId, jwtToken, true); - } - try { - handleEvent(); - } finally { - SpringSecurityContext.clear(); - } - } - public void onEvent(String myEncodedJwtToken) { if (myEncodedJwtToken != null) { SpringSecurityContext.init(myEncodedJwtToken, jwtDecoder, new LocalAuthoritiesExtractor(appId)); diff --git a/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java b/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java index 270cb9e487..f2a6d57db7 100644 --- a/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java +++ b/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java @@ -3,7 +3,6 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertThat; -import java.net.URISyntaxException; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -28,8 +27,6 @@ import com.sap.cloud.security.xsuaa.tokenflows.ClientCredentialsTokenFlow; import com.sap.cloud.security.xsuaa.tokenflows.TokenFlowException; import com.sap.cloud.security.xsuaa.tokenflows.XsuaaTokenFlows; -import com.sap.xs2.security.container.XSTokenRequestImpl; -import com.sap.xsa.security.container.XSTokenRequest; @RestController @Profile({ "test.api.v1" }) @@ -81,19 +78,6 @@ public void checkScope(@AuthenticationPrincipal Token token) { assertThat(authorities, not(hasItem(new SimpleGrantedAuthority("Other")))); } - @GetMapping("/requesttoken") - @Deprecated - public String requestToken(@AuthenticationPrincipal Token token) throws URISyntaxException { - XSTokenRequestImpl tokenRequest = new XSTokenRequestImpl(serviceConfiguration.getUaaUrl()); - tokenRequest.setClientId("c1").setClientSecret("s1").setType(XSTokenRequest.TYPE_CLIENT_CREDENTIALS_TOKEN); - Map azMape = new HashMap(); - azMape.put("a", "b"); - azMape.put("c", "d"); - tokenRequest.setAdditionalAuthorizationAttributes(azMape); - String newToken = token.requestToken(tokenRequest); - return newToken; - } - @GetMapping("/clientCredentialsToken") public String requestClientCredentialsToken(@AuthenticationPrincipal Token token) throws TokenFlowException { Map azMape = new HashMap(); diff --git a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaTokenValidationTest.java b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaTokenValidationTest.java index d4497226c8..65df56f78c 100644 --- a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaTokenValidationTest.java +++ b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaTokenValidationTest.java @@ -54,16 +54,6 @@ public void test_Scope() throws Exception { } @Test - @Deprecated - public void test_requesttoken() throws Exception { - this.mvc.perform( - get("/requesttoken").with(bearerToken(JWTUtil.createJWT("/saml.txt", "uaa", "legacy-token-key")))) - .andExpect(status().isOk()).andExpect( - content().string(containsString(".ewogICJqdGkiOiAiOGU3YjNiMDAtNzc1MS00YjQ2LTliMWEtNWE0NmEyY"))); - } - - @Test - @Deprecated public void test_clientcredentialstoken() throws Exception { this.mvc.perform( get("/clientCredentialsToken") diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java index 24a5da5af3..22eb5891fa 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java @@ -164,22 +164,6 @@ public interface Token extends UserDetails { */ String getAppToken(); - /** - * Exchange a token into a token from another service instance - *

- * - * @deprecated in favor of the @link{XsuaaTokenFlows} API. - * @since 2.0.0 - * - * @param tokenRequest - * request data - * @return requested token - * @throws URISyntaxException - * in case of wron URLs - */ - @Deprecated - String requestToken(XSTokenRequest tokenRequest) throws URISyntaxException; - /** * Returns list of scopes with appId prefix, e.g. "<my-app!t123>.Display". * diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java index 86f62bc3a2..dfc118e3e0 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java @@ -33,7 +33,6 @@ import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; -import com.sap.xs2.security.container.XSTokenRequestImpl; import com.sap.xsa.security.container.XSTokenRequest; import net.minidev.json.JSONArray; @@ -227,38 +226,6 @@ public String getAppToken() { return getTokenValue(); } - @Override - public String requestToken(XSTokenRequest tokenRequest) { - Assert.notNull(tokenRequest, "TokenRequest argument is required"); - Assert.isTrue(tokenRequest.isValid(), "TokenRequest is not valid"); - - RestOperations restOperations = new RestTemplate(); - - if (tokenRequest instanceof XSTokenRequestImpl - && ((XSTokenRequestImpl) tokenRequest).getRestTemplate() != null) { - restOperations = ((XSTokenRequestImpl) tokenRequest).getRestTemplate(); - } - - String baseUrl = tokenRequest.getTokenEndpoint().toString().replace(tokenRequest.getTokenEndpoint().getPath(), - ""); - - // initialize token flows api - OAuth2TokenService oAuth2TokenService = new XsuaaOAuth2TokenService(restOperations); - xsuaaTokenFlows = new XsuaaTokenFlows(oAuth2TokenService, new XsuaaDefaultEndpoints(baseUrl), - new ClientCredentials( - tokenRequest.getClientId(), tokenRequest.getClientSecret())); - - switch (tokenRequest.getType()) { - case XSTokenRequest.TYPE_USER_TOKEN: - return performUserTokenFlow(tokenRequest); - case XSTokenRequest.TYPE_CLIENT_CREDENTIALS_TOKEN: - return performClientCredentialsFlow(tokenRequest); - default: - throw new UnsupportedOperationException( - "Found unsupported XSTokenRequest type. The only supported types are XSTokenRequest.TYPE_USER_TOKEN and XSTokenRequest.TYPE_CLIENT_CREDENTIALS_TOKEN."); - } - } - @Override public Collection getScopes() { List scopesList = getClaimAsStringList(TokenClaims.CLAIM_SCOPES); diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenExchanger.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenExchanger.java deleted file mode 100644 index 786b9ef8f5..0000000000 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenExchanger.java +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved. - * This file is licensed under the Apache Software License, - * v. 2 except as noted otherwise in the LICENSE file - * https://github.com/SAP/cloud-security-xsuaa-integration/blob/master/LICENSE - */ -package com.sap.cloud.security.xsuaa.token; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Base64; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.util.Assert; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import com.sap.xs2.security.container.UserInfoException; -import com.sap.xsa.security.container.XSTokenRequest; - -import net.minidev.json.JSONObject; - -/** - * @deprecated in favor of XsuaaTokenFlows API. - */ -@Deprecated -public class XsuaaTokenExchanger { - Token token; - RestTemplate restTemplate; - - XsuaaTokenExchanger(RestTemplate restTemplate, Token token) { - Assert.notNull(token, "token is required"); - this.token = token; - this.restTemplate = restTemplate != null ? restTemplate : new RestTemplate(); - } - - /** - * Exchange a token into a token from another service instance - * - * @param tokenRequest - * request data - * @throws URISyntaxException - * in case of inconsistent urls - * @throws UserInfoException - * in case of token exchange errors - * @return requested token - */ - public String requestToken(XSTokenRequest tokenRequest) throws UserInfoException, URISyntaxException { - Assert.isTrue(tokenRequest.isValid(), "Invalid grant type or missing parameters for requested grant type."); - // build authorities string for additional authorization attributes - String authorities = null; - if (tokenRequest.getAdditionalAuthorizationAttributes() != null) { - Map azAttrMap = new HashMap<>(); - azAttrMap.put("az_attr", tokenRequest.getAdditionalAuthorizationAttributes()); - StringBuilder azStringBuilder = new StringBuilder(); - try { - JSONObject.writeJSON(azAttrMap, azStringBuilder); - } catch (IOException e) { - throw new UserInfoException("Error creating json representation", e); - } - authorities = azStringBuilder.toString(); - } - // check whether token endpoint has the correct subdomain, if not replace it - // with the subdomain of the token - String tokenSubdomain = token.getSubdomain(); - String tokenRequestSubdomain = getSubdomain(tokenRequest.getTokenEndpoint().toString()); - if (tokenSubdomain != null && tokenRequestSubdomain != null && !tokenSubdomain.equals(tokenRequestSubdomain)) { - tokenRequest.setTokenEndpoint(replaceSubdomain(tokenRequest.getTokenEndpoint(), tokenSubdomain)); - } - // request the token based on the type - switch (tokenRequest.getType()) { - case XSTokenRequest.TYPE_USER_TOKEN: - return requestTokenNamedUser(tokenRequest.getClientId(), tokenRequest.getClientSecret(), - tokenRequest.getTokenEndpoint().toString(), authorities); - case XSTokenRequest.TYPE_CLIENT_CREDENTIALS_TOKEN: - return requestTokenTechnicalUser(tokenRequest, authorities); - default: - throw new UserInfoException("Invalid grant type."); - } - } - - /** - * Replace the subdomain in the given uri with the given subdomain - * - * @param uri - * uri - * @param subdomain - * subdomain - * @return subdomain - */ - protected URI replaceSubdomain(URI uri, String subdomain) { - if (uri == null || subdomain == null || !uri.getHost().contains(".")) { - return null; - } - UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme(uri.getScheme()) - .host(subdomain + uri.getHost().substring(uri.getHost().indexOf("."))).port(uri.getPort()) - .path(uri.getPath()); - return uri.resolve(builder.build().toString()); - } - - private String requestTokenTechnicalUser(XSTokenRequest tokenRequest, String authorities) throws UserInfoException { - // note: consistency checks (clientid, clientsecret and url) have already been - // executed - // build uri for client credentials flow - UriComponentsBuilder builder = UriComponentsBuilder.fromUri(tokenRequest.getTokenEndpoint()) - .queryParam("grant_type", "client_credentials"); - if (authorities != null) { - builder.queryParam("authorities", authorities); - } - // build http headers - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.ACCEPT, "application/json"); - String credentials = tokenRequest.getClientId() + ":" + tokenRequest.getClientSecret(); - String base64Creds = Base64.getEncoder().encodeToString(credentials.getBytes()); - headers.add(HttpHeaders.AUTHORIZATION, "Basic " + base64Creds); - HttpEntity entity = new HttpEntity(headers); - // request the token - ResponseEntity responseEntity = restTemplate.postForEntity(builder.build().encode().toUri(), entity, - Map.class); - if (responseEntity.getStatusCode() == HttpStatus.UNAUTHORIZED) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: client_credentials). Client credentials invalid"); - } - if (responseEntity.getStatusCode() != HttpStatus.OK) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: client_credentials). HTTP status code: " - + responseEntity.getStatusCode()); - } - return responseEntity.getBody().get("access_token").toString(); - } - - private String requestTokenNamedUser(String serviceClientId, String serviceClientSecret, String serviceUaaUrl, - String authorities) throws UserInfoException { - // consistency checks - if (serviceClientId == null || serviceClientSecret == null) { - throw new UserInfoException("Invalid service credentials: Missing clientid/clientsecret."); - } - if (serviceUaaUrl == null) { - throw new UserInfoException("Invalid service credentials: Missing url."); - } - if (!checkScope("uaa.user")) { - throw new UserInfoException("JWT token does not include scope 'uaa.user'."); - } - // build uri for user token flow - UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(serviceUaaUrl) - .queryParam("grant_type", "user_token").queryParam("response_type", "token") - .queryParam("client_id", serviceClientId); - if (authorities != null) { - builder.queryParam("authorities", authorities); - } - // build http headers - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.ACCEPT, "application/json"); - headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + token.getAppToken()); - HttpEntity entity = new HttpEntity(headers); - // request the token - ResponseEntity responseEntity = restTemplate.postForEntity(builder.build().encode().toUri(), entity, - Map.class); - if (responseEntity.getStatusCode() == HttpStatus.UNAUTHORIZED) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: user_token). Bearer token invalid, requesting client does not have grant_type=user_token or no scopes were granted."); - - } - if (responseEntity.getStatusCode() != HttpStatus.OK) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: user_token). HTTP status code: " - + responseEntity.getStatusCode()); - - } - // build uri for refresh token flow - builder = UriComponentsBuilder.fromHttpUrl(serviceUaaUrl).queryParam("grant_type", "refresh_token") - .queryParam("refresh_token", responseEntity.getBody().get("refresh_token").toString()); - // build http headers - headers.clear(); - String credentials = serviceClientId + ":" + serviceClientSecret; - String base64Creds = Base64.getEncoder().encodeToString(credentials.getBytes()); - headers.add(HttpHeaders.ACCEPT, "application/json"); - headers.add(HttpHeaders.AUTHORIZATION, "Basic " + base64Creds); - entity = new HttpEntity(headers); - // request the token - responseEntity = restTemplate.postForEntity(builder.build().encode().toUri(), entity, Map.class); - if (responseEntity.getStatusCode() == HttpStatus.UNAUTHORIZED) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: refresh_token). Client credentials invalid"); - } - if (responseEntity.getStatusCode() != HttpStatus.OK) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: refresh_token). HTTP status code: " - + responseEntity.getStatusCode()); - } - return responseEntity.getBody().get("access_token").toString(); - } - - /** - * Get the subdomain (host) from the given url - * - * @param url - * url of given endpoint - * @return subdomain - */ - protected String getSubdomain(String url) { - String host = null; - try { - host = new URI(url).getHost(); - } catch (URISyntaxException e) { - return null; - } - if (host == null || !host.contains(".")) { - return null; - } - return host.split("\\.")[0]; - } - - protected boolean checkScope(String scope) { - List scopes = ((XsuaaToken) token).getClaimAsStringList(TokenClaims.CLAIM_SCOPES); - return scopes.contains(scope); - } - -} diff --git a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/SecurityContext.java b/spring-xsuaa/src/main/java/com/sap/xs2/security/container/SecurityContext.java deleted file mode 100644 index 5ce120b1c8..0000000000 --- a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/SecurityContext.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.sap.xs2.security.container; - -import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor; -import com.sap.cloud.security.xsuaa.extractor.LocalAuthoritiesExtractor; -import com.sap.cloud.security.xsuaa.token.SpringSecurityContext; -import com.sap.cloud.security.xsuaa.token.Token; -import com.sap.cloud.security.xsuaa.token.TokenAuthenticationConverter; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.jwt.JwtDecoder; - -/** - * As part of this Spring xsuaa library, we need to get rid of - * {@code com.sap.xs2.security.container} package. It will be removed with - * version {@code 2.0.0} - * - * @deprecated use {@link SpringSecurityContext} class instead. Will be removed - * with version 2.0. - */ -@Deprecated -public class SecurityContext { - /** - * Obtain the UserInfo object from the Spring Security Context - * - * @return UserInfo object - * @throws UserInfoException - * in case of error - * @deprecated use {@link #getToken()} instead. - */ - static public UserInfo getUserInfo() throws UserInfoException { - if (SecurityContextHolder.getContext().getAuthentication() != null) { - if (SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof UserInfo) { - return (UserInfo) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - } else { - throw new UserInfoException("Unexpected principal type"); - } - } else { - throw new UserInfoException("Not authenticated"); - } - } - - /** - * Obtain the Token object from the Spring Security Context - * - * @return Token object - * @throws AccessDeniedException - * in case there is no token, user is not authenticated - *

- * Note: This method is introduced with xsuaa spring client lib. - * @deprecated method is moved to {@link SpringSecurityContext#getToken()} - */ - static public Token getToken() { - return SpringSecurityContext.getToken(); - } - - /** - * Initializes the Spring Security Context and extracts the authorities. - * - * @param appId - * the application id e.g. myXsAppname!t123 - * @param token - * the jwt token - * @param extractLocalScopesOnly - * true when {@link Token#getAuthorities()} should only extract local - * scopes. Local scopes means that non-application specific scopes - * are filtered out and scopes are returned without appId prefix, - * e.g. "Display". - * @deprecated use method - * {@link SpringSecurityContext#init(String, JwtDecoder, AuthoritiesExtractor)} - * instead - */ - static public void init(String appId, Jwt token, boolean extractLocalScopesOnly) { - TokenAuthenticationConverter authenticationConverter = new TokenAuthenticationConverter( - new LocalAuthoritiesExtractor(appId)); - Authentication authentication = authenticationConverter.convert(token); - - SecurityContextHolder.createEmptyContext(); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - - /** - * Cleans up the Spring Security Context and release thread locals for Garbage - * Collector to avoid memory leaks resources. - * - * @deprecated method is moved to {@link SpringSecurityContext#clear()} - */ - static public void clear() { - SpringSecurityContext.clear(); - } -} \ No newline at end of file diff --git a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfo.java b/spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfo.java deleted file mode 100644 index 7e63c82526..0000000000 --- a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfo.java +++ /dev/null @@ -1,657 +0,0 @@ -package com.sap.xs2.security.container; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.*; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import com.sap.xsa.security.container.XSTokenRequest; -import com.sap.xsa.security.container.XSUserInfo; - -import net.minidev.json.JSONArray; -import net.minidev.json.JSONObject; - -/** - * Class providing access to common user related attributes extracted from the - * JWT token. - * - * @deprecated will be removed with version 2.0 - */ -@Deprecated -public class UserInfo implements XSUserInfo { - - private static final String USER_NAME = "user_name"; - private static final String GIVEN_NAME = "given_name"; - private static final String FAMILY_NAME = "family_name"; - private static final String EMAIL = "email"; - private static final String CID = "cid"; - private static final String ORIGIN = "origin"; - private static final String GRANT_TYPE = "grant_type"; - private static final String ADDITIONAL_AZ_ATTR = "az_attr"; - private static final String ZONE_ID = "zid"; - private static final String EXTERNAL_ATTR = "ext_attr"; - private static final String XS_SYSTEM_ATTRIBUTES = "xs.system.attributes"; - private static final String HDB_NAMEDUSER_SAML = "hdb.nameduser.saml"; - private static final String SERVICEINSTANCEID = "serviceinstanceid"; - private static final String ZDN = "zdn"; - private static final String SYSTEM = "SYSTEM"; - private static final String HDB = "HDB"; - public static final String XS_USER_ATTRIBUTES = "xs.user.attributes"; - public static final String SCOPE = "scope"; - public static final String GRANTTYPE_CLIENTCREDENTIAL = "client_credentials"; - public static final String GRANTTYPE_SAML2BEARER = "urn:ietf:params:oauth:grant-type:saml2-bearer"; - public static final String GRANTTYPE_PASSWORD = "password"; // NOSONAR - public static final String GRANTTYPE_AUTHCODE = "authorization_code"; - public static final String GRANTTYPE_USERTOKEN = "user_token"; - public static final String EXTERNAL_CONTEXT = "ext_ctx"; - - protected final Log logger = LogFactory.getLog(getClass()); - - private String xsappname = null; - private boolean foreignMode = false; - private Jwt jwt; - - /** - * @param jwt - * token - * @param xsappname - * app name - */ - protected UserInfo(Jwt jwt, String xsappname) { - this.xsappname = xsappname; - this.jwt = jwt; - } - - /** - * Get the logon name (attribute logon_name) - * - * @return user name - * @throws UserInfoException - * if method is not supported for this grant type - */ - @Override - public String getLogonName() throws UserInfoException { - if (getGrantType().equals(GRANTTYPE_CLIENTCREDENTIAL)) { - throw new UserInfoException( - "Method getLogonName is not supported for grant type " + GRANTTYPE_CLIENTCREDENTIAL); - } - return getJsonValueInternal(USER_NAME); - } - - /** - * Get the given name (attribute given_name) - * - * @return name - * @throws UserInfoException - * if method is not supported for this grant type - */ - @Override - public String getGivenName() throws UserInfoException { - if (getGrantType().equals(GRANTTYPE_CLIENTCREDENTIAL)) { - throw new UserInfoException( - "Method getGivenName is not supported for grant type " + GRANTTYPE_CLIENTCREDENTIAL); - } - return getExternalAttributeWithFallback(GIVEN_NAME); - } - - /** - * Get the family name (attribute family_name) - * - * @return family name - * @throws UserInfoException - * if method is not supported for this grant type - */ - @Override - public String getFamilyName() throws UserInfoException { - if (getGrantType().equals(GRANTTYPE_CLIENTCREDENTIAL)) { - throw new UserInfoException( - "Method getFamilyName is not supported for grant type " + GRANTTYPE_CLIENTCREDENTIAL); - } - return getExternalAttributeWithFallback(FAMILY_NAME); - } - - /** - * Get the uaa identity zone from the token - * - * @return identity zone - * @throws UserInfoException - * attribute not found - */ - public String getIdentityZone() throws UserInfoException { - return getJsonValueInternal(ZONE_ID); - } - - /** - * Get the subdomain from the token - * - * @return subdomain - * @throws UserInfoException - * attribute cannot be found or read - */ - @Override - public String getSubdomain() throws UserInfoException { - try { - return getExternalAttribute(ZDN); - } catch (Exception e) { - return null; - } - } - - /** - * Get the client id from the token - * - * @return client id - * @throws UserInfoException - * attribute cannot be found or read - */ - @Override - public String getClientId() throws UserInfoException { - return getJsonValueInternal(CID); - } - - /** - * Get the expriation date of the access token - * - * @return expiration date - * @throws UserInfoException - * attribute cannot be found or read - */ - public Date getExpirationDate() throws UserInfoException { - return Date.from(jwt.getExpiresAt()); - } - - private String getJsonValueInternal(String attribute) throws UserInfoException { - String data = jwt.getClaimAsString(attribute); - if (data == null) - throw new UserInfoException("Invalid user attribute " + attribute); - return data; - } - - /** - * Method to extract raw data from the JWT token - * - * @param attribute - * attribute name - * @return attribute value - * @throws UserInfoException - * attribute cannot be found or read - */ - @Deprecated - public String getJsonValue(String attribute) throws UserInfoException { - return getJsonValueInternal(attribute); - } - - /** - * Get email address property - * - * @return email address - * @throws UserInfoException - * method is not supported for this grant typ - */ - @Override - public String getEmail() throws UserInfoException { - if (getGrantType().equals(GRANTTYPE_CLIENTCREDENTIAL)) { - throw new UserInfoException( - "Method getEmail is not supported for grant type " + GRANTTYPE_CLIENTCREDENTIAL); - } - return getJsonValueInternal(EMAIL); - } - - /** - * Get a token for personalizing the connection to the HANA database - * - * @return token - * @throws UserInfoException - * attribute cannot be found or read - */ - @Deprecated - @Override - public String getDBToken() throws UserInfoException { - return getHdbToken(); - } - - /** - * Get a token for personalizing the connection to the HANA database - * - * @return token - * @throws UserInfoException - * attribute cannot be found or read - */ - @Override - public String getHdbToken() throws UserInfoException { - return getToken(SYSTEM, HDB); - } - - /** - * Get the application token, e.g. for token forwarding to another app - * - * @return token - */ - @Override - public String getAppToken() { - return jwt.getTokenValue(); - } - - /** - * Get a token, e.g. for forwarding to other resource servers - * - * @param namespace - * token namespace - * @param name - * attribute name - * @return token - * @throws UserInfoException - * attribute cannot be found or read - */ - @Deprecated - @Override - public String getToken(String namespace, String name) throws UserInfoException { - if (!(getGrantType().equals(GRANTTYPE_CLIENTCREDENTIAL)) && hasAttributes() && isInForeignMode()) { - throw new UserInfoException("The Spring Security Context has been initialized with an access token of a\n" - + "foreign OAuth Client Id and/or Identity Zone. Furthermore, the\n" - + "access token contains attributes. Due to the fact that we want to\n" - + "restrict attribute access to the application that provided the \n" - + "attributes, the getToken function does not return a valid token"); - } - if (!namespace.equals(SYSTEM)) { - throw new UserInfoException("Invalid namespace " + namespace); - } - if (name.equals(HDB)) { - String token = null; - if (this.jwt.getClaimAsMap(EXTERNAL_CONTEXT) != null) - token = ((net.minidev.json.JSONObject) this.jwt.getClaimAsMap(EXTERNAL_CONTEXT)) - .getAsString(HDB_NAMEDUSER_SAML); - else - token = getJsonValueInternal(HDB_NAMEDUSER_SAML); - - return token; - } else if (name.equals("JobScheduler")) { - return jwt.getTokenValue(); - } else { - throw new UserInfoException("Invalid name " + name + " for namespace " + namespace); - } - } - - /** - * Get a user attribute from the JWT token - * - * @return attribute values - * @throws UserInfoException - * attribute cannot be found or read - */ - @Override - public String[] getAttribute(String attributeName) throws UserInfoException { - if (getGrantType().equals(GRANTTYPE_CLIENTCREDENTIAL)) { - throw new UserInfoException( - "Method getAttribute is not supported for grant type " + GRANTTYPE_CLIENTCREDENTIAL); - } - return getMultiValueAttributeFromExtObject(attributeName, XS_USER_ATTRIBUTES); - } - - /** - * Check if the JWT token contains attributes - * - * @return true: attribute exists - * @throws UserInfoException - * attribute cannot be found or read - */ - @Override - public boolean hasAttributes() throws UserInfoException { - if (getGrantType().equals(GRANTTYPE_CLIENTCREDENTIAL)) { - throw new UserInfoException( - "Method hasAttributes is not supported for grant type " + GRANTTYPE_CLIENTCREDENTIAL); - } - - Map attributeData; - if (this.jwt.containsClaim(EXTERNAL_CONTEXT)) { - attributeData = (Map) this.jwt.getClaimAsMap(EXTERNAL_CONTEXT).get(XS_USER_ATTRIBUTES); - } else { - attributeData = this.jwt.getClaimAsMap(XS_USER_ATTRIBUTES); - } - if (attributeData == null) { - return false; - } else { - for (String attributeName : attributeData.keySet()) - if (((JSONArray) attributeData.get(attributeName)).size() > 0) { - return true; - } - } - return false; - } - - /** - * Get a system attribute from the JWT token - * - * @param attributeName - * attribute name - * @return attribute values - * @throws UserInfoException - * attribute cannot be found or read - */ - @Override - public String[] getSystemAttribute(String attributeName) throws UserInfoException { - return getMultiValueAttributeFromExtObject(attributeName, XS_SYSTEM_ATTRIBUTES); - } - - /** - * Check if a scope {@code .} is granted to a user - * - * @param scope - * scope name - * @return true: has scope - * @throws UserInfoException - * attribute cannot be found or read - */ - @Override - public boolean checkScope(String scope) throws UserInfoException { - List scopes = jwt.getClaimAsStringList(SCOPE); - return scopes.contains(scope); - } - - /** - * Check if a local scope is granted to a user - * - * @param scope - * scope name - * @return true: has scope - * @throws UserInfoException - * attribute cannot be found or read - */ - @Override - public boolean checkLocalScope(String scope) throws UserInfoException { - if (xsappname == null) { - throw new UserInfoException( - "Property xsappname not found in VCAP_SERVICES, must be declared in xs-security.json"); - } - return checkScope(xsappname + "." + scope); - } - - // for unit test - protected void setXSAppname(String xsappname) { - this.xsappname = xsappname; - } - - // for unit test - protected void setForeignMode(boolean foreignMode) { - this.foreignMode = foreignMode; - } - - @Override - public String getAdditionalAuthAttribute(String attributeName) throws UserInfoException { - return getAttributeFromObject(attributeName, ADDITIONAL_AZ_ATTR); - } - - @Override - public String getCloneServiceInstanceId() throws UserInfoException { - return getExternalAttribute(SERVICEINSTANCEID); - } - - @Override - public String getGrantType() throws UserInfoException { - return getJsonValueInternal(GRANT_TYPE); - } - - @Override - public boolean isInForeignMode() throws UserInfoException { - return foreignMode; - } - - private String getExternalAttribute(String attributeName) throws UserInfoException { - return getAttributeFromObject(attributeName, EXTERNAL_ATTR); - } - - private String getExternalAttributeWithFallback(String attributeName) throws UserInfoException { - try { - return getExternalAttribute(attributeName); - } catch (UserInfoException e) { - return getJsonValueInternal(attributeName); - } - } - - private String getAttributeFromObject(String attributeName, String objectName) throws UserInfoException { - Map dataMap = jwt.getClaimAsMap(objectName); - if (dataMap == null) { - throw new UserInfoException("Invalid value of " + objectName); - } - String data = (String) jwt.getClaimAsMap(objectName).get(attributeName); - if (data == null) - throw new UserInfoException("Invalid value of " + objectName); - return data; - } - - private String[] getMultiValueAttributeFromExtObject(String attributeName, String objectName) - throws UserInfoException { - String[] attributeValues = null; - if (jwt.containsClaim(EXTERNAL_CONTEXT)) { - JSONObject jsonExtern = (JSONObject) jwt.getClaimAsMap(EXTERNAL_CONTEXT); - JSONObject jsonObject = (JSONObject) jsonExtern.get(objectName); - JSONArray jsonArray = (JSONArray) jsonObject.get(attributeName); - int length = jsonArray.size(); - attributeValues = new String[length]; - for (int i = 0; i < length; i++) { - attributeValues[i] = (String) jsonArray.get(i); - } - } else { - return getMultiValueAttributeFromObject(attributeName, objectName); - } - - return attributeValues; - } - - private String[] getMultiValueAttributeFromObject(String attributeName, String objectName) - throws UserInfoException { - String[] attributeValues = new String[0]; - Map jsonObject = jwt.getClaimAsMap(objectName); - if (jsonObject == null) { - throw new UserInfoException("Invalid value of " + objectName); - } - JSONArray jsonArray = (JSONArray) jsonObject.get(attributeName); - if (jsonArray != null) { - int length = jsonArray.size(); - attributeValues = new String[length]; - for (int i = 0; i < length; i++) { - attributeValues[i] = (String) jsonArray.get(i); - } - } else { - throw new UserInfoException("Invalid value of " + objectName); - } - return attributeValues; - } - - @Override - public String getSubaccountId() throws UserInfoException { - return getIdentityZone(); - } - - @Override - public String getOrigin() throws UserInfoException { - if (getGrantType().equals(GRANTTYPE_CLIENTCREDENTIAL)) { - throw new UserInfoException( - "Method getOrigin is not supported for grant type " + GRANTTYPE_CLIENTCREDENTIAL); - } - return getJsonValueInternal(ORIGIN); - } - - @Override - public String requestToken(XSTokenRequest tokenRequest) throws UserInfoException { - if (!tokenRequest.isValid()) { - throw new UserInfoException("Invalid grant type or missing parameters for requested grant type."); - } - // build authorities string for additional authorization attributes - String authorities = null; - if (tokenRequest.getAdditionalAuthorizationAttributes() != null) { - Map azAttrMap = new HashMap<>(); - azAttrMap.put("az_attr", tokenRequest.getAdditionalAuthorizationAttributes()); - StringBuilder azStringBuilder = new StringBuilder(); - try { - JSONObject.writeJSON(azAttrMap, azStringBuilder); - } catch (IOException e) { - throw new UserInfoException("Error creating json representation", e); - } - authorities = azStringBuilder.toString(); - } - // check whether token endpoint has the correct subdomain, if not replace it - // with the subdomain of the token - String tokenSubdomain = getSubdomain(); - String tokenRequestSubdomain = getSubdomain(tokenRequest.getTokenEndpoint().toString()); - if (tokenSubdomain != null && tokenRequestSubdomain != null && !tokenSubdomain.equals(tokenRequestSubdomain)) { - tokenRequest.setTokenEndpoint(replaceSubdomain(tokenRequest.getTokenEndpoint(), tokenSubdomain)); - } - // request the token based on the type - switch (tokenRequest.getType()) { - case XSTokenRequest.TYPE_USER_TOKEN: - return requestTokenNamedUser(tokenRequest.getClientId(), tokenRequest.getClientSecret(), - tokenRequest.getTokenEndpoint().toString(), authorities); - case XSTokenRequest.TYPE_CLIENT_CREDENTIALS_TOKEN: - return requestTokenTechnicalUser(tokenRequest, authorities); - default: - throw new UserInfoException("Invalid grant type."); - } - } - - private String requestTokenTechnicalUser(XSTokenRequest tokenRequest, String authorities) throws UserInfoException { - // note: consistency checks (clientid, clientsecret and url) have already been - // executed - // build uri for client credentials flow - UriComponentsBuilder builder = UriComponentsBuilder.fromUri(tokenRequest.getTokenEndpoint()) - .queryParam("grant_type", "client_credentials"); - if (authorities != null) { - builder.queryParam("authorities", authorities); - } - // build http headers - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.ACCEPT, "application/json"); - String credentials = tokenRequest.getClientId() + ":" + tokenRequest.getClientSecret(); - String base64Creds = Base64.getEncoder().encodeToString(credentials.getBytes()); - headers.add(HttpHeaders.AUTHORIZATION, "Basic " + base64Creds); - HttpEntity entity = new HttpEntity(headers); - // request the token - RestTemplate rt = new RestTemplate(); - ResponseEntity responseEntity = rt.postForEntity(builder.build().encode().toUri(), entity, Map.class); - if (responseEntity.getStatusCode() == HttpStatus.UNAUTHORIZED) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: client_credentials). Client credentials invalid"); - } - if (responseEntity.getStatusCode() != HttpStatus.OK) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: client_credentials). HTTP status code: " - + responseEntity.getStatusCode()); - } - return responseEntity.getBody().get("access_token").toString(); - } - - private String requestTokenNamedUser(String serviceClientId, String serviceClientSecret, String serviceUaaUrl, - String authorities) throws UserInfoException { - // consistency checks - if (serviceClientId == null || serviceClientSecret == null) { - throw new UserInfoException("Invalid service credentials: Missing clientid/clientsecret."); - } - if (serviceUaaUrl == null) { - throw new UserInfoException("Invalid service credentials: Missing url."); - } - if (!checkScope("uaa.user")) { - throw new UserInfoException("JWT token does not include scope 'uaa.user'."); - } - // build uri for user token flow - UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(serviceUaaUrl) - .queryParam("grant_type", "user_token").queryParam("response_type", "token") - .queryParam("client_id", serviceClientId); - if (authorities != null) { - builder.queryParam("authorities", authorities); - } - // build http headers - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.ACCEPT, "application/json"); - headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + this.jwt.getTokenValue()); - HttpEntity entity = new HttpEntity(headers); - // request the token - RestTemplate rt = new RestTemplate(); - ResponseEntity responseEntity = rt.postForEntity(builder.build().encode().toUri(), entity, Map.class); - if (responseEntity.getStatusCode() == HttpStatus.UNAUTHORIZED) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: user_token). Bearer token invalid, requesting client does not have grant_type=user_token or no scopes were granted."); - - } - if (responseEntity.getStatusCode() != HttpStatus.OK) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: user_token). HTTP status code: " - + responseEntity.getStatusCode()); - - } - // build uri for refresh token flow - builder = UriComponentsBuilder.fromHttpUrl(serviceUaaUrl).queryParam("grant_type", "refresh_token") - .queryParam("refresh_token", responseEntity.getBody().get("refresh_token").toString()); - // build http headers - headers.clear(); - String credentials = serviceClientId + ":" + serviceClientSecret; - String base64Creds = Base64.getEncoder().encodeToString(credentials.getBytes()); - headers.add(HttpHeaders.ACCEPT, "application/json"); - headers.add(HttpHeaders.AUTHORIZATION, "Basic " + base64Creds); - entity = new HttpEntity(headers); - // request the token - responseEntity = rt.postForEntity(builder.build().encode().toUri(), entity, Map.class); - if (responseEntity.getStatusCode() == HttpStatus.UNAUTHORIZED) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: refresh_token). Client credentials invalid"); - } - if (responseEntity.getStatusCode() != HttpStatus.OK) { - throw new UserInfoException( - "Call to /oauth/token was not successful (grant_type: refresh_token). HTTP status code: " - + responseEntity.getStatusCode()); - } - return responseEntity.getBody().get("access_token").toString(); - } - - @Deprecated - public String requestTokenForClient(String serviceClientId, String serviceClientSecret, String serviceUaaUrl) - throws UserInfoException { - String url = serviceUaaUrl != null ? serviceUaaUrl + "/oauth/token" : null; - return requestTokenNamedUser(serviceClientId, serviceClientSecret, url, null); - } - - /** - * Get the subdomain from the given url - * - * @return - * @throws UserInfoException - */ - private String getSubdomain(String url) { - String host = null; - try { - host = new URI(url).getHost(); - } catch (URISyntaxException e) { - return null; - } - if (host == null || !host.contains(".")) { - return null; - } - return host.split("\\.")[0]; - } - - /** - * Replace the subdomain in the given uri with the given subdomain - * - * @return - * @throws UserInfoException - */ - private URI replaceSubdomain(URI uri, String subdomain) { - if (uri == null || subdomain == null || !uri.getHost().contains(".")) { - return null; - } - UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme(uri.getScheme()) - .host(subdomain + uri.getHost().substring(uri.getHost().indexOf("."))).port(uri.getPort()) - .path(uri.getPath()); - return uri.resolve(builder.build().toString()); - } - -} diff --git a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfoException.java b/spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfoException.java deleted file mode 100644 index 66d13bbbeb..0000000000 --- a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfoException.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.sap.xs2.security.container; - -import com.sap.xsa.security.container.XSUserInfoException; - -/** - * @deprecated will be removed with version 2.0 - */ -@Deprecated -public class UserInfoException extends XSUserInfoException { - - private static final long serialVersionUID = 1L; - - public UserInfoException(String message) { - super(message); - } - - public UserInfoException(String message, Throwable reason) { - super(message, reason); - } - -} \ No newline at end of file diff --git a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/XSTokenRequestImpl.java b/spring-xsuaa/src/main/java/com/sap/xs2/security/container/XSTokenRequestImpl.java deleted file mode 100644 index 6ca2ea050f..0000000000 --- a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/XSTokenRequestImpl.java +++ /dev/null @@ -1,207 +0,0 @@ -/** - * Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved. - * This file is licensed under the Apache Software License, - * v. 2 except as noted otherwise in the LICENSE file - * https://github.com/SAP/cloud-security-xsuaa-integration/blob/master/LICENSE - */ -package com.sap.xs2.security.container; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import com.sap.xsa.security.container.XSTokenRequest; -import org.springframework.web.client.RestTemplate; - -/** - * @deprecated in favor of XsuaaTokenFlows API. Will be removed with version - * 2.0. - */ -@Deprecated -public class XSTokenRequestImpl implements XSTokenRequest { - - @Deprecated - public static final int TYPE_USER_TOKEN = 0; - @Deprecated - public static final int TYPE_CLIENT_CREDENTIALS_TOKEN = 1; - - private URI tokenEndpoint; - - private int type; - private String clientId; - private String clientSecret; - private RestTemplate restTemplate; - - private Map additionalAuthorizationAttributes; - - /** - * Constructs a token request - * - * @param uaabaseUrl - * - uaa base url - * @throws URISyntaxException - * when uaabaseUrl could not be parsed as URI - */ - public XSTokenRequestImpl(String uaabaseUrl) throws URISyntaxException { - URI uaaBaseURI = URI.create(uaabaseUrl); - this.tokenEndpoint = new URI(uaabaseUrl + "/oauth/token"); - } - - /** - * Returns true if this object contains enough information to retrieve a token - * - * @return true if this object contains enough information to retrieve a token - */ - public boolean isValid() { - return !this.hasAnyNullValues(Arrays.asList(this.tokenEndpoint, this.clientId, this.clientSecret)); - } - - /** - * Returns the client ID, if set, that will be used to authenticate the client - * - * @return the client ID if set - */ - public String getClientId() { - return this.clientId; - } - - /** - * Sets the client ID to be used for authentication during the token request - * - * @param clientId - * a string, no more than 255 characters identifying a valid client - * on the UAA - * @return this mutable object - */ - public XSTokenRequest setClientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Returns the client secret, if set, that will be used to authenticate the - * client - * - * @return the client secret if set - */ - public String getClientSecret() { - return this.clientSecret; - } - - /** - * Sets the client secret to be used for authentication during the token request - * - * @param clientSecret - * a string representing the password for a valid client on the UAA - * @return this mutable object - */ - public XSTokenRequest setClientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Returns the list of requested additional authorization attributes, or null if - * no additional authorization attributes have been requested. - * - * @return the list of requested additional authorization attributes, or null if - * no additional authorization attributes have been requested. - */ - public Map getAdditionalAuthorizationAttributes() { - return additionalAuthorizationAttributes; - } - - /** - * Sets the requested additional authorization attributes list for this token - * request. Use this if you would like to add additional authorization - * attributes to the access token - * - * @param additionalAuthorizationAttributes - * a set of strings representing requested additional authorization - * attributes - * @return this mutable object - */ - public XSTokenRequest setAdditionalAuthorizationAttributes(Map additionalAuthorizationAttributes) { - this.additionalAuthorizationAttributes = (additionalAuthorizationAttributes == null) ? null - : new HashMap<>(additionalAuthorizationAttributes); - return this; - } - - /** - * Returns the type of the requested token - * - * @return the type of the requested token - */ - public int getType() { - return this.type; - } - - /** - * Set the requested token type - * - * @param type - * an integer representing the grant type - * @return this mutable object - */ - public XSTokenRequest setType(int type) { - this.type = type; - return this; - } - - /** - * @return the token endpoint URI, for example - * http://localhost:8080/uaa/oauth/token - */ - public URI getTokenEndpoint() { - return this.tokenEndpoint; - } - - /** - * Set the token endpoint URI - * - * @param tokenEndpoint - * an URI representing the token endpoint of the UAA - * @return this mutable object - */ - public XSTokenRequest setTokenEndpoint(URI tokenEndpoint) { - this.tokenEndpoint = tokenEndpoint; - return this; - } - - /** - * Returns true if the list or any item in the list is null - * - * @param objects - * a list of items to be evaluated for null references - * @return true if the list or any item in the list is null - */ - private boolean hasAnyNullValues(List objects) { - if (Objects.isNull(objects)) { - return true; - } - return objects.stream().filter(o -> Objects.isNull(o)).count() > 0; - } - - /** - * Allows to overwrite the default RestTemplate - * - * @param restTemplate - * the custom restTemplate - */ - public void setRestTemplate(RestTemplate restTemplate) { - this.restTemplate = restTemplate; - } - - /** - * Returns the custom RestTemplate - * - * @return the custom restTemplate or null - */ - public RestTemplate getRestTemplate() { - return restTemplate; - } -} diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java index a57a5f70c1..9eaf793be6 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java @@ -29,7 +29,6 @@ import com.nimbusds.jwt.JWTClaimsSet; import com.sap.cloud.security.xsuaa.test.JwtGenerator; -import com.sap.xs2.security.container.XSTokenRequestImpl; import com.sap.xsa.security.container.XSTokenRequest; public class XsuaaTokenTest { @@ -250,70 +249,6 @@ public void getAppToken() { assertThat(token.getAppToken(), startsWith("eyJhbGciOiJSUzI1NiIsInR5")); } - @Test - @Deprecated - public void requestClientCredentialsToken() throws URISyntaxException { - // prepare response - Map ccToken = new HashMap<>(); - Jwt mockJwt = buildMockJwt(); - ccToken.put(OAuth2TokenServiceConstants.ACCESS_TOKEN, mockJwt.getTokenValue()); - ccToken.put(OAuth2TokenServiceConstants.EXPIRES_IN, 43199); - - // mock rest call - // http://myuaa.com/oauth/token?grant_type=client_credentials&authorities=%7B%22az_attr%22:%7B%22a%22:%22b%22,%22c%22:%22d%22%7D%7D - RestTemplate mockRestTemplate = Mockito.mock(RestTemplate.class); - ResponseEntity response = new ResponseEntity<>(ccToken, HttpStatus.OK); - Mockito.when(mockRestTemplate.postForEntity(any(URI.class), any(HttpEntity.class), eq(Map.class))) - .thenReturn(response); - - token = createToken(claimsSetBuilder); - - String mockServerUrl = "http://myuaa.com"; - XSTokenRequestImpl tokenRequest = new XSTokenRequestImpl(mockServerUrl); - tokenRequest.setRestTemplate(mockRestTemplate); - tokenRequest.setClientId("c1").setClientSecret("s1").setType(XSTokenRequest.TYPE_CLIENT_CREDENTIALS_TOKEN); - - Map azMape = new HashMap<>(); - azMape.put("a", "b"); - azMape.put("c", "d"); - tokenRequest.setAdditionalAuthorizationAttributes(azMape); - - assertThat(token.requestToken(tokenRequest), is("mock.jwt.value")); - } - - @Test - @Deprecated - public void requestUserToken() throws URISyntaxException { - // prepare response - Map userToken = new HashMap<>(); - Jwt mockJwt = buildMockJwt(); - userToken.put(OAuth2TokenServiceConstants.ACCESS_TOKEN, mockJwt.getTokenValue()); - userToken.put(OAuth2TokenServiceConstants.EXPIRES_IN, 43199); - userToken.put(OAuth2TokenServiceConstants.REFRESH_TOKEN, "a07356ec2e5449329ab6dd6728623bda"); - - // mock rest call - // http://myuaa.com/oauth/token?grant_type=client_credentials&authorities=%7B%22az_attr%22:%7B%22a%22:%22b%22,%22c%22:%22d%22%7D%7D - RestTemplate mockRestTemplate = Mockito.mock(RestTemplate.class); - ResponseEntity response = new ResponseEntity<>(userToken, HttpStatus.OK); - Mockito.when(mockRestTemplate.postForEntity(any(URI.class), any(HttpEntity.class), eq(Map.class))) - .thenReturn(response); - - claimsSetBuilder.claim(TokenClaims.CLAIM_SCOPES, new String[] { "uaa.user" }); - token = createToken(claimsSetBuilder); - - String mockServerUrl = "http://myuaa.com"; - XSTokenRequestImpl tokenRequest = new XSTokenRequestImpl(mockServerUrl); - tokenRequest.setRestTemplate(mockRestTemplate); - tokenRequest.setClientId("c1").setClientSecret("s1").setType(XSTokenRequest.TYPE_USER_TOKEN); - - Map azMape = new HashMap<>(); - azMape.put("a", "b"); - azMape.put("c", "d"); - tokenRequest.setAdditionalAuthorizationAttributes(azMape); - - assertThat(token.requestToken(tokenRequest), is("mock.jwt.value")); - } - private Jwt buildMockJwt() { Map jwtHeaders = new HashMap(); jwtHeaders.put("dummyHeader", "dummyHeaderValue"); diff --git a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestClientCredentials.java b/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestClientCredentials.java deleted file mode 100644 index 8b559bf94b..0000000000 --- a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestClientCredentials.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.sap.xs2.security.container; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.sap.xsa.security.container.XSUserInfoException; - -public class UserInfoTestClientCredentials { - - private UserInfo infoCc = null; - private UserInfo infoCcNoAttr = null; - - @Before - public void setup() throws Exception { - infoCc = UserInfoTestUtil.createFromJwtFile("/token_cc.txt", "java-hello-world"); - infoCcNoAttr = UserInfoTestUtil.createFromJwtFile("/token_cc_noattr.txt", "java-hello-world"); - } - - @Test(expected = UserInfoException.class) - public void getLogonNameCc() throws XSUserInfoException { - infoCc.getLogonName(); - } - - @Test(expected = UserInfoException.class) - public void getEmailCc() throws XSUserInfoException { - infoCc.getEmail(); - } - - @Test(expected = UserInfoException.class) - public void getGivenNameCc() throws XSUserInfoException { - infoCc.getGivenName(); - } - - @Test(expected = UserInfoException.class) - public void getFamilyNameCc() throws XSUserInfoException { - infoCc.getFamilyName(); - } - - @Test(expected = UserInfoException.class) - public void getOriginCc() throws XSUserInfoException { - Assert.assertNull(infoCc.getOrigin()); // not in samlUserInfo - } - - @Test(expected = UserInfoException.class) - public void getAttributeCc() throws XSUserInfoException { - infoCc.getAttribute("cost center"); - } - - @Test(expected = UserInfoException.class) - public void getAttributeCcNotExisting() throws XSUserInfoException { - infoCcNoAttr.getAttribute("cost center"); - } - - @Test(expected = XSUserInfoException.class) - public void hasAttributesCc() throws XSUserInfoException { - infoCc.hasAttributes(); - } - - @Test - public void getTokenCc() throws XSUserInfoException { - Assert.assertNotNull("Token must not be null", infoCc.getHdbToken()); - Assert.assertTrue(!infoCc.getHdbToken().isEmpty()); - Assert.assertTrue(infoCc.getHdbToken().equals(infoCc.getToken("SYSTEM", "HDB"))); - Assert.assertTrue(!infoCc.getAppToken().isEmpty()); - Assert.assertTrue(infoCc.getAppToken().equals(infoCc.getToken("SYSTEM", "JobScheduler"))); - } -} diff --git a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestEndUser.java b/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestEndUser.java deleted file mode 100644 index 5367b1e8ee..0000000000 --- a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestEndUser.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.sap.xs2.security.container; - -import org.junit.Before; -import org.junit.Test; - -import com.sap.xsa.security.container.XSUserInfoException; - -public class UserInfoTestEndUser { - - private UserInfo correctEnduserInfo = null; - private UserInfo correctEnduserInfoWithUaaUser = null; - - @Before - public void setup() throws Exception { - correctEnduserInfo = UserInfoTestUtil.createFromJwtFile("/correctEndUserToken.txt", "java-hello-world"); - correctEnduserInfoWithUaaUser = UserInfoTestUtil.createFromJwtFile("/correctEndUserTokenUaaUser.txt", - "java-hello-world"); - } - - @Test(expected = UserInfoException.class) - public void requestTokenForClientTestNoUaaUserScope() throws XSUserInfoException { - correctEnduserInfo.requestTokenForClient("foo", "bar", "foobar"); - } - - @Test(expected = UserInfoException.class) - public void requestTokenForClientTestInvalidClientId() throws XSUserInfoException { - correctEnduserInfoWithUaaUser.requestTokenForClient(null, "foo", "bar"); - } - - @Test(expected = UserInfoException.class) - public void requestTokenForClientTestInvalidClientSecret() throws XSUserInfoException { - correctEnduserInfoWithUaaUser.requestTokenForClient("foo", null, "bar"); - } - - @Test(expected = UserInfoException.class) - public void requestTokenForClientTestInvalidUaaUrl() throws XSUserInfoException { - correctEnduserInfoWithUaaUser.requestTokenForClient("foo", "bar", null); - } -} diff --git a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestSAML.java b/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestSAML.java deleted file mode 100644 index c7417bb5df..0000000000 --- a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestSAML.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.sap.xs2.security.container; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class UserInfoTestSAML { - - UserInfo samlUserInfo = null; - - @Before - public void setup() throws Exception { - samlUserInfo = UserInfoTestUtil.createFromTemplate("/saml.txt", "java-hello-world"); - } - - @Test - public void testSAMLToken() throws Exception { - // attributes - old style - Assert.assertEquals(2, samlUserInfo.getAttribute("cost-center").length); - Assert.assertEquals("0815", samlUserInfo.getAttribute("cost-center")[0]); - Assert.assertEquals("4711", samlUserInfo.getAttribute("cost-center")[1]); - Assert.assertEquals(1, samlUserInfo.getAttribute("country").length); - Assert.assertEquals("Germany", samlUserInfo.getAttribute("country")[0]); - - // scopes - Assert.assertEquals(true, samlUserInfo.checkLocalScope("Display")); - Assert.assertEquals(true, samlUserInfo.checkLocalScope("Create")); - Assert.assertEquals(true, samlUserInfo.checkLocalScope("Delete")); - Assert.assertEquals(false, samlUserInfo.checkLocalScope("Other")); - // client id - Assert.assertEquals("sb-java-hello-world", samlUserInfo.getClientId()); - // grant type - Assert.assertEquals("authorization_code", samlUserInfo.getGrantType()); - - // logon name - Assert.assertEquals("Mustermann", samlUserInfo.getLogonName()); - // email - Assert.assertEquals("max@example.com", samlUserInfo.getEmail()); - // zone - Assert.assertEquals("11-22-33", samlUserInfo.getIdentityZone()); - Assert.assertEquals("11-22-33", samlUserInfo.getSubaccountId()); - // embedded SAML - Assert.assertNotNull(samlUserInfo.getHdbToken()); - // ext attr - Assert.assertEquals("domain\\group1", samlUserInfo.getAdditionalAuthAttribute("external_group")); - Assert.assertEquals("abcd1234", samlUserInfo.getAdditionalAuthAttribute("external_id")); - // subdomain - Assert.assertEquals("testsubdomain", samlUserInfo.getSubdomain()); - // service instance id - Assert.assertEquals("abcd1234", samlUserInfo.getCloneServiceInstanceId()); - // groups - Assert.assertEquals(1, samlUserInfo.getSystemAttribute("xs.saml.groups").length); - Assert.assertEquals("g1", samlUserInfo.getSystemAttribute("xs.saml.groups")[0]); - // role collections - Assert.assertEquals(1, samlUserInfo.getSystemAttribute("xs.rolecollections").length); - Assert.assertEquals("rc1", samlUserInfo.getSystemAttribute("xs.rolecollections")[0]); - } -} diff --git a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestUser.java b/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestUser.java deleted file mode 100644 index c5819ae695..0000000000 --- a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestUser.java +++ /dev/null @@ -1,186 +0,0 @@ -package com.sap.xs2.security.container; - -import java.util.Calendar; -import java.util.TimeZone; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import com.sap.xsa.security.container.XSUserInfoException; - -import net.minidev.json.parser.ParseException; - -public class UserInfoTestUser { - - private UserInfo infoUser = null; - private UserInfo infoUserNoAttr = null; - - @Before - public void setup() throws Exception { - infoUser = UserInfoTestUtil.createFromJwtFile("/token_user.txt", "java-hello-world"); - infoUserNoAttr = UserInfoTestUtil.createFromJwtFile("/token_user_noattr.txt", "java-hello-world"); - } - - @Test - public void testSAMLToken() throws Exception { - - UserInfo token = UserInfoTestUtil.createFromTemplate("/saml.txt", "java-hello-world"); - // attributes - old style - Assert.assertEquals(2, token.getAttribute("cost-center").length); - Assert.assertEquals("0815", token.getAttribute("cost-center")[0]); - Assert.assertEquals("4711", token.getAttribute("cost-center")[1]); - Assert.assertEquals(1, token.getAttribute("country").length); - Assert.assertEquals("Germany", token.getAttribute("country")[0]); - - // scopes - Assert.assertEquals(true, token.checkLocalScope("Display")); - Assert.assertEquals(true, token.checkLocalScope("Create")); - Assert.assertEquals(true, token.checkLocalScope("Delete")); - Assert.assertEquals(false, token.checkLocalScope("Other")); - // client id - Assert.assertEquals("sb-java-hello-world", token.getClientId()); - // grant type - Assert.assertEquals("authorization_code", token.getGrantType()); - - // logon name - Assert.assertEquals("Mustermann", token.getLogonName()); - // email - Assert.assertEquals("max@example.com", token.getEmail()); - // zone - Assert.assertEquals("11-22-33", token.getIdentityZone()); - Assert.assertEquals("11-22-33", token.getSubaccountId()); - // embedded SAML - Assert.assertNotNull(token.getHdbToken()); - // ext attr - Assert.assertEquals("domain\\group1", token.getAdditionalAuthAttribute("external_group")); - Assert.assertEquals("abcd1234", token.getAdditionalAuthAttribute("external_id")); - // subdomain - Assert.assertEquals("testsubdomain", token.getSubdomain()); - // service instance id - Assert.assertEquals("abcd1234", token.getCloneServiceInstanceId()); - // groups - Assert.assertEquals(1, token.getSystemAttribute("xs.saml.groups").length); - Assert.assertEquals("g1", token.getSystemAttribute("xs.saml.groups")[0]); - // role collections - Assert.assertEquals(1, token.getSystemAttribute("xs.rolecollections").length); - Assert.assertEquals("rc1", token.getSystemAttribute("xs.rolecollections")[0]); - } - - @Test - public void getLogonName() throws XSUserInfoException { - Assert.assertEquals("WOLFGANG", infoUser.getLogonName()); - } - - @Test - public void getEmail() throws XSUserInfoException { - Assert.assertEquals("WOLFGANG@unknown", infoUser.getEmail()); - } - - @Test(expected = UserInfoException.class) - public void getGivenName() throws XSUserInfoException { - infoUser.getGivenName(); // not in samlUserInfo - } - - @Test(expected = UserInfoException.class) - public void getFamilyName() throws XSUserInfoException { - infoUser.getFamilyName(); // not in samlUserInfo - } - - @Test - public void getOrigin() throws XSUserInfoException { - Assert.assertEquals("useridp", infoUser.getOrigin()); - } - - @Ignore - @Test - public void getJwtAlg() throws XSUserInfoException { - // Assert.assertEquals(1, infoUser.getJwtAlgId()); - } - - @Test - public void getIdentityZone() throws UserInfoException { - Assert.assertEquals("uaa", infoUser.getIdentityZone()); - Assert.assertEquals("uaa", infoUser.getSubaccountId()); - } - - @Test - public void getClientId() throws UserInfoException { - Assert.assertEquals("sb-java-hello-world", infoUser.getClientId()); - } - - @Test - public void getExpirationDate() throws UserInfoException { - // Expected date: Tue Sep 22 22:55:22 CEST 2015 - Calendar calendar = Calendar.getInstance(); - calendar.setTime(infoUser.getExpirationDate()); - calendar.setTimeZone(TimeZone.getTimeZone("UTC")); - Assert.assertEquals("Year", 2015, calendar.get(Calendar.YEAR)); - Assert.assertEquals("Month", Calendar.SEPTEMBER, calendar.get(Calendar.MONTH)); - Assert.assertEquals("Day", 22, calendar.get(Calendar.DAY_OF_MONTH)); - Assert.assertEquals("Hours", 20, calendar.get(Calendar.HOUR_OF_DAY)); - Assert.assertEquals("Minutes", 55, calendar.get(Calendar.MINUTE)); - Assert.assertEquals("Seconds", 22, calendar.get(Calendar.SECOND)); - } - - @Test - public void checkScope() throws UserInfoException { - Assert.assertEquals(false, infoUser.checkScope("cloud_controller.read")); - Assert.assertEquals(true, infoUser.checkScope("java-hello-world.Display")); - } - - @Test - public void checkLocalScopeXsappname() throws UserInfoException { - infoUser.setXSAppname("cloud_controller"); - Assert.assertEquals(false, infoUser.checkLocalScope("read")); - infoUser.setXSAppname("java-hello-world"); - Assert.assertEquals(true, infoUser.checkLocalScope("Display")); - } - - @Test - public void getAttribute() throws XSUserInfoException { - String[] cost_center = infoUser.getAttribute("cost center"); - Assert.assertEquals(2, cost_center.length); - Assert.assertEquals("0815", cost_center[0]); - Assert.assertEquals("4711", cost_center[1]); - String[] country = infoUser.getAttribute("country"); - Assert.assertEquals(1, country.length); - Assert.assertEquals("Germany", country[0]); - } - - @Test - public void testServiceInstanceId() throws XSUserInfoException { - Assert.assertEquals("abcd1234", infoUser.getCloneServiceInstanceId()); - } - - @Test - public void testAdditionalAuthAttr() throws XSUserInfoException { - Assert.assertEquals("abcd1234", infoUser.getAdditionalAuthAttribute("external_id")); - } - - @Test - public void getToken() throws XSUserInfoException { - Assert.assertNotNull("Token must not be null", infoUser.getHdbToken()); - Assert.assertTrue(!infoUser.getHdbToken().isEmpty()); - Assert.assertTrue(infoUser.getHdbToken().equals(infoUser.getToken("SYSTEM", "HDB"))); - Assert.assertTrue(!infoUser.getAppToken().isEmpty()); - Assert.assertTrue(infoUser.getAppToken().equals(infoUser.getToken("SYSTEM", "JobScheduler"))); - } - - @Test(expected = UserInfoException.class) - public void getAttributeNotExisting() throws XSUserInfoException { - infoUserNoAttr.getAttribute("cost center"); - } - - @Test(expected = XSUserInfoException.class) - public void testFailServiceInstanceIdNoId() throws XSUserInfoException { - Assert.assertEquals("abcd1234", infoUserNoAttr.getCloneServiceInstanceId()); - } - - @Test(expected = XSUserInfoException.class) - public void testFailAdditionalAuthAttrNoAttr() throws XSUserInfoException { - Assert.assertEquals("abcd1234", infoUserNoAttr.getAdditionalAuthAttribute("external_id")); - } - -} diff --git a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestUtil.java b/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestUtil.java deleted file mode 100644 index 4bb3fed103..0000000000 --- a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/UserInfoTestUtil.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.sap.xs2.security.container; - -import java.io.IOException; -import java.nio.charset.Charset; - -import org.apache.commons.io.IOUtils; -import org.springframework.security.oauth2.jwt.Jwt; - -import com.sap.cloud.security.xsuaa.test.JwtGenerator; - -public class UserInfoTestUtil { - - public static UserInfo createFromTemplate(String pathToTemplate, String appName) throws IOException { - Jwt jwt = new JwtGenerator().createFromTemplate(pathToTemplate); - return new UserInfo(jwt, appName); - } - - public static UserInfo createFromJwtFile(String pathToJwt, String appName) throws IOException { - String token = IOUtils.resourceToString(pathToJwt, Charset.forName("UTF-8")); - Jwt jwt = JwtGenerator.convertTokenToOAuthJwt(token); - return new UserInfo(jwt, appName); - } -} \ No newline at end of file diff --git a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/XSTokenRequestImplTest.java b/spring-xsuaa/src/test/java/com/sap/xs2/security/container/XSTokenRequestImplTest.java deleted file mode 100644 index 3999ca73e3..0000000000 --- a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/XSTokenRequestImplTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.sap.xs2.security.container; - -import static org.junit.Assert.*; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; - -import org.junit.Before; -import org.junit.Test; - -import com.sap.xsa.security.container.XSTokenRequest; - -public class XSTokenRequestImplTest { - - private XSTokenRequest request; - - @Before - public void setUp() throws Exception { - request = new XSTokenRequestImpl("http://localhost:8080/uaa/oauth/samlUserInfo"); - } - - @Test - public void test_setTokenEndpoint() throws URISyntaxException { - URI tokenEndpoint = new URI("http://localhost:8080/uaa/oauth/samlUserInfo"); - request.setTokenEndpoint(tokenEndpoint); - assertEquals(request.getTokenEndpoint().toString(), "http://localhost:8080/uaa/oauth/samlUserInfo"); - } - - @Test(expected = URISyntaxException.class) - public void test_setTokenEndpoint_fails_UriSyntax() throws URISyntaxException { - URI tokenEndpoint = new URI("PC_DEV2::localhost:8080/uaa/oauth/samlUserInfo"); - request.setTokenEndpoint(tokenEndpoint); - } - - @Test - public void test_is_user_token_grant_valid() throws Exception { - assertFalse(request.isValid()); - assertFalse(request.setType(XSTokenRequest.TYPE_USER_TOKEN).isValid()); - assertFalse(request.setClientId("client_id").isValid()); - assertTrue(request.setClientSecret("client_secret").isValid()); - } - - @Test - public void test_is_client_credentials_grant_valid() throws Exception { - assertFalse(request.isValid()); - assertFalse(request.setType(XSTokenRequest.TYPE_CLIENT_CREDENTIALS_TOKEN).isValid()); - assertFalse(request.setClientId("client_id").isValid()); - assertTrue(request.setClientSecret("client_secret").isValid()); - } - - @Test - public void test_additional_authorization_attributes() throws Exception { - HashMap map = new HashMap<>(); - map.put("foo", "bar"); - map.put("key", "value"); - request.setAdditionalAuthorizationAttributes(map); - assertEquals("bar", request.getAdditionalAuthorizationAttributes().get("foo")); - assertEquals("value", request.getAdditionalAuthorizationAttributes().get("key")); - assertFalse(request.getAdditionalAuthorizationAttributes().containsKey("hugo")); - } - - @Test - public void test_additional_authorization_attributes_null() throws Exception { - request.setAdditionalAuthorizationAttributes(null); - assertNull(request.getAdditionalAuthorizationAttributes()); - HashMap map = new HashMap<>(); - request.setAdditionalAuthorizationAttributes(map); - assertNull(request.getAdditionalAuthorizationAttributes().get(null)); - assertNotNull(request.getAdditionalAuthorizationAttributes()); - /* - * assertEquals("bar", - * request.getAdditionalAuthorizationAttributes().get("foo")); - * assertEquals("value", - * request.getAdditionalAuthorizationAttributes().get("key")); - * assertFalse(request.getAdditionalAuthorizationAttributes().containsKey("hugo" - * )); - */ - } -} diff --git a/token-client/README.md b/token-client/README.md index b0ea02fbc1..a4e7c51b02 100644 --- a/token-client/README.md +++ b/token-client/README.md @@ -2,7 +2,7 @@ ## Motivation -This library serves as slim client for some XSUAA `/oauth/token` token endpoints as specified [here](https://docs.cloudfoundry.org/api/uaa/version/4.30.0/index.html#token). +This library serves as slim client for some XSUAA `/oauth/token` token endpoints as specified [here](https://docs.cloudfoundry.org/api/uaa/version/74.1.0/index.html#token). Furthermore it introduces a new API to support the following token flows: * **User Token Flow**. From 0246a60d53cb0fffb2cf23c9b7071023f19cd39e Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Mon, 9 Sep 2019 16:43:44 +0200 Subject: [PATCH 02/22] get rid of XsuaaServiceConfiguration.getTokenUrl() --- .../BasicCredentialExtractorTest.java | 4 +- .../XsuaaServiceConfigurationDummy.java | 5 -- .../XsuaaServiceConfigurationTest.java | 5 -- .../xsuaa/XsuaaServiceConfiguration.java | 15 ------ .../XsuaaServiceConfigurationDefault.java | 10 ---- .../sap/cloud/security/xsuaa/token/Token.java | 20 -------- .../security/xsuaa/token/XsuaaToken.java | 12 ----- .../ReactiveXsuaaJwtDecoder.java | 48 ++++++++++--------- .../xsuaa/DummyXsuaaServiceConfiguration.java | 5 -- 9 files changed, 28 insertions(+), 96 deletions(-) diff --git a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java index 5ef04843ff..d28e142dbd 100644 --- a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java +++ b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java @@ -21,6 +21,8 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; + @RunWith(SpringRunner.class) @ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = { XsuaaServiceConfigurationDummy.class, TokenBrokerTestConfiguration.class }) @@ -134,7 +136,7 @@ public void testCombinedCredentials_shouldTakeBasicAsFallback() { assertThat(token).isEqualTo("token_pwd"); } - private XsuaaServiceConfigurationDummy getXsuaaServiceConfiguration() { + private XsuaaServiceConfiguration getXsuaaServiceConfiguration() { XsuaaServiceConfigurationDummy cfg = new XsuaaServiceConfigurationDummy(); cfg.appId = "a1!123"; cfg.clientId = "myclient!t1"; diff --git a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/XsuaaServiceConfigurationDummy.java b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/XsuaaServiceConfigurationDummy.java index 82f84c495b..d4dc72ba69 100644 --- a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/XsuaaServiceConfigurationDummy.java +++ b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/XsuaaServiceConfigurationDummy.java @@ -25,11 +25,6 @@ public String getUaaUrl() { return uaaUrl; } - @Override - public String getTokenKeyUrl(String identityZoneId, String subdomain) { - return null; - } - @Override public String getAppId() { return appId; diff --git a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaServiceConfigurationTest.java b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaServiceConfigurationTest.java index 2ec502b8f9..a4c732b60e 100644 --- a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaServiceConfigurationTest.java +++ b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaServiceConfigurationTest.java @@ -30,11 +30,6 @@ public void testInjectedPropertyValue() { Assert.assertEquals("https://auth.com", exampleBean.serviceConfiguration.getUaaUrl()); Assert.assertEquals("xs2.usertoken", exampleBean.serviceConfiguration.getClientId()); Assert.assertEquals("secret", exampleBean.serviceConfiguration.getClientSecret()); - Assert.assertEquals("https://auth.com/token_keys", - exampleBean.serviceConfiguration.getTokenKeyUrl("uaa", null)); - Assert.assertEquals("https://myhost.auth.com/token_keys", - exampleBean.serviceConfiguration.getTokenKeyUrl("zone", "myhost")); - Assert.assertEquals("java-hello-world", exampleBean.serviceConfiguration.getAppId()); Assert.assertEquals("auth.com", exampleBean.serviceConfiguration.getUaaDomain()); } diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfiguration.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfiguration.java index a35be47dd8..5ce8e3f433 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfiguration.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfiguration.java @@ -23,21 +23,6 @@ public interface XsuaaServiceConfiguration { */ String getUaaUrl(); - /** - * Url to the token_keys endpoint - * - * @param identityZoneId - * Identity Zone Id (subaccount id) - * @param subdomain - * of the subaccount - * @return token key endpoint - * - * @deprecated Xsuaa version 2.8.0 provides jwt token key url (jku) as part of - * the JWT. This method gets deleted with version 2.0.0. - */ - @Deprecated - String getTokenKeyUrl(String identityZoneId, String subdomain); - /** * XS application identifier * diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefault.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefault.java index 4e9bf3325d..cb36d79c76 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefault.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefault.java @@ -37,16 +37,6 @@ public String getClientId() { return clientId; } - @Override - public String getTokenKeyUrl(String identityZoneId, String subdomain) { - // Please note this method is deprecated and will be deleted with version 2.0 - if ("uaa".equals(identityZoneId)) { - return getUaaUrl() + "/token_keys"; - } else { - return String.format("https://%s.%s/token_keys", subdomain, uaadomain); - } - } - @Override public String getClientSecret() { return clientSecret; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java index 22eb5891fa..bc8fa0ca40 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java @@ -1,33 +1,13 @@ package com.sap.cloud.security.xsuaa.token; -import com.sap.xsa.security.container.XSTokenRequest; import org.springframework.lang.Nullable; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; -import java.net.URISyntaxException; import java.util.Collection; import java.util.Date; public interface Token extends UserDetails { - /** - * @deprecated use instead @link{TokenClaims.CLAIM_XS_USER_ATTRIBUTES} - * @since 2.0.0 - */ - @Deprecated - String CLAIM_XS_USER_ATTRIBUTES = "xs.user.attributes"; - /** - * @deprecated use instead @link{TokenClaims.CLAIM_SCOPES} - * @since 2.0.0 - */ - @Deprecated - String CLAIM_SCOPES = "scope"; - /** - * @deprecated use instead @link{TokenClaims.CLAIM_CLIENT_ID} - * @since 2.0.0 - */ - @Deprecated - String CLIENT_ID = "cid"; String GRANTTYPE_CLIENTCREDENTIAL = "client_credentials"; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java index dfc118e3e0..ea0c57859d 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java @@ -243,18 +243,6 @@ public boolean hasClaim(String claim) { return containsClaim(claim); } - /** - * For custom access to the claims of the authentication token. - * - * @return this - * @deprecated with version 1.5 as XsuaaToken inherits from {@link Jwt} which - * implements {@link JwtClaimAccessor} - */ - @Deprecated - ClaimAccessor getClaimAccessor() { - return this; - } - void setAuthorities(Collection authorities) { Assert.notNull(authorities, "authorities are required"); this.authorities = authorities; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/ReactiveXsuaaJwtDecoder.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/ReactiveXsuaaJwtDecoder.java index 5d78feb1cd..1d317c5684 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/ReactiveXsuaaJwtDecoder.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/ReactiveXsuaaJwtDecoder.java @@ -1,5 +1,8 @@ package com.sap.cloud.security.xsuaa.token.authentication; +import static com.sap.cloud.security.xsuaa.token.TokenClaims.CLAIM_JKU; +import static com.sap.cloud.security.xsuaa.token.TokenClaims.CLAIM_KID; + import java.text.ParseException; import java.util.*; import java.util.concurrent.TimeUnit; @@ -8,7 +11,6 @@ import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtException; -import org.springframework.security.oauth2.jwt.JwtTimestampValidator; import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder; import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; @@ -18,15 +20,14 @@ import com.nimbusds.jwt.JWTParser; import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; -import net.minidev.json.JSONObject; import reactor.core.publisher.Mono; public class ReactiveXsuaaJwtDecoder implements ReactiveJwtDecoder { Cache cache; - private final XsuaaServiceConfiguration xsuaaServiceConfiguration; private List> tokenValidators = new ArrayList<>(); private Collection postValidationActions; + private TokenInfoExtractor tokenInfoExtractor; private static final String EXT_ATTR = "ext_attr"; private static final String ZDN = "zdn"; @@ -39,7 +40,23 @@ public class ReactiveXsuaaJwtDecoder implements ReactiveJwtDecoder { OAuth2TokenValidator tokenValidators, Collection postValidationActions) { cache = Caffeine.newBuilder().expireAfterWrite(cacheValidityInSeconds, TimeUnit.SECONDS).maximumSize(cacheSize) .build(); - this.xsuaaServiceConfiguration = xsuaaServiceConfiguration; + + this.tokenInfoExtractor = new TokenInfoExtractor() { + @Override + public String getJku(JWT jwt) { + return (String) jwt.getHeader().toJSONObject().getOrDefault(CLAIM_JKU, null); + } + + @Override + public String getKid(JWT jwt) { + return (String) jwt.getHeader().toJSONObject().getOrDefault(CLAIM_KID, null); + } + + @Override + public String getUaaDomain(JWT jwt) { + return xsuaaServiceConfiguration.getUaaDomain(); + } + }; this.tokenValidators.addAll(Arrays.asList(tokenValidators)); this.postValidationActions = postValidationActions != null ? postValidationActions : Collections.EMPTY_LIST; @@ -54,29 +71,14 @@ public Mono decode(String token) throws JwtException { throw new JwtException("Error initializing JWT decoder:" + e.getMessage()); } }).map(jwtToken -> { - try { - String subdomain = this.getSubdomain(jwtToken); - String zoneId = jwtToken.getJWTClaimsSet().getStringClaim(ZID); - return cache.get(subdomain, k -> this.getDecoder(zoneId, subdomain)); - } catch (ParseException e) { - throw new JwtException("Error initializing JWT decoder:" + e.getMessage()); - } + String cacheKey = tokenInfoExtractor.getJku(jwtToken) + tokenInfoExtractor.getKid(jwtToken); + return cache.get(cacheKey, k -> this.getDecoder(tokenInfoExtractor.getJku(jwtToken))); }).flatMap(decoder -> decoder.decode(token)) .doOnSuccess(jwt -> postValidationActions.forEach(act -> act.perform(jwt))); } - protected String getSubdomain(JWT jwt) throws ParseException { - String subdomain = ""; - JSONObject extAttr = jwt.getJWTClaimsSet().getJSONObjectClaim(EXT_ATTR); - if (extAttr != null && extAttr.getAsString(ZDN) != null) { - subdomain = extAttr.getAsString(ZDN); - } - return subdomain; - } - - private ReactiveJwtDecoder getDecoder(String zid, String subdomain) { - String url = xsuaaServiceConfiguration.getTokenKeyUrl(zid, subdomain); - NimbusReactiveJwtDecoder decoder = new NimbusReactiveJwtDecoder(url); + private ReactiveJwtDecoder getDecoder(String jku) { + NimbusReactiveJwtDecoder decoder = new NimbusReactiveJwtDecoder(jku); decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(tokenValidators)); return decoder; } diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/DummyXsuaaServiceConfiguration.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/DummyXsuaaServiceConfiguration.java index c46412b6df..9051bdb025 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/DummyXsuaaServiceConfiguration.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/DummyXsuaaServiceConfiguration.java @@ -29,11 +29,6 @@ public String getUaaUrl() { return "https://subdomain.authentication.eu10.hana.ondemand.com"; } - @Override - public String getTokenKeyUrl(String identityZoneId, String subdomain) { - return null; - } - @Override public String getAppId() { return appId; From 9d1f68d2172390162e9c5a3e17fe221c28275b50 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Mon, 9 Sep 2019 17:57:24 +0200 Subject: [PATCH 03/22] update version to 2.0.0-SNAPSHOT --- api/pom.xml | 4 ++-- pom.xml | 2 +- samples/spring-security-basic-auth/pom.xml | 2 +- samples/spring-security-xsuaa-usage/pom.xml | 4 ++-- samples/spring-webflux-security-xsuaa-usage/pom.xml | 2 +- spring-xsuaa-it/pom.xml | 2 +- spring-xsuaa-mock/README.md | 2 +- spring-xsuaa-mock/pom.xml | 2 +- spring-xsuaa-starter/pom.xml | 3 ++- spring-xsuaa-test/README.md | 2 +- spring-xsuaa-test/pom.xml | 2 +- spring-xsuaa/README.md | 4 ++-- spring-xsuaa/pom.xml | 2 +- token-client/README.md | 4 ++-- token-client/pom.xml | 2 +- 15 files changed, 20 insertions(+), 19 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index e1606ad330..6a0e89b28e 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -4,12 +4,12 @@ com.sap.cloud.security.xsuaa api - 1.7.0 + 2.0.0-SNAPSHOT com.sap.cloud.security.xsuaa parent - 1.7.0 + 2.0.0-SNAPSHOT jar diff --git a/pom.xml b/pom.xml index 8e6a49b94d..6b5f1263a3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.sap.cloud.security.xsuaa parent - 1.7.0 + 2.0.0-SNAPSHOT pom parent diff --git a/samples/spring-security-basic-auth/pom.xml b/samples/spring-security-basic-auth/pom.xml index cac08426c9..1de4e57567 100644 --- a/samples/spring-security-basic-auth/pom.xml +++ b/samples/spring-security-basic-auth/pom.xml @@ -11,7 +11,7 @@ spring-security-basic-auth - 1.7.0 + 2.0.0-SNAPSHOT spring-security-basic-auth diff --git a/samples/spring-security-xsuaa-usage/pom.xml b/samples/spring-security-xsuaa-usage/pom.xml index e5bf6fad73..e7a0c01769 100644 --- a/samples/spring-security-xsuaa-usage/pom.xml +++ b/samples/spring-security-xsuaa-usage/pom.xml @@ -14,7 +14,7 @@ com.sap.cloud.security.samples spring-security-xsuaa-usage - 1.3.0 + 2.0.0-SNAPSHOT spring-security-xsuaa-usage @@ -37,7 +37,7 @@ com.sap.cloud.security.xsuaa xsuaa-spring-boot-starter - 1.7.0 + 2.0.0-SNAPSHOT org.springframework.boot diff --git a/samples/spring-webflux-security-xsuaa-usage/pom.xml b/samples/spring-webflux-security-xsuaa-usage/pom.xml index 26ac63c430..60eb08876a 100644 --- a/samples/spring-webflux-security-xsuaa-usage/pom.xml +++ b/samples/spring-webflux-security-xsuaa-usage/pom.xml @@ -12,7 +12,7 @@ com.sap.cloud.security.samples spring-webflux-security-xsuaa-usage - 1.7.0 + 2.0.0-SNAPSHOT spring-webflux-security-xsuaa-usage diff --git a/spring-xsuaa-it/pom.xml b/spring-xsuaa-it/pom.xml index f41e3bb38c..c000c054ee 100644 --- a/spring-xsuaa-it/pom.xml +++ b/spring-xsuaa-it/pom.xml @@ -11,7 +11,7 @@ spring-xsuaa-it - 1.7.0 + 2.0.0-SNAPSHOT spring-xsuaa-it diff --git a/spring-xsuaa-mock/README.md b/spring-xsuaa-mock/README.md index 7a43762310..bdedda4f37 100644 --- a/spring-xsuaa-mock/README.md +++ b/spring-xsuaa-mock/README.md @@ -17,7 +17,7 @@ The default implementation offers already valid *token_keys* for JWT tokens, tha com.sap.cloud.security.xsuaa spring-xsuaa-mock - 1.7.0 + 2.0.0-SNAPSHOT org.springframework.boot diff --git a/spring-xsuaa-mock/pom.xml b/spring-xsuaa-mock/pom.xml index a9d4b7a7f3..8544e3afb8 100644 --- a/spring-xsuaa-mock/pom.xml +++ b/spring-xsuaa-mock/pom.xml @@ -7,7 +7,7 @@ com.sap.cloud.security.xsuaa parent - 1.7.0 + 2.0.0-SNAPSHOT spring-xsuaa-mock diff --git a/spring-xsuaa-starter/pom.xml b/spring-xsuaa-starter/pom.xml index 6404e847ee..d5330dcbf5 100644 --- a/spring-xsuaa-starter/pom.xml +++ b/spring-xsuaa-starter/pom.xml @@ -14,7 +14,7 @@ com.sap.cloud.security.xsuaa parent - 1.7.0 + 2.0.0-SNAPSHOT @@ -30,6 +30,7 @@ xsuaa-spring-boot-starter + 2.0.0-SNAPSHOT SAP Spring Boot Xsuaa Starter SAP Starter for integrating application with XSUAA service https://github.com/SAP/cloud-security-xsuaa-integration diff --git a/spring-xsuaa-test/README.md b/spring-xsuaa-test/README.md index fb4a3614d5..925ff79b20 100644 --- a/spring-xsuaa-test/README.md +++ b/spring-xsuaa-test/README.md @@ -27,7 +27,7 @@ This includes for example a `JwtGenerator` that generates JSON Web Tokens (JWT) com.sap.cloud.security.xsuaa spring-xsuaa-test - 1.7.0 + 2.0.0-SNAPSHOT test diff --git a/spring-xsuaa-test/pom.xml b/spring-xsuaa-test/pom.xml index 46fefea98f..7d21ca02d0 100644 --- a/spring-xsuaa-test/pom.xml +++ b/spring-xsuaa-test/pom.xml @@ -7,7 +7,7 @@ com.sap.cloud.security.xsuaa parent - 1.7.0 + 2.0.0-SNAPSHOT spring-xsuaa-test diff --git a/spring-xsuaa/README.md b/spring-xsuaa/README.md index 4836a7118a..5046f290b4 100644 --- a/spring-xsuaa/README.md +++ b/spring-xsuaa/README.md @@ -23,7 +23,7 @@ This library enhances the [spring-security](https://github.com/spring-projects/s com.sap.cloud.security.xsuaa spring-xsuaa - 1.7.0 + 2.0.0-SNAPSHOT org.apache.logging.log4j @@ -38,7 +38,7 @@ This library enhances the [spring-security](https://github.com/spring-projects/s com.sap.cloud.security.xsuaa xsuaa-spring-boot-starter - 1.7.0 + 2.0.0-SNAPSHOT ``` diff --git a/spring-xsuaa/pom.xml b/spring-xsuaa/pom.xml index f4813ad14c..aa42a547db 100644 --- a/spring-xsuaa/pom.xml +++ b/spring-xsuaa/pom.xml @@ -7,7 +7,7 @@ com.sap.cloud.security.xsuaa parent - 1.7.0 + 2.0.0-SNAPSHOT spring-xsuaa diff --git a/token-client/README.md b/token-client/README.md index 5066651f01..def216edaf 100644 --- a/token-client/README.md +++ b/token-client/README.md @@ -25,7 +25,7 @@ A Refresh Token ([RFC 6749, section 1.5](https://tools.ietf.org/html/rfc6749#sec com.sap.cloud.security.xsuaa token-client - 1.7.0 + 2.0.0-SNAPSHOT ``` ## Configuration for Spring Boot Applications @@ -36,7 +36,7 @@ In context of a Spring Boot application you may like to leverage auto-configurat com.sap.cloud.security.xsuaa xsuaa-spring-boot-starter - 1.7.0 + 2.0.0-SNAPSHOT ``` diff --git a/token-client/pom.xml b/token-client/pom.xml index 714532a6b2..52a19bf2f7 100644 --- a/token-client/pom.xml +++ b/token-client/pom.xml @@ -7,7 +7,7 @@ com.sap.cloud.security.xsuaa parent - 1.7.0 + 2.0.0-SNAPSHOT token-client From 76a8eb589caf2106dbf6836c66f05a70ef4097b5 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Tue, 10 Sep 2019 10:50:34 +0200 Subject: [PATCH 04/22] Get rid of Spring Jwt lib --- token-client/pom.xml | 5 ----- .../sap/cloud/security/xsuaa/tokenflows/UserTokenFlow.java | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/token-client/pom.xml b/token-client/pom.xml index 52a19bf2f7..d193408a0f 100644 --- a/token-client/pom.xml +++ b/token-client/pom.xml @@ -36,11 +36,6 @@ json 20190722 - - org.springframework.security - spring-security-jwt - 1.0.9.RELEASE - com.google.code.findbugs jsr305 diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/UserTokenFlow.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/UserTokenFlow.java index 59a4fa66ee..85ebe425b2 100644 --- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/UserTokenFlow.java +++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/UserTokenFlow.java @@ -1,7 +1,6 @@ package com.sap.cloud.security.xsuaa.tokenflows; import static com.sap.cloud.security.xsuaa.tokenflows.XsuaaTokenFlowsUtils.buildAuthorities; -import org.springframework.security.jwt.JwtHelper; import java.util.HashMap; import java.util.Iterator; @@ -17,6 +16,7 @@ import com.sap.cloud.security.xsuaa.client.OAuth2ServiceEndpointsProvider; import com.sap.cloud.security.xsuaa.client.OAuth2ServiceException; import com.sap.cloud.security.xsuaa.client.OAuth2TokenService; +import com.sap.cloud.security.xsuaa.jwt.Base64JwtDecoder; import com.sap.xsa.security.container.XSTokenRequest; /** @@ -215,7 +215,7 @@ private OAuth2TokenResponse requestUserToken(XsuaaTokenFlowRequest request) thro * @return {@code true} if the scope is contained, {@code false} otherwise. */ private boolean hasScope(String token, String scope) { - String claims = JwtHelper.decode(token).getClaims(); + String claims = new Base64JwtDecoder().decode(token).getPayload(); try { JSONObject rootObject = new JSONObject(claims); JSONArray scopesArray = rootObject.getJSONArray(SCOPE_CLAIM); From 2414dd3ba40ab98bff479c5ba5c5907a7cfefdf2 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Tue, 10 Sep 2019 10:53:48 +0200 Subject: [PATCH 05/22] sample should show getDecodedAccessToken --- .../src/main/java/sample/spring/xsuaa/TestController.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/samples/spring-security-xsuaa-usage/src/main/java/sample/spring/xsuaa/TestController.java b/samples/spring-security-xsuaa-usage/src/main/java/sample/spring/xsuaa/TestController.java index 347704a530..ab443d9108 100644 --- a/samples/spring-security-xsuaa-usage/src/main/java/sample/spring/xsuaa/TestController.java +++ b/samples/spring-security-xsuaa-usage/src/main/java/sample/spring/xsuaa/TestController.java @@ -126,7 +126,7 @@ public String requestClientCredentialsToken() throws TokenFlowException { OAuth2TokenResponse clientCredentialsTokenResponse = tokenFlows.clientCredentialsTokenFlow().execute(); logger.info("Got the Client Credentials Token: {}", clientCredentialsTokenResponse.getAccessToken()); - return "The client-credentials token (encoded) can be found in the logs 'cf logs spring-security-xsuaa-usage --recent'"; + return clientCredentialsTokenResponse.getDecodedAccessToken().getPayload(); } /** @@ -156,7 +156,9 @@ public String requestUserToken(@AuthenticationPrincipal Jwt jwt) throws TokenFlo logger.info("Got the exchanged token for 3rd party service: {}", userTokenResponse); logger.info("You can now call the 3rd party service passing the exchanged token value: {}. ", userTokenResponse); - return "The refresh-token: " + userTokenResponse.getRefreshToken() + ". The access-token (encoded) can be found in the logs 'cf logs spring-security-xsuaa-usage --recent'"; + return "

The access-token (decoded):

" + userTokenResponse.getDecodedAccessToken().getPayload() + + "

The refresh-token:

" + userTokenResponse.getRefreshToken() + + "

The access-token (encoded) can be found in the logs 'cf logs spring-security-xsuaa-usage --recent'

"; } /** @@ -176,7 +178,7 @@ public String requestRefreshToken(@AuthenticationPrincipal Jwt jwt, @PathVariabl logger.info("Got the access token for the refresh token: {}", refreshTokenResponse.getAccessToken()); logger.info("You could now inject this into Spring's SecurityContext, using: SpringSecurityContext.init(...)."); - return "The exchanged access token (encoded) can be found in the logs 'cf logs spring-security-xsuaa-usage --recent'"; + return refreshTokenResponse.getDecodedAccessToken().getPayload(); } } From b622d139ac49a86df65603801fd44c810f5862d0 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Tue, 10 Sep 2019 15:29:10 +0200 Subject: [PATCH 06/22] sonarlint --- .../security/xsuaa/token/XsuaaToken.java | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java index ea0c57859d..50dca510c4 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java @@ -16,24 +16,14 @@ import java.util.List; import java.util.Map; -import com.sap.cloud.security.xsuaa.client.ClientCredentials; -import com.sap.cloud.security.xsuaa.client.OAuth2TokenService; -import com.sap.cloud.security.xsuaa.client.XsuaaDefaultEndpoints; -import com.sap.cloud.security.xsuaa.client.XsuaaOAuth2TokenService; import com.sap.cloud.security.xsuaa.tokenflows.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.lang.Nullable; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.core.ClaimAccessor; import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.jwt.JwtClaimAccessor; import org.springframework.security.oauth2.jwt.JwtTimestampValidator; import org.springframework.util.Assert; -import org.springframework.web.client.RestOperations; -import org.springframework.web.client.RestTemplate; - -import com.sap.xsa.security.container.XSTokenRequest; import net.minidev.json.JSONArray; @@ -58,7 +48,6 @@ public class XsuaaToken extends Jwt implements Token { static final String CLAIM_EXTERNAL_CONTEXT = "ext_ctx"; private Collection authorities = Collections.emptyList(); - private XsuaaTokenFlows xsuaaTokenFlows = null; /** * @param jwt @@ -279,40 +268,4 @@ private String[] getStringListAttributeFromClaim(String attributeName, String cl return attributeValues; } - private String performClientCredentialsFlow(XSTokenRequest tokenRequest) { - String ccfToken; - try { - ccfToken = xsuaaTokenFlows.clientCredentialsTokenFlow() - .subdomain(this.getSubdomain()) - .attributes(tokenRequest.getAdditionalAuthorizationAttributes()) - .execute().getAccessToken(); - } catch (TokenFlowException e) { - throw new RuntimeException("Error performing Client Credentials Flow. See exception cause.", e); - } - - logger.info("Got the Client Credentials Flow Token: {}", ccfToken); - - return ccfToken; - } - - private String performUserTokenFlow(XSTokenRequest tokenRequest) { - String userToken; - try { - userToken = xsuaaTokenFlows.userTokenFlow() - .subdomain(this.getSubdomain()) - .token(this.getTokenValue()) - .attributes(tokenRequest.getAdditionalAuthorizationAttributes()) - .execute().getAccessToken(); - } catch (TokenFlowException e) { - throw new RuntimeException("Error performing User Token Flow. See exception cause.", e); - } - - logger.info("Got the exchanged token for 3rd party service (clientId: {}) : {}", tokenRequest.getClientId(), - userToken); - logger.info("You can now call the 3rd party service passing the exchanged token value: {}. ", - userToken); - - return userToken; - } - } From ac68e06ab0492dcd3217749fb24fc50599979af9 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Tue, 10 Sep 2019 16:11:31 +0200 Subject: [PATCH 07/22] fix webflux sample --- samples/spring-webflux-security-xsuaa-usage/manifest.yml | 2 ++ .../main/java/sample/spring/webflux/xsuaa/TestController.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/samples/spring-webflux-security-xsuaa-usage/manifest.yml b/samples/spring-webflux-security-xsuaa-usage/manifest.yml index f32b865962..6f7431016e 100644 --- a/samples/spring-webflux-security-xsuaa-usage/manifest.yml +++ b/samples/spring-webflux-security-xsuaa-usage/manifest.yml @@ -10,6 +10,8 @@ applications: routes: - route: spring-webflux-security-xsuaa-usage-((ID)).((LANDSCAPE_APPS_DOMAIN)) path: target/spring-webflux-security-xsuaa-usage.jar + env: + JBP_CONFIG_DEBUG: '{enabled: true}' services: - xsuaa-webflux diff --git a/samples/spring-webflux-security-xsuaa-usage/src/main/java/sample/spring/webflux/xsuaa/TestController.java b/samples/spring-webflux-security-xsuaa-usage/src/main/java/sample/spring/webflux/xsuaa/TestController.java index d5942bc588..f82009e75f 100644 --- a/samples/spring-webflux-security-xsuaa-usage/src/main/java/sample/spring/webflux/xsuaa/TestController.java +++ b/samples/spring-webflux-security-xsuaa-usage/src/main/java/sample/spring/webflux/xsuaa/TestController.java @@ -18,6 +18,6 @@ public Mono> sayHello() { return ReactiveSecurityContext.getToken() .doOnError(throwable -> Mono.just(unAuthenticated)) .map(token -> ResponseEntity.ok().contentType(MediaType.TEXT_PLAIN) - .body("Authorities: " + token.getAuthorities())); + .body("Scopes: " + token.getScopes())); } } From 7189cbef61de48cdac60cd37fcb15365b816d5f7 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Tue, 10 Sep 2019 16:15:00 +0200 Subject: [PATCH 08/22] fix imports / format --- .../testservice/api/v1/TestController.java | 4 +++- .../BasicCredentialExtractorTest.java | 4 ++-- .../XsuaaServicePropertySourceFactory.java | 6 +++-- .../XsuaaAutoConfiguration.java | 12 ++++++---- ...uaaResourceServerJwkAutoConfiguration.java | 13 ++++++---- .../XsuaaTokenFlowAutoConfiguration.java | 11 ++++++--- .../AuthenticationInformationExtractor.java | 4 ++-- .../xsuaa/extractor/AuthoritiesExtractor.java | 3 ++- ...ultAuthenticationInformationExtractor.java | 4 ++-- .../DefaultAuthoritiesExtractor.java | 5 ++-- .../extractor/LocalAuthoritiesExtractor.java | 3 ++- .../xsuaa/extractor/TokenBrokerResolver.java | 4 ++-- .../xsuaa/token/ReactiveSecurityContext.java | 8 ++++++- .../ReactiveTokenAuthenticationConverter.java | 6 +++-- .../xsuaa/token/SpringSecurityContext.java | 5 ++-- .../sap/cloud/security/xsuaa/token/Token.java | 6 ++--- .../token/TokenAuthenticationConverter.java | 9 +++---- .../security/xsuaa/token/XsuaaToken.java | 1 - .../ReactiveXsuaaJwtDecoder.java | 6 ++++- .../XsuaaJwtDecoderBuilder.java | 4 ++-- .../XsuaaAutoConfigurationTest.java | 15 +++++++----- ...esourceServerJwkAutoConfigurationTest.java | 3 ++- .../XsuaaTokenFlowAutoConfigurationTest.java | 15 ++++++------ .../token/ReactiveSecurityContextTest.java | 4 +++- .../token/SpringSecurityContextTest.java | 21 ++++++++-------- .../TokenAuthenticationConverterTest.java | 7 +++--- .../security/xsuaa/token/XsuaaTokenTest.java | 24 ++++++++----------- ...uaaAudienceValidatorForCloneTokenTest.java | 11 +++++---- .../XsuaaAudienceValidatorTest.java | 11 +++++---- 29 files changed, 134 insertions(+), 95 deletions(-) diff --git a/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java b/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java index f2a6d57db7..0da7b100ef 100644 --- a/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java +++ b/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java @@ -1,6 +1,8 @@ package testservice.api.v1; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertThat; import java.util.Collection; diff --git a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java index d28e142dbd..83c5a8ad92 100644 --- a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java +++ b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java @@ -5,12 +5,12 @@ import static org.assertj.core.api.Assertions.assertThat; +import javax.servlet.http.HttpServletRequest; + import java.util.Arrays; import java.util.Base64; import java.util.List; -import javax.servlet.http.HttpServletRequest; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServicePropertySourceFactory.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServicePropertySourceFactory.java index 7b0c8e8ecf..5a07d17a9b 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServicePropertySourceFactory.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServicePropertySourceFactory.java @@ -3,8 +3,6 @@ import java.io.IOException; import java.util.Properties; -import com.sap.cloud.security.xsuaa.autoconfiguration.XsuaaAutoConfiguration; -import net.minidev.json.parser.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.PropertiesPropertySource; @@ -12,6 +10,10 @@ import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.PropertySourceFactory; +import com.sap.cloud.security.xsuaa.autoconfiguration.XsuaaAutoConfiguration; + +import net.minidev.json.parser.ParseException; + /** * Part of Auto Configuration {@link XsuaaAutoConfiguration} * diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfiguration.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfiguration.java index b71c6a164a..940234f991 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfiguration.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfiguration.java @@ -1,20 +1,22 @@ package com.sap.cloud.security.xsuaa.autoconfiguration; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfigurationDefault; -import com.sap.cloud.security.xsuaa.XsuaaServicePropertySourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfigurationDefault; +import com.sap.cloud.security.xsuaa.XsuaaServicePropertySourceFactory; + /** * {@link EnableAutoConfiguration Auto-configuration} for default beans used by * the XSUAA client library. diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfiguration.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfiguration.java index e73c8c6a98..97de692389 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfiguration.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfiguration.java @@ -1,19 +1,24 @@ package com.sap.cloud.security.xsuaa.autoconfiguration; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoderBuilder; +import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.*; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; -import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.*; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoderBuilder; /** * {@link EnableAutoConfiguration Auto-configuration} that exposes a diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaTokenFlowAutoConfiguration.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaTokenFlowAutoConfiguration.java index 27549f7618..b107765158 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaTokenFlowAutoConfiguration.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaTokenFlowAutoConfiguration.java @@ -1,8 +1,5 @@ package com.sap.cloud.security.xsuaa.autoconfiguration; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.client.*; -import com.sap.cloud.security.xsuaa.tokenflows.XsuaaTokenFlows; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -14,6 +11,14 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestOperations; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.client.ClientCredentials; +import com.sap.cloud.security.xsuaa.client.OAuth2ServiceEndpointsProvider; +import com.sap.cloud.security.xsuaa.client.OAuth2TokenService; +import com.sap.cloud.security.xsuaa.client.XsuaaDefaultEndpoints; +import com.sap.cloud.security.xsuaa.client.XsuaaOAuth2TokenService; +import com.sap.cloud.security.xsuaa.tokenflows.XsuaaTokenFlows; + /** * {@link EnableAutoConfiguration Auto-configuration} for default beans used by * the XSUAA client library. diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractor.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractor.java index 4934d65346..4a5eb6b8ad 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractor.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractor.java @@ -3,11 +3,11 @@ */ package com.sap.cloud.security.xsuaa.extractor; +import javax.servlet.http.HttpServletRequest; + import java.util.List; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; - public interface AuthenticationInformationExtractor { /** diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthoritiesExtractor.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthoritiesExtractor.java index 6ce5a524b3..ba64493df9 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthoritiesExtractor.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthoritiesExtractor.java @@ -2,9 +2,10 @@ import java.util.Collection; -import com.sap.cloud.security.xsuaa.token.XsuaaToken; import org.springframework.security.core.GrantedAuthority; +import com.sap.cloud.security.xsuaa.token.XsuaaToken; + /** * Extracts the authorities from the Jwt token. Can use this method to map / * manipulate scopes, e.g. by changing their prefix, etc. diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java index 99628b743e..800086b168 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java @@ -3,13 +3,13 @@ */ package com.sap.cloud.security.xsuaa.extractor; +import javax.servlet.http.HttpServletRequest; + import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; - /** * Default Implementation * diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthoritiesExtractor.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthoritiesExtractor.java index ab1d26ea7f..10091a70cf 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthoritiesExtractor.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthoritiesExtractor.java @@ -5,13 +5,14 @@ import java.util.List; import java.util.stream.Collectors; -import com.sap.cloud.security.xsuaa.token.TokenClaims; -import com.sap.cloud.security.xsuaa.token.XsuaaToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; +import com.sap.cloud.security.xsuaa.token.TokenClaims; +import com.sap.cloud.security.xsuaa.token.XsuaaToken; + public class DefaultAuthoritiesExtractor extends JwtAuthenticationConverter implements AuthoritiesExtractor { public Collection getAuthorities(XsuaaToken jwt) { diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/LocalAuthoritiesExtractor.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/LocalAuthoritiesExtractor.java index 276b4fcdd0..e8a6f93750 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/LocalAuthoritiesExtractor.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/LocalAuthoritiesExtractor.java @@ -5,10 +5,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.sap.cloud.security.xsuaa.token.XsuaaToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import com.sap.cloud.security.xsuaa.token.XsuaaToken; + public class LocalAuthoritiesExtractor implements AuthoritiesExtractor { protected String appId; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java index 69330d9a1b..f248002a16 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java @@ -1,5 +1,7 @@ package com.sap.cloud.security.xsuaa.extractor; +import javax.servlet.http.HttpServletRequest; + import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -8,8 +10,6 @@ import java.util.List; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cache.Cache; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContext.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContext.java index b7d8f625d8..b170d5d666 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContext.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContext.java @@ -7,6 +7,9 @@ import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.oauth2.jwt.Jwt; + +import com.sap.cloud.security.xsuaa.jwt.Base64JwtDecoder; + import reactor.core.publisher.Mono; public class ReactiveSecurityContext { @@ -25,7 +28,10 @@ static public Mono getToken() { .map(SecurityContext::getAuthentication) .map(Authentication::getCredentials) .map(credentials -> new XsuaaToken((Jwt) credentials)) - .doOnSuccess(token -> logger.info("Got Jwt token: " + token.toString())) + .doOnSuccess(token -> { + String decodedJson = new Base64JwtDecoder().decode(token.getAppToken()).getPayload(); + logger.info("Got Jwt token: " + decodedJson); + }) .doOnError(throwable -> logger.error("ERROR to getToken", throwable)); } } diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveTokenAuthenticationConverter.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveTokenAuthenticationConverter.java index 02766c4593..d40a65fa62 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveTokenAuthenticationConverter.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveTokenAuthenticationConverter.java @@ -1,10 +1,12 @@ package com.sap.cloud.security.xsuaa.token; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.extractor.LocalAuthoritiesExtractor; import org.springframework.core.convert.converter.Converter; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.oauth2.jwt.Jwt; + +import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.extractor.LocalAuthoritiesExtractor; + import reactor.core.publisher.Mono; /** diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContext.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContext.java index f4b4932ca2..c205d84762 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContext.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContext.java @@ -1,7 +1,5 @@ package com.sap.cloud.security.xsuaa.token; -import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor; -import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoder; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -9,6 +7,9 @@ import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.util.Assert; +import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor; +import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoder; + public class SpringSecurityContext { /** diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java index bc8fa0ca40..bf5ad988c1 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java @@ -1,12 +1,12 @@ package com.sap.cloud.security.xsuaa.token; +import java.util.Collection; +import java.util.Date; + import org.springframework.lang.Nullable; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; -import java.util.Collection; -import java.util.Date; - public interface Token extends UserDetails { String GRANTTYPE_CLIENTCREDENTIAL = "client_credentials"; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverter.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverter.java index c48a0a8d0b..b69763bae1 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverter.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverter.java @@ -1,14 +1,15 @@ package com.sap.cloud.security.xsuaa.token; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor; -import com.sap.cloud.security.xsuaa.extractor.DefaultAuthoritiesExtractor; -import com.sap.cloud.security.xsuaa.extractor.LocalAuthoritiesExtractor; import org.springframework.core.convert.converter.Converter; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.util.Assert; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor; +import com.sap.cloud.security.xsuaa.extractor.DefaultAuthoritiesExtractor; +import com.sap.cloud.security.xsuaa.extractor.LocalAuthoritiesExtractor; + /** * An authentication converter that extracts authorization related information * from the Jwt token. For example the{@link LocalAuthoritiesExtractor} can diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java index 50dca510c4..fffcc8ca92 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java @@ -16,7 +16,6 @@ import java.util.List; import java.util.Map; -import com.sap.cloud.security.xsuaa.tokenflows.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.lang.Nullable; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/ReactiveXsuaaJwtDecoder.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/ReactiveXsuaaJwtDecoder.java index 1d317c5684..b836ed69cd 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/ReactiveXsuaaJwtDecoder.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/ReactiveXsuaaJwtDecoder.java @@ -4,7 +4,11 @@ import static com.sap.cloud.security.xsuaa.token.TokenClaims.CLAIM_KID; import java.text.ParseException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.concurrent.TimeUnit; import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderBuilder.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderBuilder.java index 2eb555e814..8cec2b52dc 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderBuilder.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderBuilder.java @@ -3,14 +3,14 @@ import java.util.Arrays; import java.util.Collection; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.JwtValidators; import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; -import org.springframework.security.oauth2.jwt.JwtValidators; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; public class XsuaaJwtDecoderBuilder { diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfigurationTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfigurationTest.java index 2aae5de1f8..7a4e9e067f 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfigurationTest.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfigurationTest.java @@ -1,8 +1,11 @@ package com.sap.cloud.security.xsuaa.autoconfiguration; -import com.sap.cloud.security.xsuaa.DummyXsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfigurationDefault; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; import org.junit.Test; import org.junit.runner.RunWith; @@ -19,9 +22,9 @@ import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; +import com.sap.cloud.security.xsuaa.DummyXsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfigurationDefault; @RunWith(SpringRunner.class) @SpringBootTest(classes = { XsuaaAutoConfiguration.class, DummyXsuaaServiceConfiguration.class }) diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfigurationTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfigurationTest.java index f0b11c8eba..be7f2906e3 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfigurationTest.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfigurationTest.java @@ -6,7 +6,6 @@ import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; -import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoder; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -24,6 +23,8 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport; import org.springframework.test.context.junit4.SpringRunner; +import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoder; + @RunWith(SpringRunner.class) @SpringBootTest(classes = { XsuaaResourceServerJwkAutoConfiguration.class, XsuaaAutoConfiguration.class }) public class XsuaaResourceServerJwkAutoConfigurationTest { diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaTokenFlowAutoConfigurationTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaTokenFlowAutoConfigurationTest.java index 7f75730501..15374a9ddc 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaTokenFlowAutoConfigurationTest.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaTokenFlowAutoConfigurationTest.java @@ -1,11 +1,7 @@ package com.sap.cloud.security.xsuaa.autoconfiguration; -import com.sap.cloud.security.xsuaa.DummyXsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.client.ClientCredentials; -import com.sap.cloud.security.xsuaa.client.XsuaaDefaultEndpoints; -import com.sap.cloud.security.xsuaa.client.XsuaaOAuth2TokenService; -import com.sap.cloud.security.xsuaa.tokenflows.XsuaaTokenFlows; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -19,7 +15,12 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.client.RestOperations; -import static org.assertj.core.api.Assertions.assertThat; +import com.sap.cloud.security.xsuaa.DummyXsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.client.ClientCredentials; +import com.sap.cloud.security.xsuaa.client.XsuaaDefaultEndpoints; +import com.sap.cloud.security.xsuaa.client.XsuaaOAuth2TokenService; +import com.sap.cloud.security.xsuaa.tokenflows.XsuaaTokenFlows; @RunWith(SpringRunner.class) @SpringBootTest(classes = { XsuaaAutoConfiguration.class, XsuaaTokenFlowAutoConfiguration.class, diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContextTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContextTest.java index 2fae87ddff..19c3d08b24 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContextTest.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContextTest.java @@ -1,6 +1,5 @@ package com.sap.cloud.security.xsuaa.token; -import com.sap.cloud.security.xsuaa.test.JwtGenerator; import org.junit.Ignore; import org.junit.Test; import org.springframework.security.access.AccessDeniedException; @@ -8,6 +7,9 @@ import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextImpl; + +import com.sap.cloud.security.xsuaa.test.JwtGenerator; + import reactor.core.publisher.Mono; import reactor.test.StepVerifier; diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContextTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContextTest.java index af6019fd56..32c8f97aff 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContextTest.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContextTest.java @@ -1,8 +1,13 @@ package com.sap.cloud.security.xsuaa.token; -import com.sap.cloud.security.xsuaa.extractor.DefaultAuthoritiesExtractor; -import com.sap.cloud.security.xsuaa.test.JwtGenerator; -import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoder; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -11,13 +16,9 @@ import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.when; +import com.sap.cloud.security.xsuaa.extractor.DefaultAuthoritiesExtractor; +import com.sap.cloud.security.xsuaa.test.JwtGenerator; +import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoder; public class SpringSecurityContextTest { diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverterTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverterTest.java index 9998b87a5e..eb00033357 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverterTest.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverterTest.java @@ -9,9 +9,6 @@ import java.util.HashSet; import java.util.Set; -import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor; -import com.sap.cloud.security.xsuaa.extractor.DefaultAuthoritiesExtractor; -import com.sap.cloud.security.xsuaa.test.JwtGenerator; import org.junit.Before; import org.junit.Test; import org.springframework.security.authentication.AbstractAuthenticationToken; @@ -19,6 +16,10 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; +import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor; +import com.sap.cloud.security.xsuaa.extractor.DefaultAuthoritiesExtractor; +import com.sap.cloud.security.xsuaa.test.JwtGenerator; + public class TokenAuthenticationConverterTest { private String xsAppName = "my-app-name!400"; private TokenAuthenticationConverter tokenConverterDefault; diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java index 9eaf793be6..30710f2eb6 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java @@ -1,35 +1,31 @@ package com.sap.cloud.security.xsuaa.token; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; import java.time.Instant; -import java.util.*; - -import com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.web.client.RestTemplate; import com.nimbusds.jwt.JWTClaimsSet; import com.sap.cloud.security.xsuaa.test.JwtGenerator; -import com.sap.xsa.security.container.XSTokenRequest; public class XsuaaTokenTest { diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorForCloneTokenTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorForCloneTokenTest.java index 8b1654ec4a..5fbf850314 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorForCloneTokenTest.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorForCloneTokenTest.java @@ -6,11 +6,6 @@ import java.util.Date; import java.util.List; -import com.nimbusds.jwt.JWTClaimsSet; -import com.sap.cloud.security.xsuaa.DummyXsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.test.JwtGenerator; -import com.sap.cloud.security.xsuaa.token.TokenClaims; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -18,6 +13,12 @@ import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; +import com.nimbusds.jwt.JWTClaimsSet; +import com.sap.cloud.security.xsuaa.DummyXsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.test.JwtGenerator; +import com.sap.cloud.security.xsuaa.token.TokenClaims; + public class XsuaaAudienceValidatorForCloneTokenTest { private JWTClaimsSet.Builder claimsBuilder; diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorTest.java index e7b918a3ad..0cddf0a81c 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorTest.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorTest.java @@ -8,11 +8,6 @@ import java.util.Date; import java.util.List; -import com.nimbusds.jwt.JWTClaimsSet; -import com.sap.cloud.security.xsuaa.DummyXsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; -import com.sap.cloud.security.xsuaa.test.JwtGenerator; -import com.sap.cloud.security.xsuaa.token.TokenClaims; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -21,6 +16,12 @@ import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; import org.springframework.security.oauth2.jwt.Jwt; +import com.nimbusds.jwt.JWTClaimsSet; +import com.sap.cloud.security.xsuaa.DummyXsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.test.JwtGenerator; +import com.sap.cloud.security.xsuaa.token.TokenClaims; + public class XsuaaAudienceValidatorTest { private Jwt tokenWithAudience; From 6d3f0c9f32fa81a6623cc7af30c72b5ed1d5d135 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Tue, 10 Sep 2019 20:12:29 +0200 Subject: [PATCH 09/22] improve webflux sample --- .../main/java/sample/spring/webflux/xsuaa/TestController.java | 3 ++- .../java/sample/spring/webflux/xsuaa/TestControllerTest.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/samples/spring-webflux-security-xsuaa-usage/src/main/java/sample/spring/webflux/xsuaa/TestController.java b/samples/spring-webflux-security-xsuaa-usage/src/main/java/sample/spring/webflux/xsuaa/TestController.java index f82009e75f..7ba455b523 100644 --- a/samples/spring-webflux-security-xsuaa-usage/src/main/java/sample/spring/webflux/xsuaa/TestController.java +++ b/samples/spring-webflux-security-xsuaa-usage/src/main/java/sample/spring/webflux/xsuaa/TestController.java @@ -1,5 +1,6 @@ package sample.spring.webflux.xsuaa; +import com.sap.cloud.security.xsuaa.jwt.Base64JwtDecoder; import com.sap.cloud.security.xsuaa.token.ReactiveSecurityContext; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -18,6 +19,6 @@ public Mono> sayHello() { return ReactiveSecurityContext.getToken() .doOnError(throwable -> Mono.just(unAuthenticated)) .map(token -> ResponseEntity.ok().contentType(MediaType.TEXT_PLAIN) - .body("Scopes: " + token.getScopes())); + .body(new Base64JwtDecoder().decode(token.getAppToken()).getPayload())); } } diff --git a/samples/spring-webflux-security-xsuaa-usage/src/test/java/sample/spring/webflux/xsuaa/TestControllerTest.java b/samples/spring-webflux-security-xsuaa-usage/src/test/java/sample/spring/webflux/xsuaa/TestControllerTest.java index 9b97895cdc..057e33ab4e 100644 --- a/samples/spring-webflux-security-xsuaa-usage/src/test/java/sample/spring/webflux/xsuaa/TestControllerTest.java +++ b/samples/spring-webflux-security-xsuaa-usage/src/test/java/sample/spring/webflux/xsuaa/TestControllerTest.java @@ -14,6 +14,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.Assert; +import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.startsWith; @RunWith(SpringRunner.class) @@ -42,7 +43,7 @@ public void authorizedRequest() { webClient.method(HttpMethod.GET).uri("/v1/sayHello").contentType(MediaType.APPLICATION_JSON_UTF8) .header(HttpHeaders.AUTHORIZATION, jwtGenerator.getTokenForAuthorizationHeader()).exchange() - .expectStatus().is2xxSuccessful().expectBody(String.class).value(startsWith("Authorities:")); + .expectStatus().is2xxSuccessful().expectBody(String.class).value(containsString(",\"scope\":[\"xsapplication!t895.Read\"],")); } private String getGlobalScope(String localScope) { From fadd3ec939575ede831e87bde55a60666f483252 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Tue, 10 Sep 2019 20:13:43 +0200 Subject: [PATCH 10/22] make part of extractor package private --- .../BasicCredentialExtractorTest.java | 4 +- ...ultAuthenticationInformationExtractor.java | 10 ++-- .../security/xsuaa/extractor/TokenBroker.java | 3 +- .../xsuaa/extractor/TokenBrokerResolver.java | 2 +- .../xsuaa/extractor/TokenUrlUtils.java | 14 ++--- .../xsuaa/extractor/UaaTokenBroker.java | 54 +++++++------------ 6 files changed, 36 insertions(+), 51 deletions(-) diff --git a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java index 83c5a8ad92..d28e142dbd 100644 --- a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java +++ b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java @@ -5,12 +5,12 @@ import static org.assertj.core.api.Assertions.assertThat; -import javax.servlet.http.HttpServletRequest; - import java.util.Arrays; import java.util.Base64; import java.util.List; +import javax.servlet.http.HttpServletRequest; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java index 800086b168..5a1b475d28 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java @@ -14,7 +14,7 @@ * Default Implementation * */ -public class DefaultAuthenticationInformationExtractor implements AuthenticationInformationExtractor { +class DefaultAuthenticationInformationExtractor implements AuthenticationInformationExtractor { private static final String SUBDOMAIN_HEADER = "X-Identity-Zone-Subdomain"; @@ -22,21 +22,21 @@ public class DefaultAuthenticationInformationExtractor implements Authentication private List authenticationMethods = Arrays.asList(AuthenticationMethod.BASIC, AuthenticationMethod.OAUTH2); - public DefaultAuthenticationInformationExtractor() { + DefaultAuthenticationInformationExtractor() { super(); this.subDomain = null; } - public DefaultAuthenticationInformationExtractor(String subDomain) { + DefaultAuthenticationInformationExtractor(String subDomain) { super(); this.subDomain = subDomain; } - public DefaultAuthenticationInformationExtractor(AuthenticationMethod... authenticationMethods) { + DefaultAuthenticationInformationExtractor(AuthenticationMethod... authenticationMethods) { this(null, authenticationMethods); } - public DefaultAuthenticationInformationExtractor(String subDomain, AuthenticationMethod... authenticationMethods) { + DefaultAuthenticationInformationExtractor(String subDomain, AuthenticationMethod... authenticationMethods) { super(); this.subDomain = subDomain; this.authenticationMethods = Arrays.asList(authenticationMethods); diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java index eba0f7d110..f73eb6c307 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java @@ -2,8 +2,9 @@ /** * - * + * @deprecated in favor of {@link com.sap.cloud.security.xsuaa.tokenflows.XsuaaTokenFlows} API. */ +@Deprecated public interface TokenBroker { /** diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java index f248002a16..5a31495c58 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java @@ -55,7 +55,7 @@ public class TokenBrokerResolver implements BearerTokenResolver { * configured AuthenticationMethodConfiguration */ - public TokenBrokerResolver(XsuaaServiceConfiguration configuration, Cache tokenCache, TokenBroker tokenBroker, + TokenBrokerResolver(XsuaaServiceConfiguration configuration, Cache tokenCache, TokenBroker tokenBroker, AuthenticationInformationExtractor authenticationConfig) { this.configuration = configuration; this.tokenCache = tokenCache; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenUrlUtils.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenUrlUtils.java index 87692bc15e..0e95608bde 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenUrlUtils.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenUrlUtils.java @@ -12,7 +12,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -public final class TokenUrlUtils { +final class TokenUrlUtils { private final static Log logger = LogFactory.getLog(TokenUrlUtils.class); @@ -34,8 +34,7 @@ private TokenUrlUtils() { * * @return token request URL */ - - public static String getMultiTenancyUrl(final String endpoint, final String uaaUrl, final String uaaDomain, + static String getMultiTenancyUrl(final String endpoint, final String uaaUrl, final String uaaDomain, final String uaaSubDomain) { Objects.requireNonNull(uaaUrl, "URL must not be null"); Objects.requireNonNull(uaaDomain, "Domain must not be null"); @@ -47,7 +46,8 @@ public static String getMultiTenancyUrl(final String endpoint, final String uaaU return TokenUrlUtils.getUrl(endpoint, uaaUrl, uaaDomain, uaaSubDomain); } - public static String getOauthTokenUrl(final String endpoint, final String uaaUrl, final String uaaDomain) { + // TODO check how it can be replaced by oauthTokenUrl = new XsuaaDefaultEndpoints(uaaUrl).getTokenEndpoint().toString(); + static String getOauthTokenUrl(final String endpoint, final String uaaUrl, final String uaaDomain) { return TokenUrlUtils.getMultiTenancyUrl(endpoint, uaaUrl, uaaDomain, null); } @@ -65,7 +65,7 @@ private static String getUrl(final String endpoint, final String uaaUrl, final S try { url = new URL(uaaUrl); } catch (MalformedURLException e) { - throw new RuntimeException("Cannot create valid URL from given UAA-Url " + uaaUrl); + throw new RuntimeException("Cannot create valid URL from given UAA-Url" + uaaUrl); } String protocol = url.getProtocol(); @@ -74,7 +74,7 @@ private static String getUrl(final String endpoint, final String uaaUrl, final S return tenantTokenUrl; } - public static String getHost(String path) { + static String getHost(String path) { URL url; try { url = new URL(path); @@ -84,7 +84,7 @@ public static String getHost(String path) { return url.getHost(); } - public static boolean isUrl(String url) { + static boolean isUrl(String url) { if (isEmpty(url)) { return false; } diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java index ac3aa2ee58..962f4559c3 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java @@ -1,75 +1,59 @@ package com.sap.cloud.security.xsuaa.extractor; +import java.net.URI; import java.util.Base64; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.cache.annotation.Cacheable; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestTemplate; -@Component -public class UaaTokenBroker implements TokenBroker { +import com.sap.cloud.security.xsuaa.client.ClientCredentials; +import com.sap.cloud.security.xsuaa.client.OAuth2ServiceException; +import com.sap.cloud.security.xsuaa.client.XsuaaOAuth2TokenService; + +/** + * @deprecated in favor of {@link XsuaaOAuth2TokenService} + */ +@Deprecated +class UaaTokenBroker implements TokenBroker { private final static Log logger = LogFactory.getLog(UaaTokenBroker.class); private final RestTemplate restTemplate; + private XsuaaOAuth2TokenService oAuth2TokenService; - public UaaTokenBroker(RestTemplate restTemplate) { - super(); + UaaTokenBroker(RestTemplate restTemplate) { this.restTemplate = restTemplate; + this.oAuth2TokenService = new XsuaaOAuth2TokenService(restTemplate); } - public UaaTokenBroker() { + UaaTokenBroker() { this(new RestTemplate()); } @Override - @Cacheable(cacheManager = "xsuaa.tokenbroker") public String getAccessTokenFromClientCredentials(String tokenURL, String clientId, String clientSecret) throws TokenBrokerException { try { - HttpHeaders headers = new HttpHeaders(); - String credentials = clientId + ":" + clientSecret; - String base64Creds = Base64.getEncoder().encodeToString(credentials.getBytes()); - headers.add("ACCEPT", "application/json"); - headers.add("AUTHORIZATION", "Basic " + base64Creds); - - MultiValueMap body = new LinkedMultiValueMap(); - - body.add("grant_type", "client_credentials"); - body.add("response_type", "token"); - body.add("client_id", clientId); - - // Note the body object as first parameter! - HttpEntity httpEntity = new HttpEntity(body, headers); - - @SuppressWarnings("rawtypes") - ResponseEntity exchange = restTemplate.exchange(tokenURL, HttpMethod.POST, httpEntity, Map.class); - - return (String) exchange.getBody().get("access_token"); - } catch (HttpClientErrorException ex) { - logger.warn("Cannot obtain Token from given client credentials"); - throw new TokenBrokerException( - "Error obtaining access token:" + ex.getStatusText() + " " + ex.getResponseBodyAsString()); - } catch (HttpServerErrorException ex) { - logger.warn("Cannot obtain Token from given client credentials"); - throw new TokenBrokerException("Error obtaining access token from server:" + ex.getStatusText() + " " - + ex.getResponseBodyAsString()); + return oAuth2TokenService.retrieveAccessTokenViaClientCredentialsGrant( + URI.create(tokenURL), new ClientCredentials(clientId, clientSecret), null, null).getAccessToken(); + } catch (OAuth2ServiceException ex) { + throw new TokenBrokerException("Cannot obtain Token from given clientId / secret", ex); } } @Override + // TODO move to XsuaaOAuth2TokenService public String getAccessTokenFromPasswordCredentials(String tokenURL, String clientId, String clientSecret, String username, String password) throws TokenBrokerException { try { From 5d299cb6ee23ad46b1138bef5c4aad3d781a17d9 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Tue, 10 Sep 2019 20:15:03 +0200 Subject: [PATCH 11/22] improve error handling of XsuaaOAuth2TokenService --- .../security/xsuaa/client/XsuaaOAuth2TokenService.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 c3ec85378d..6df2002750 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 @@ -29,6 +29,7 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestOperations; import org.springframework.web.util.UriBuilder; import org.springframework.web.util.UriComponentsBuilder; @@ -151,11 +152,15 @@ private OAuth2TokenResponse requestAccessToken(URI tokenEndpointUri, HttpHeaders if (!ex.getStatusCode().is2xxSuccessful()) { throw new OAuth2ServiceException(String.format( "Error retrieving JWT token. Received status code %s. Call to XSUAA was not successful: %s", - ex.getStatusCode(), ex.getCause())); + ex.getStatusCode(), ex.getResponseBodyAsString())); } throw new OAuth2ServiceException(String.format( "Error retrieving JWT token. Call to XSUAA was not successful: %s", - ex.getMessage())); + ex.getResponseBodyAsString())); + } catch (HttpServerErrorException ex) { + logger.warn("Error obtaining access token from server"); + throw new OAuth2ServiceException(String.format("Error obtaining access token from server (%s): %s", + ex.getStatusCode(), ex.getResponseBodyAsString())); } @SuppressWarnings("unchecked") From f8d54016e4fc414d38b363bb69032dee013c3558 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Tue, 10 Sep 2019 20:15:56 +0200 Subject: [PATCH 12/22] fix sonar issues --- .../security/xsuaa/token/ReactiveSecurityContext.java | 7 +++---- .../java/com/sap/cloud/security/xsuaa/token/Token.java | 2 +- .../java/com/sap/cloud/security/xsuaa/Assertions.java | 3 +++ .../cloud/security/xsuaa/client/ClientCredentials.java | 2 -- .../xsuaa/tokenflows/XsuaaTokenFlowRequest.java | 10 +++++++--- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContext.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContext.java index b170d5d666..ac09913876 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContext.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/ReactiveSecurityContext.java @@ -15,6 +15,8 @@ public class ReactiveSecurityContext { private static Logger logger = LoggerFactory.getLogger(ReactiveSecurityContext.class); + private ReactiveSecurityContext() {} + /** * Obtain the Token object from the Spring Reactive SecurityContext * @@ -28,10 +30,7 @@ static public Mono getToken() { .map(SecurityContext::getAuthentication) .map(Authentication::getCredentials) .map(credentials -> new XsuaaToken((Jwt) credentials)) - .doOnSuccess(token -> { - String decodedJson = new Base64JwtDecoder().decode(token.getAppToken()).getPayload(); - logger.info("Got Jwt token: " + decodedJson); - }) + .doOnSuccess(token -> logger.info("Got Jwt token: {}", token.toString())) .doOnError(throwable -> logger.error("ERROR to getToken", throwable)); } } diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java index bf5ad988c1..dd7361c16c 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java @@ -9,7 +9,7 @@ public interface Token extends UserDetails { - String GRANTTYPE_CLIENTCREDENTIAL = "client_credentials"; + static final String GRANTTYPE_CLIENTCREDENTIAL = "client_credentials"; /** * Returns the subaccount identifier, which can be used as tenant GUID. diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/Assertions.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/Assertions.java index b3b29efdc4..5ee3224d98 100644 --- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/Assertions.java +++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/Assertions.java @@ -1,6 +1,9 @@ package com.sap.cloud.security.xsuaa; public class Assertions { + + private Assertions() {} + public static void assertNotNull(Object object, String message) { if (object == null) { throw new IllegalArgumentException(message); diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/ClientCredentials.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/ClientCredentials.java index c970838702..a1a46bcfed 100644 --- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/ClientCredentials.java +++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/ClientCredentials.java @@ -1,7 +1,5 @@ package com.sap.cloud.security.xsuaa.client; -import com.sap.cloud.security.xsuaa.Assertions; - import javax.annotation.Nonnull; import java.util.Objects; diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/XsuaaTokenFlowRequest.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/XsuaaTokenFlowRequest.java index e3d7a4e670..34398057be 100644 --- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/XsuaaTokenFlowRequest.java +++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/XsuaaTokenFlowRequest.java @@ -21,6 +21,7 @@ class XsuaaTokenFlowRequest implements XSTokenRequest { private String subdomain; private String clientSecret; private Map additionalAuthorizationAttributes; + private static final String UNSUPPORTED_INTF_METHOD_INFO = "This XSTokenRequest method is no longer needed in context of new XsuaaTokenFlows API."; /** * Creates a new token exchange request. @@ -80,24 +81,27 @@ public XSTokenRequest setAdditionalAuthorizationAttributes(Map a * @deprecated in favor of @link{XsuaaTokenFlows} API */ @Override + @Deprecated public XSTokenRequest setType(int type) { - throw new AssertionError("This method is no longer needed in context of new XsuaaTokenFlows API."); + throw new AssertionError(UNSUPPORTED_INTF_METHOD_INFO); } /** * @deprecated in favor of @link{XsuaaTokenFlows} API */ @Override + @Deprecated public int getType() { - throw new AssertionError("This method is no longer needed in context of new XsuaaTokenFlows API."); + throw new AssertionError(UNSUPPORTED_INTF_METHOD_INFO); } /** * @deprecated in favor of @link{{@link #setSubdomain} )}} */ @Override + @Deprecated public XSTokenRequest setTokenEndpoint(URI tokenUri) { - throw new AssertionError("This method is no longer needed in context of new XsuaaTokenFlows API."); + throw new AssertionError(UNSUPPORTED_INTF_METHOD_INFO); } /** From ef003c2d51f31315ca1ae0120d194744b8f251e0 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Wed, 11 Sep 2019 17:18:57 +0200 Subject: [PATCH 13/22] cleanup TokenUrlUtils --- .../sample/spring/xsuaa/TestController.java | 2 +- .../BasicCredentialExtractorTest.java | 2 +- .../xsuaa/extractor/TokenUrlUtils.java | 86 +++++++------------ 3 files changed, 33 insertions(+), 57 deletions(-) diff --git a/samples/spring-security-basic-auth/src/main/java/sample/spring/xsuaa/TestController.java b/samples/spring-security-basic-auth/src/main/java/sample/spring/xsuaa/TestController.java index 4b35b9d73a..ccdc1d3c88 100644 --- a/samples/spring-security-basic-auth/src/main/java/sample/spring/xsuaa/TestController.java +++ b/samples/spring-security-basic-auth/src/main/java/sample/spring/xsuaa/TestController.java @@ -29,7 +29,7 @@ public class TestController { @GetMapping("/hello-token") public Map message(@AuthenticationPrincipal Token token) { - Map result = new HashMap<>(); + Map result = new HashMap(); result.put("grant type", token.getGrantType()); result.put("client id", token.getClientId()); result.put("subaccount id", token.getSubaccountId()); diff --git a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java index d28e142dbd..b1564f2caf 100644 --- a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java +++ b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java @@ -74,7 +74,7 @@ public void testClientCredentials() { authenticationMethods(AuthenticationMethod.CLIENT_CREDENTIALS)); request.addHeader("Authorization", "basic " + Base64.getEncoder().encodeToString("client1234:secret1234".getBytes())); - request.addHeader("X-Identity-Zone-Subdomain", "true"); + request.addHeader("X-Identity-Zone-Subdomain", "x-idz-subdomain"); request.setScheme("http"); request.setServerName("t1.cloudfoundry"); String token = extractor.resolve(request); diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenUrlUtils.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenUrlUtils.java index 0e95608bde..e56c6a406d 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenUrlUtils.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenUrlUtils.java @@ -1,16 +1,10 @@ -/** - * - */ package com.sap.cloud.security.xsuaa.extractor; -import static org.springframework.util.StringUtils.isEmpty; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Objects; +import java.net.URI; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.util.Assert; final class TokenUrlUtils { @@ -22,78 +16,60 @@ private TokenUrlUtils() { /** * Retrieves the URL for the token request *

- * + * * @param endpoint * endpoint * @param uaaUrl - * UAA-URL from VCAP-Services + * UAA-URL from Xsuaa Service binding * @param uaaDomain - * UAA-Domain from VCAP-Services + * UAA-Domain from Xsuaa Service binding * @param uaaSubDomain * UAA-Subdomain in case of Multi tenancy - * + * * @return token request URL */ - static String getMultiTenancyUrl(final String endpoint, final String uaaUrl, final String uaaDomain, + static String getMultiTenancyUrl(final String endpoint, final String uaaUrl, final String uaaDomain, final String uaaSubDomain) { - Objects.requireNonNull(uaaUrl, "URL must not be null"); - Objects.requireNonNull(uaaDomain, "Domain must not be null"); - Objects.requireNonNull(endpoint, "Endpoint must not be null"); + Assert.notNull(endpoint, "Endpoint must not be null"); + Assert.notNull(uaaUrl, "UAA URL must not be null"); + Assert.notNull(uaaDomain, "UAA Domain must not be null"); + Assert.notNull(uaaSubDomain, "UAA Subdomain must not be null"); - if (uaaSubDomain == null) { - return uaaUrl + endpoint; - } return TokenUrlUtils.getUrl(endpoint, uaaUrl, uaaDomain, uaaSubDomain); } - // TODO check how it can be replaced by oauthTokenUrl = new XsuaaDefaultEndpoints(uaaUrl).getTokenEndpoint().toString(); + /** + * Retrieves the URL for the token request + *

+ * + * @param endpoint + * endpoint + * @param uaaUrl + * UAA-URL from Xsuaa Service binding + * @param uaaDomain + * UAA-URL from Xsuaa Service binding + * + * @return token request URL + */ static String getOauthTokenUrl(final String endpoint, final String uaaUrl, final String uaaDomain) { - return TokenUrlUtils.getMultiTenancyUrl(endpoint, uaaUrl, uaaDomain, null); + Assert.notNull(endpoint, "Endpoint must not be null"); + Assert.notNull(uaaUrl, "UAA URL must not be null"); + Assert.notNull(uaaDomain, "UAA Domain must not be null"); + return uaaUrl + endpoint; } private static String getUrl(final String endpoint, final String uaaUrl, final String uaaDomain, String tenantSubDomain) { - Objects.requireNonNull(uaaUrl, "UAA-URL must not be null"); - Objects.requireNonNull(uaaDomain, "Domain must not be null"); - Objects.requireNonNull(tenantSubDomain, "SubDomain must not be null"); - Objects.requireNonNull(endpoint, "Endpoint must not be null"); - String tenantUaaDomain = tenantSubDomain + "." + uaaDomain; - URL url; - try { - url = new URL(uaaUrl); - } catch (MalformedURLException e) { - throw new RuntimeException("Cannot create valid URL from given UAA-Url" + uaaUrl); - } - String protocol = url.getProtocol(); + URI uri = URI.create(uaaUrl); + + String protocol = uri.getScheme(); String tenantTokenUrl = String.format("%s://%s", protocol, tenantUaaDomain + endpoint); logger.debug("Created tenant token URL " + tenantTokenUrl); return tenantTokenUrl; } - static String getHost(String path) { - URL url; - try { - url = new URL(path); - } catch (MalformedURLException e) { - throw new RuntimeException("Cannot create valid URL from given Url " + path); - } - return url.getHost(); - } - - static boolean isUrl(String url) { - if (isEmpty(url)) { - return false; - } - try { - new URL(url); - return true; - } catch (MalformedURLException e) { - return false; - } - } - } From 12da8fb2a2cc27c82a6863e115c15baaac21557f Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Wed, 11 Sep 2019 18:23:20 +0200 Subject: [PATCH 14/22] refactor TokenBrokerResolver APIs --- .../AuthenticationInformationExtractor.java | 3 - ...ultAuthenticationInformationExtractor.java | 9 --- .../security/xsuaa/extractor/TokenBroker.java | 14 +++- .../xsuaa/extractor/TokenBrokerResolver.java | 65 +++++++++++++++---- .../xsuaa/extractor/UaaTokenBroker.java | 25 ++++--- 5 files changed, 79 insertions(+), 37 deletions(-) diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractor.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractor.java index 4a5eb6b8ad..3b3689c4a9 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractor.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractor.java @@ -1,6 +1,3 @@ -/** - * - */ package com.sap.cloud.security.xsuaa.extractor; import javax.servlet.http.HttpServletRequest; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java index 5a1b475d28..fc4b917ca8 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java @@ -1,6 +1,3 @@ -/** - * - */ package com.sap.cloud.security.xsuaa.extractor; import javax.servlet.http.HttpServletRequest; @@ -23,15 +20,9 @@ class DefaultAuthenticationInformationExtractor implements AuthenticationInforma AuthenticationMethod.OAUTH2); DefaultAuthenticationInformationExtractor() { - super(); this.subDomain = null; } - DefaultAuthenticationInformationExtractor(String subDomain) { - super(); - this.subDomain = subDomain; - } - DefaultAuthenticationInformationExtractor(AuthenticationMethod... authenticationMethods) { this(null, authenticationMethods); } diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java index f73eb6c307..36e2a89d0a 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java @@ -1,8 +1,15 @@ package com.sap.cloud.security.xsuaa.extractor; +import java.net.URI; +import java.util.Map; + +import com.sap.cloud.security.xsuaa.client.ClientCredentials; + /** * - * @deprecated in favor of {@link com.sap.cloud.security.xsuaa.tokenflows.XsuaaTokenFlows} API. + * @deprecated in favor of + * {@link com.sap.cloud.security.xsuaa.client.OAuth2TokenService} + * API. */ @Deprecated public interface TokenBroker { @@ -19,7 +26,10 @@ public interface TokenBroker { * @return String * @throws TokenBrokerException * TokenBrokerException + * @deprecated in favor of + * {@link com.sap.cloud.security.xsuaa.client.OAuth2TokenService#retrieveAccessTokenViaClientCredentialsGrant(URI, ClientCredentials, String, Map)} */ + @Deprecated public String getAccessTokenFromClientCredentials(String tokenURL, String clientId, String clientSecret) throws TokenBrokerException; @@ -39,6 +49,8 @@ public String getAccessTokenFromClientCredentials(String tokenURL, String client * @return String * @throws TokenBrokerException * TokenBrokerException + * @deprecated in favor of + * {@link com.sap.cloud.security.xsuaa.client.OAuth2TokenService} */ public String getAccessTokenFromPasswordCredentials(String tokenURL, String clientId, String clientSecret, String username, String password) throws TokenBrokerException; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java index 5a31495c58..7d30f305d6 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java @@ -17,6 +17,7 @@ import org.springframework.util.StringUtils; import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.client.OAuth2TokenService; /** * Analyse authentication header and obtain token from UAA @@ -40,8 +41,6 @@ public class TokenBrokerResolver implements BearerTokenResolver { private Cache tokenCache; private TokenBroker tokenBroker; - private String uaaUrl; - private String uaaDomain; private AuthenticationInformationExtractor authenticationConfig; /** @@ -53,18 +52,46 @@ public class TokenBrokerResolver implements BearerTokenResolver { * Token-Broker for accessing the UAA * @param authenticationConfig * configured AuthenticationMethodConfiguration + * @deprecated in favor of + * {@link #TokenBrokerResolver(XsuaaServiceConfiguration, Cache, OAuth2TokenService, AuthenticationInformationExtractor)} */ - - TokenBrokerResolver(XsuaaServiceConfiguration configuration, Cache tokenCache, TokenBroker tokenBroker, + @Deprecated + public TokenBrokerResolver(XsuaaServiceConfiguration configuration, Cache tokenCache, TokenBroker tokenBroker, AuthenticationInformationExtractor authenticationConfig) { this.configuration = configuration; this.tokenCache = tokenCache; this.tokenBroker = tokenBroker; - this.uaaUrl = configuration.getUaaUrl(); - this.uaaDomain = configuration.getUaaDomain(); this.authenticationConfig = authenticationConfig; } + /** + * @param configuration + * - Configuration properties from environment. + * @param tokenCache + * - the Token-Cache. + * @param tokenService + * - the {@link OAuth2TokenService} used to execute the final + * request. + * @param authenticationConfig + * - configured AuthenticationMethodConfiguration. + */ + public TokenBrokerResolver(XsuaaServiceConfiguration configuration, Cache tokenCache, + OAuth2TokenService tokenService, + AuthenticationInformationExtractor authenticationConfig) { + this.configuration = configuration; + this.tokenCache = tokenCache; + this.tokenBroker = new UaaTokenBroker(tokenService); + this.authenticationConfig = authenticationConfig; + } + + /** + * @param configuration + * Configuration properties from environment + * @param tokenCache + * Token-Cache + * @param authenticationMethods + * list of supported authentication methods + */ public TokenBrokerResolver(XsuaaServiceConfiguration configuration, Cache tokenCache, AuthenticationMethod... authenticationMethods) { this(configuration, tokenCache, new UaaTokenBroker(), @@ -93,14 +120,7 @@ private String extractToken(HttpServletRequest request) throws TokenBrokerExcept checkTypes(authenticationMethods); - Optional subdomainResult = authenticationConfig.getSubdomain(request); - String oauthTokenUrl; - if (subdomainResult.isPresent()) { - oauthTokenUrl = TokenUrlUtils.getMultiTenancyUrl(OAUTH_TOKEN_PATH, uaaUrl, uaaDomain, - subdomainResult.get()); - } else { - oauthTokenUrl = TokenUrlUtils.getOauthTokenUrl(OAUTH_TOKEN_PATH, uaaUrl, uaaDomain); - } + String oauthTokenUrl = getOAuthTokenUrl(request); for (AuthenticationMethod credentialType : authenticationMethods) { Enumeration headers = request.getHeaders(AUTHORIZATION_HEADER); @@ -112,6 +132,23 @@ private String extractToken(HttpServletRequest request) throws TokenBrokerExcept return null; } + private String getOAuthTokenUrl(HttpServletRequest request) { + String uaaUrl = configuration.getUaaUrl(); + String uaaDomain = configuration.getUaaDomain(); + + Optional subdomainResult = authenticationConfig.getSubdomain(request); + + String oauthTokenUrl; + if (subdomainResult.isPresent()) { + oauthTokenUrl = TokenUrlUtils.getMultiTenancyUrl(OAUTH_TOKEN_PATH, uaaUrl, uaaDomain, + subdomainResult.get()); + } else { + oauthTokenUrl = TokenUrlUtils.getOauthTokenUrl(OAUTH_TOKEN_PATH, uaaUrl, uaaDomain); + } + + return oauthTokenUrl; + } + private String getBrokerToken(AuthenticationMethod credentialType, Enumeration headers, String oauthTokenUrl) throws TokenBrokerException { while (headers.hasMoreElements()) { diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java index 962f4559c3..fd88f974f7 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java @@ -18,32 +18,38 @@ import com.sap.cloud.security.xsuaa.client.ClientCredentials; import com.sap.cloud.security.xsuaa.client.OAuth2ServiceException; +import com.sap.cloud.security.xsuaa.client.OAuth2TokenService; import com.sap.cloud.security.xsuaa.client.XsuaaOAuth2TokenService; -/** - * @deprecated in favor of {@link XsuaaOAuth2TokenService} - */ -@Deprecated class UaaTokenBroker implements TokenBroker { private final static Log logger = LogFactory.getLog(UaaTokenBroker.class); private final RestTemplate restTemplate; - private XsuaaOAuth2TokenService oAuth2TokenService; - - UaaTokenBroker(RestTemplate restTemplate) { + private OAuth2TokenService oAuth2TokenService; + + /** + * @deprecated in favor of {@link #UaaTokenBroker(OAuth2TokenService)} + * @param restTemplate + */ + @Deprecated + public UaaTokenBroker(RestTemplate restTemplate) { this.restTemplate = restTemplate; this.oAuth2TokenService = new XsuaaOAuth2TokenService(restTemplate); } - UaaTokenBroker() { + public UaaTokenBroker() { this(new RestTemplate()); } + public UaaTokenBroker(OAuth2TokenService tokenService) { + this.restTemplate = new RestTemplate(); + oAuth2TokenService = tokenService; + } + @Override public String getAccessTokenFromClientCredentials(String tokenURL, String clientId, String clientSecret) throws TokenBrokerException { - try { return oAuth2TokenService.retrieveAccessTokenViaClientCredentialsGrant( URI.create(tokenURL), new ClientCredentials(clientId, clientSecret), null, null).getAccessToken(); @@ -53,7 +59,6 @@ public String getAccessTokenFromClientCredentials(String tokenURL, String client } @Override - // TODO move to XsuaaOAuth2TokenService public String getAccessTokenFromPasswordCredentials(String tokenURL, String clientId, String clientSecret, String username, String password) throws TokenBrokerException { try { From 07835209f90bbca54dfb0586baf4a3bbca665c26 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Thu, 12 Sep 2019 12:21:11 +0200 Subject: [PATCH 15/22] cleanup TokenBrokerResolver --- .../xsuaa/extractor/TokenBrokerResolver.java | 100 +++++++++--------- .../xsuaa/client/ClientCredentials.java | 6 ++ .../xsuaa/client/ClientCredentialsTest.java | 19 +++- 3 files changed, 72 insertions(+), 53 deletions(-) diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java index 7d30f305d6..f4c21658f3 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java @@ -13,10 +13,12 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cache.Cache; +import org.springframework.lang.Nullable; import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver; import org.springframework.util.StringUtils; import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; +import com.sap.cloud.security.xsuaa.client.ClientCredentials; import com.sap.cloud.security.xsuaa.client.OAuth2TokenService; /** @@ -90,7 +92,8 @@ public TokenBrokerResolver(XsuaaServiceConfiguration configuration, Cache tokenC * @param tokenCache * Token-Cache * @param authenticationMethods - * list of supported authentication methods + * list of supported authentication methods. + * Choose either {@link AuthenticationMethod#BASIC} or {@link AuthenticationMethod#CLIENT_CREDENTIALS}. */ public TokenBrokerResolver(XsuaaServiceConfiguration configuration, Cache tokenCache, AuthenticationMethod... authenticationMethods) { @@ -98,13 +101,6 @@ public TokenBrokerResolver(XsuaaServiceConfiguration configuration, Cache tokenC new DefaultAuthenticationInformationExtractor(authenticationMethods)); } - private void checkTypes(List authenticationMethods) { - if (authenticationMethods.contains(AuthenticationMethod.BASIC) - && authenticationMethods.contains(AuthenticationMethod.CLIENT_CREDENTIALS)) { - throw new IllegalArgumentException("Use either CLIENT_CREDENTIALS or BASIC"); - } - } - @Override public String resolve(HttpServletRequest request) { try { @@ -132,6 +128,13 @@ private String extractToken(HttpServletRequest request) throws TokenBrokerExcept return null; } + private void checkTypes(List authenticationMethods) { + if (authenticationMethods.contains(AuthenticationMethod.BASIC) + && authenticationMethods.contains(AuthenticationMethod.CLIENT_CREDENTIALS)) { + throw new IllegalArgumentException("Use either CLIENT_CREDENTIALS or BASIC"); + } + } + private String getOAuthTokenUrl(HttpServletRequest request) { String uaaUrl = configuration.getUaaUrl(); String uaaDomain = configuration.getUaaDomain(); @@ -151,53 +154,42 @@ private String getOAuthTokenUrl(HttpServletRequest request) { private String getBrokerToken(AuthenticationMethod credentialType, Enumeration headers, String oauthTokenUrl) throws TokenBrokerException { + ClientCredentials clientCredentials = new ClientCredentials(configuration.getClientId(), configuration.getClientSecret()); while (headers.hasMoreElements()) { String header = headers.nextElement(); switch (credentialType) { case OAUTH2: - return extractCredential(BEARER_TYPE, header); + return extractAuthorizationHeader(BEARER_TYPE, header); case BASIC: - String clientId = configuration.getClientId(); - String clientSecret = configuration.getClientSecret(); - if (clientId == null) { - throw new TokenBrokerException("Missing clientId"); - } - if (clientSecret == null) { - throw new TokenBrokerException("Missing client secret"); - } - String basicCredential = extractCredential(BASIC_CREDENTIAL, header); - if (basicCredential != null) { - final String[] credentialDetails = obtainCredentialDetails(basicCredential); - if (credentialDetails.length == 2) { - String cacheKey = createSecureHash(oauthTokenUrl, clientId, clientSecret, credentialDetails[0], - credentialDetails[1]); - String storedToken = tokenCache.get(cacheKey, String.class); - if (storedToken == null) { - String token = tokenBroker.getAccessTokenFromPasswordCredentials(oauthTokenUrl, clientId, - clientSecret, credentialDetails[0], credentialDetails[1]); - tokenCache.put(cacheKey, token); - return token; - } else { - return storedToken; - } + String basicAuthHeader = extractAuthorizationHeader(BASIC_CREDENTIAL, header); + ClientCredentials userCredentialsFromHeader = createCredentialsFromBasicAuthorizationHeader(basicAuthHeader); + if (userCredentialsFromHeader != null) { + String cacheKey = createSecureHash(oauthTokenUrl, clientCredentials.toString(), userCredentialsFromHeader.toString()); + String cachedToken = tokenCache.get(cacheKey, String.class); + if (cachedToken != null) { + return cachedToken; + } else { + String token = tokenBroker.getAccessTokenFromPasswordCredentials(oauthTokenUrl, clientCredentials.getId(), + clientCredentials.getSecret(), userCredentialsFromHeader.getId(), userCredentialsFromHeader.getSecret()); + tokenCache.put(cacheKey, token); + return token; } } break; case CLIENT_CREDENTIALS: - String clientCredential = extractCredential(BASIC_CREDENTIAL, header); - if (clientCredential != null) { - final String[] credentialDetails = obtainCredentialDetails(clientCredential); - if (credentialDetails.length == 2) { - String cacheKey = createSecureHash(oauthTokenUrl, credentialDetails[0], credentialDetails[1]); - String storedToken = tokenCache.get(cacheKey, String.class); - if (storedToken == null) { - String token = tokenBroker.getAccessTokenFromClientCredentials(oauthTokenUrl, - credentialDetails[0], credentialDetails[1]); - tokenCache.put(cacheKey, token); - return token; - } else { - return storedToken; - } + String clientCredentialsAuthHeader = extractAuthorizationHeader(BASIC_CREDENTIAL, header); + ClientCredentials clientCredentialsFromHeader = createCredentialsFromBasicAuthorizationHeader(clientCredentialsAuthHeader); + if (clientCredentialsFromHeader != null) { + String cacheKey = createSecureHash(oauthTokenUrl, clientCredentialsFromHeader.toString()); + String cachedToken = tokenCache.get(cacheKey, String.class); + if (cachedToken != null) { + return cachedToken; + } else { + String token = tokenBroker + .getAccessTokenFromClientCredentials(oauthTokenUrl, clientCredentialsFromHeader.getId(), + clientCredentialsFromHeader.getSecret()); + tokenCache.put(cacheKey, token); + return token; } } break; @@ -208,14 +200,20 @@ private String getBrokerToken(AuthenticationMethod credentialType, Enumeration Date: Thu, 12 Sep 2019 11:21:35 +0200 Subject: [PATCH 16/22] SECAUTH-479: [Token-Clien] Support Password-token in OAuth2TokenService --- .../xsuaa/client/OAuth2TokenService.java | 24 +- .../client/OAuth2TokenServiceConstants.java | 4 + .../xsuaa/client/XsuaaOAuth2TokenService.java | 214 ++++++++++-------- .../XsuaaOAuth2TokenServicePasswordTest.java | 197 ++++++++++++++++ 4 files changed, 341 insertions(+), 98 deletions(-) create mode 100644 token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServicePasswordTest.java diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java index 00c8421eec..ca9ce7adbf 100644 --- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java +++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java @@ -23,7 +23,6 @@ public interface OAuth2TokenService { * supplying a subdomain (tenant). * @param optionalParameters * optional request parameters, can be null. - * * @return the OAuth2AccessToken. * @throws OAuth2ServiceException * in case of an error during the http request. @@ -50,7 +49,6 @@ OAuth2TokenResponse retrieveAccessTokenViaClientCredentialsGrant(URI tokenEndpoi * supplying a subdomain (tenant). * @param optionalParameters * optional request parameters, can be null. - * * @return the OAuth2AccessToken. * @throws OAuth2ServiceException * in case of an error during the http request. @@ -82,4 +80,26 @@ OAuth2TokenResponse retrieveAccessTokenViaUserTokenGrant(URI tokenEndpointUri, */ OAuth2TokenResponse retrieveAccessTokenViaRefreshToken(URI tokenEndpointUri, ClientCredentials clientCredentials, String refreshToken, @Nullable String subdomain) throws OAuth2ServiceException; + + /** + * @param tokenEndpointUri + * the token endpoint URI. + * @param clientCredentials + * the client id and secret of the OAuth client, the recipient of the + * token. + * @param username + * the username for the user trying to get a token + * @param password + * the password for the user trying to get a token + * @param subdomain + * optionally indicates what Identity Zone this request goes to by + * supplying a subdomain (tenant). + * @param optionalParameters + * optional request parameters, can be null. + * @return + * @throws OAuth2ServiceException + */ + OAuth2TokenResponse retrieveAccessTokenViaPasswordGrant(URI tokenEndpointUri, ClientCredentials clientCredentials, + String username, String password, String subdomain, Map optionalParameters) + throws OAuth2ServiceException; } diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenServiceConstants.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenServiceConstants.java index 3fcd5bd420..bf41a3f0c9 100644 --- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenServiceConstants.java +++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenServiceConstants.java @@ -11,11 +11,15 @@ private OAuth2TokenServiceConstants() { public static final String REFRESH_TOKEN = "refresh_token"; public static final String CLIENT_ID = "client_id"; public static final String CLIENT_SECRET = "client_secret"; + public static final String USERNAME = "username"; + public static final String PASSWORD = "password"; public static final String GRANT_TYPE = "grant_type"; public static final String GRANT_TYPE_USER_TOKEN = "user_token"; public static final String GRANT_TYPE_REFRESH_TOKEN = "refresh_token"; public static final String GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials"; + public static final String GRANT_TYPE_PASSWORD = "password"; + public static final String TOKEN_TYPE_OPAQUE = "opaque"; public static final String PARAMETER_CLIENT_ID = "client_id"; 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 6df2002750..7d9455fed5 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 @@ -1,21 +1,5 @@ package com.sap.cloud.security.xsuaa.client; -import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.ACCESS_TOKEN; -import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.CLIENT_ID; -import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.CLIENT_SECRET; -import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.EXPIRES_IN; -import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.GRANT_TYPE; -import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.GRANT_TYPE_CLIENT_CREDENTIALS; -import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.GRANT_TYPE_REFRESH_TOKEN; -import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.GRANT_TYPE_USER_TOKEN; -import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.PARAMETER_CLIENT_ID; -import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.REFRESH_TOKEN; - -import java.net.URI; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpEntity; @@ -34,16 +18,99 @@ import org.springframework.web.util.UriBuilder; import org.springframework.web.util.UriComponentsBuilder; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.*; + public class XsuaaOAuth2TokenService implements OAuth2TokenService { - private final RestOperations restOperations; private static Logger logger = LoggerFactory.getLogger(XsuaaOAuth2TokenService.class); + private final RestOperations restOperations; public XsuaaOAuth2TokenService(@NonNull RestOperations restOperations) { Assert.notNull(restOperations, "restOperations is required"); this.restOperations = restOperations; } + /** + * Utility method that replaces the subdomain of the URI with the given + * subdomain. + * + * @param uri + * the URI to be replaced. + * @param subdomain + * of the tenant. + * @return the URI with the replaced subdomain or the passed URI in case a + * replacement was not possible. + */ + static URI replaceSubdomain(@NonNull URI uri, @Nullable String subdomain) { + Assert.notNull(uri, "the uri parameter must not be null"); + if (StringUtils.hasText(subdomain) && uri.getHost().contains(".")) { + UriBuilder builder = UriComponentsBuilder.newInstance().scheme(uri.getScheme()) + .host(subdomain + uri.getHost().substring(uri.getHost().indexOf('.'))).port(uri.getPort()) + .path(uri.getPath()); + return uri.resolve(builder.build()); + } + logger.warn("the subdomain of the URI '{}' is not replaced by subdomain '{}'", uri, subdomain); + return uri; + } + + /** + * Creates a copy of the given map or an new empty map of type MultiValueMap. + * + * @return a new @link{MultiValueMap} that contains all entries of the optional + * map. + */ + private static MultiValueMap copyIntoForm(Map parameters) { + MultiValueMap formData = new LinkedMultiValueMap(); + if (parameters != null) { + parameters.forEach(formData::add); + } + return formData; + } + + /** + * Creates the set of HTTP headers with client-credentials basic authentication + * header. + * + * @return the HTTP headers. + */ + private static HttpHeaders createHeadersWithoutAuthorization() { + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + return headers; + } + + /** + * Creates the set of HTTP headers with Authorization Bearer header. + * + * @return the HTTP headers. + */ + private static HttpHeaders createHeadersWithAuthorization(String token) { + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + addAuthorizationBearerHeader(headers, token); + return headers; + } + + /** + * Adds the {@code Authorization: Bearer } header to the set of headers. + * + * @param headers + * - the set of headers to add the header to. + * @param token + * - the token which should be part of the header. + */ + static void addAuthorizationBearerHeader(HttpHeaders headers, String token) { + final String AUTHORIZATION_BEARER_TOKEN_FORMAT = "Bearer %s"; + headers.add(HttpHeaders.AUTHORIZATION, String.format(AUTHORIZATION_BEARER_TOKEN_FORMAT, token)); + } + @Override public OAuth2TokenResponse retrieveAccessTokenViaClientCredentialsGrant(@NonNull URI tokenEndpointUri, @NonNull ClientCredentials clientCredentials, @@ -55,8 +122,7 @@ public OAuth2TokenResponse retrieveAccessTokenViaClientCredentialsGrant(@NonNull // build parameters Map parameters = new HashMap<>(); parameters.put(GRANT_TYPE, GRANT_TYPE_CLIENT_CREDENTIALS); - parameters.put(CLIENT_ID, clientCredentials.getId()); - parameters.put(CLIENT_SECRET, clientCredentials.getSecret()); + addClientCredentials(clientCredentials, parameters); if (optionalParameters != null) { optionalParameters.forEach(parameters::putIfAbsent); } @@ -102,8 +168,7 @@ public OAuth2TokenResponse retrieveAccessTokenViaRefreshToken(@NonNull URI token Map parameters = new HashMap<>(); parameters.put(GRANT_TYPE, GRANT_TYPE_REFRESH_TOKEN); parameters.put(REFRESH_TOKEN, refreshToken); - parameters.put(CLIENT_ID, clientCredentials.getId()); - parameters.put(CLIENT_SECRET, clientCredentials.getSecret()); + addClientCredentials(clientCredentials, parameters); // build header HttpHeaders headers = createHeadersWithoutAuthorization(); @@ -111,29 +176,41 @@ public OAuth2TokenResponse retrieveAccessTokenViaRefreshToken(@NonNull URI token return requestAccessToken(replaceSubdomain(tokenEndpointUri, subdomain), headers, copyIntoForm(parameters)); } - /** - * Utility method that replaces the subdomain of the URI with the given - * subdomain. - * - * @param uri - * the URI to be replaced. - * @param subdomain - * of the tenant. - * @return the URI with the replaced subdomain or the passed URI in case a - * replacement was not possible. - */ - static URI replaceSubdomain(@NonNull URI uri, @Nullable String subdomain) { - Assert.notNull(uri, "the uri parameter must not be null"); - if (StringUtils.hasText(subdomain) && uri.getHost().contains(".")) { - UriBuilder builder = UriComponentsBuilder.newInstance().scheme(uri.getScheme()) - .host(subdomain + uri.getHost().substring(uri.getHost().indexOf('.'))).port(uri.getPort()) - .path(uri.getPath()); - return uri.resolve(builder.build()); + @Override + public OAuth2TokenResponse retrieveAccessTokenViaPasswordGrant(@NonNull URI tokenEndpoint, + @NonNull ClientCredentials clientCredentials, @NonNull String username, @NonNull String password, + @Nullable String subdomain, @Nullable Map optionalParameters) + throws OAuth2ServiceException { + Assert.notNull(tokenEndpoint, "tokenEndpoint is required"); + Assert.notNull(clientCredentials, "clientCredentials are required"); + Assert.notNull(username, "username is required"); + Assert.notNull(password, "password is required"); + + Map parameters = new HashMap<>(); + parameters.put(GRANT_TYPE, GRANT_TYPE_PASSWORD); + parameters.put(USERNAME, username); + parameters.put(PASSWORD, password); + addClientCredentials(clientCredentials, parameters); + + if (optionalParameters != null) { + optionalParameters.forEach(parameters::putIfAbsent); } - logger.warn("the subdomain of the URI '{}' is not replaced by subdomain '{}'", uri, subdomain); - return uri; + + HttpHeaders headers = createHeadersWithoutAuthorization(); + + return requestAccessToken(replaceSubdomain(tokenEndpoint, subdomain), headers, copyIntoForm(parameters)); } + private void addClientCredentials(ClientCredentials clientCredentials, + Map parameters) { + parameters.put(CLIENT_ID, clientCredentials.getId()); + parameters.put(CLIENT_SECRET, clientCredentials.getSecret()); + } + + /** + * common utilities + **/ + private OAuth2TokenResponse requestAccessToken(URI tokenEndpointUri, HttpHeaders headers, MultiValueMap parameters) throws OAuth2ServiceException { @@ -172,59 +249,4 @@ private OAuth2TokenResponse requestAccessToken(URI tokenEndpointUri, HttpHeaders String refreshToken = accessTokenMap.get(REFRESH_TOKEN); return new OAuth2TokenResponse(accessToken, expiresIn, refreshToken); } - - /** - * Creates a copy of the given map or an new empty map of type MultiValueMap. - * - * @return a new @link{MultiValueMap} that contains all entries of the optional - * map. - */ - private static MultiValueMap copyIntoForm(Map parameters) { - MultiValueMap formData = new LinkedMultiValueMap(); - if (parameters != null) { - parameters.forEach(formData::add); - } - return formData; - } - - /** - * Creates the set of HTTP headers with client-credentials basic authentication - * header. - * - * @return the HTTP headers. - */ - private static HttpHeaders createHeadersWithoutAuthorization() { - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - return headers; - } - - /** - * Creates the set of HTTP headers with Authorization Bearer header. - * - * @return the HTTP headers. - */ - private static HttpHeaders createHeadersWithAuthorization(String token) { - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - addAuthorizationBearerHeader(headers, token); - return headers; - } - - /** common utilities **/ - - /** - * Adds the {@code Authorization: Bearer } header to the set of headers. - * - * @param headers - * - the set of headers to add the header to. - * @param token - * - the token which should be part of the header. - */ - static void addAuthorizationBearerHeader(HttpHeaders headers, String token) { - final String AUTHORIZATION_BEARER_TOKEN_FORMAT = "Bearer %s"; - headers.add(HttpHeaders.AUTHORIZATION, String.format(AUTHORIZATION_BEARER_TOKEN_FORMAT, token)); - } } diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServicePasswordTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServicePasswordTest.java new file mode 100644 index 0000000000..6f20770cac --- /dev/null +++ b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServicePasswordTest.java @@ -0,0 +1,197 @@ +package com.sap.cloud.security.xsuaa.client; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.http.*; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestOperations; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class XsuaaOAuth2TokenServicePasswordTest { + + private OAuth2TokenService cut; + + private String clientSecret = "test321"; + private String clientId = "theClientId"; + private String password = "test123"; + private String username = "bob"; + private String subdomain = "subdomain"; + private ClientCredentials clientCredentials = new ClientCredentials(clientId, clientSecret); + private URI tokenEndpoint = URI.create("https://subdomain.myauth.server.com/oauth/token"); + private Map optionalParameters; + private Map response; + + @Mock + private RestOperations mockRestOperations; + + @Before + public void setup() { + response = new HashMap(); + response.putIfAbsent(ACCESS_TOKEN, "f529.dd6e30.d454677322aaabb0"); + response.putIfAbsent(EXPIRES_IN, "43199"); + when(mockRestOperations.postForEntity(any(), any(), any())) + .thenReturn(ResponseEntity.status(200).body(response)); + optionalParameters = new HashMap<>(); + cut = new XsuaaOAuth2TokenService(mockRestOperations); + } + + @Test(expected = OAuth2ServiceException.class) + public void retrieveToken_httpStatusUnauthorized_throwsException() throws OAuth2ServiceException { + throwExceptionOnPost(HttpStatus.UNAUTHORIZED); + + cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + username, password, null, null); + } + + @Test(expected = OAuth2ServiceException.class) + public void retrieveToken_httpStatusNotOk_throwsException() throws OAuth2ServiceException { + throwExceptionOnPost(HttpStatus.BAD_REQUEST); + + cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + username, password, null, null); + } + + @Test + public void retrieveToken_requiredParametersMissing_throwsException() { + assertThatThrownBy(() -> cut.retrieveAccessTokenViaPasswordGrant(null, clientCredentials, + username, password, subdomain, optionalParameters)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, null, + username, password, subdomain, optionalParameters)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + null, password, subdomain, optionalParameters)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + username, null, subdomain, optionalParameters)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void retrieveToken_callsTokenEndpoint() throws OAuth2ServiceException { + cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + username, password, null, null); + + Mockito.verify(mockRestOperations, times(1)) + .postForEntity(eq(tokenEndpoint), any(), any()); + } + + @Test + public void retrieveToken_setsCorrectGrantType() throws OAuth2ServiceException { + cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + username, password, null, null); + + ArgumentCaptor>> requestEntityCaptor = captureRequestEntity(); + + String actualGrantType = valueOfParameter(GRANT_TYPE, requestEntityCaptor); + assertThat(actualGrantType).isEqualTo(GRANT_TYPE_PASSWORD); + } + + @Test + public void retrieveToken_setsUsername() throws OAuth2ServiceException { + cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + username, password, null, null); + + ArgumentCaptor>> requestEntityCaptor = captureRequestEntity(); + + assertThat(valueOfParameter(USERNAME, requestEntityCaptor)).isEqualTo(username); + } + + @Test + public void retrieveToken_setsPassword() throws OAuth2ServiceException { + cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + username, password, null, null); + + ArgumentCaptor>> requestEntityCaptor = captureRequestEntity(); + + assertThat(valueOfParameter(PASSWORD, requestEntityCaptor)).isEqualTo(password); + } + + @Test + public void retrieveToken_setsClientCredentials() throws OAuth2ServiceException { + cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + username, password, null, null); + + ArgumentCaptor>> requestEntityCaptor = captureRequestEntity(); + + assertThat(valueOfParameter(CLIENT_ID, requestEntityCaptor)).isEqualTo(clientCredentials.getId()); + assertThat(valueOfParameter(CLIENT_SECRET, requestEntityCaptor)).isEqualTo(clientCredentials.getSecret()); + } + + @Test + public void retrieveToken_setsOptionalParameters() throws OAuth2ServiceException { + String tokenFormatParameterKey = "token_format"; + String tokenFormat = "opaque"; + String loginHintParameterKey = "login_hint"; + String loginHint = "origin"; + + optionalParameters.put(tokenFormatParameterKey, tokenFormat); + optionalParameters.put(loginHintParameterKey, loginHint); + + cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + username, password, null, optionalParameters); + + ArgumentCaptor>> requestEntityCaptor = captureRequestEntity(); + assertThat(valueOfParameter(tokenFormatParameterKey, requestEntityCaptor)).isEqualTo(tokenFormat); + assertThat(valueOfParameter(loginHintParameterKey, requestEntityCaptor)).isEqualTo(loginHint); + } + + @Test + public void retrieveToken_setsCorrectHeaders() throws OAuth2ServiceException { + cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + username, password, null, optionalParameters); + + ArgumentCaptor>> requestEntityCaptor = captureRequestEntity(); + HttpHeaders headers = requestEntityCaptor.getValue().getHeaders(); + + assertThat(headers.getAccept()).containsExactly(MediaType.APPLICATION_JSON); + assertThat(headers.getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED); + } + + @Test + public void retrieveToken() throws OAuth2ServiceException { + OAuth2TokenResponse actualResponse = cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientCredentials, + username, password, null, null); + + assertThat(actualResponse.getAccessToken()).isEqualTo(response.get(ACCESS_TOKEN)); + + assertThat(actualResponse.getExpiredAtDate()).isNotNull(); + } + + private ArgumentCaptor>> captureRequestEntity() { + ArgumentCaptor>> requestEntityCaptor = ArgumentCaptor + .forClass(HttpEntity.class); + Mockito.verify(mockRestOperations, times(1)) + .postForEntity( + eq(tokenEndpoint), + requestEntityCaptor.capture(), + eq(Map.class)); + return requestEntityCaptor; + } + + private String valueOfParameter( + String parameterKey, ArgumentCaptor>> requestEntityCaptor) { + MultiValueMap body = requestEntityCaptor.getValue().getBody(); + return body.getFirst(parameterKey); + } + + private void throwExceptionOnPost(HttpStatus unauthorized) { + when(mockRestOperations.postForEntity(any(), any(), any())) + .thenThrow(new HttpClientErrorException(unauthorized)); + } + +} \ No newline at end of file From cff81ef5f5e76901f0fa2c5ddcbb1cca8dd2c965 Mon Sep 17 00:00:00 2001 From: Daniel Hassler Date: Thu, 12 Sep 2019 11:49:09 +0200 Subject: [PATCH 17/22] SECAUTH-479: [Token-Clien] Improved documentation, moved method down --- .../security/xsuaa/client/OAuth2TokenService.java | 6 ++++-- .../xsuaa/client/XsuaaOAuth2TokenService.java | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java index ca9ce7adbf..c1296ed17d 100644 --- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java +++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java @@ -96,10 +96,12 @@ OAuth2TokenResponse retrieveAccessTokenViaRefreshToken(URI tokenEndpointUri, Cli * supplying a subdomain (tenant). * @param optionalParameters * optional request parameters, can be null. - * @return + * @return the OAuth2AccessToken * @throws OAuth2ServiceException + * in case of an error during the http request. */ OAuth2TokenResponse retrieveAccessTokenViaPasswordGrant(URI tokenEndpointUri, ClientCredentials clientCredentials, - String username, String password, String subdomain, Map optionalParameters) + String username, String password, @Nullable String subdomain, + @Nullable Map optionalParameters) throws OAuth2ServiceException; } 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 7d9455fed5..775a2eb9e2 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 @@ -201,12 +201,6 @@ public OAuth2TokenResponse retrieveAccessTokenViaPasswordGrant(@NonNull URI toke return requestAccessToken(replaceSubdomain(tokenEndpoint, subdomain), headers, copyIntoForm(parameters)); } - private void addClientCredentials(ClientCredentials clientCredentials, - Map parameters) { - parameters.put(CLIENT_ID, clientCredentials.getId()); - parameters.put(CLIENT_SECRET, clientCredentials.getSecret()); - } - /** * common utilities **/ @@ -249,4 +243,10 @@ private OAuth2TokenResponse requestAccessToken(URI tokenEndpointUri, HttpHeaders String refreshToken = accessTokenMap.get(REFRESH_TOKEN); return new OAuth2TokenResponse(accessToken, expiresIn, refreshToken); } + + private void addClientCredentials(ClientCredentials clientCredentials, + Map parameters) { + parameters.put(CLIENT_ID, clientCredentials.getId()); + parameters.put(CLIENT_SECRET, clientCredentials.getSecret()); + } } From 3cc20cd00d8a15819ff4c86a9785e838a338ec3b Mon Sep 17 00:00:00 2001 From: Daniel Hassler Date: Thu, 12 Sep 2019 13:56:47 +0200 Subject: [PATCH 18/22] SECAUTH-479: [Token-Clien] Restructured code --- .../xsuaa/client/OAuth2TokenService.java | 4 +- .../xsuaa/client/XsuaaOAuth2TokenService.java | 164 +++++++++--------- 2 files changed, 83 insertions(+), 85 deletions(-) diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java index c1296ed17d..242e8e84fc 100644 --- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java +++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenService.java @@ -102,6 +102,6 @@ OAuth2TokenResponse retrieveAccessTokenViaRefreshToken(URI tokenEndpointUri, Cli */ OAuth2TokenResponse retrieveAccessTokenViaPasswordGrant(URI tokenEndpointUri, ClientCredentials clientCredentials, String username, String password, @Nullable String subdomain, - @Nullable Map optionalParameters) - throws OAuth2ServiceException; + @Nullable Map optionalParameters) throws OAuth2ServiceException; + } 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 775a2eb9e2..0137aafa35 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 @@ -27,90 +27,14 @@ public class XsuaaOAuth2TokenService implements OAuth2TokenService { - private static Logger logger = LoggerFactory.getLogger(XsuaaOAuth2TokenService.class); private final RestOperations restOperations; + private static Logger logger = LoggerFactory.getLogger(XsuaaOAuth2TokenService.class); public XsuaaOAuth2TokenService(@NonNull RestOperations restOperations) { Assert.notNull(restOperations, "restOperations is required"); this.restOperations = restOperations; } - /** - * Utility method that replaces the subdomain of the URI with the given - * subdomain. - * - * @param uri - * the URI to be replaced. - * @param subdomain - * of the tenant. - * @return the URI with the replaced subdomain or the passed URI in case a - * replacement was not possible. - */ - static URI replaceSubdomain(@NonNull URI uri, @Nullable String subdomain) { - Assert.notNull(uri, "the uri parameter must not be null"); - if (StringUtils.hasText(subdomain) && uri.getHost().contains(".")) { - UriBuilder builder = UriComponentsBuilder.newInstance().scheme(uri.getScheme()) - .host(subdomain + uri.getHost().substring(uri.getHost().indexOf('.'))).port(uri.getPort()) - .path(uri.getPath()); - return uri.resolve(builder.build()); - } - logger.warn("the subdomain of the URI '{}' is not replaced by subdomain '{}'", uri, subdomain); - return uri; - } - - /** - * Creates a copy of the given map or an new empty map of type MultiValueMap. - * - * @return a new @link{MultiValueMap} that contains all entries of the optional - * map. - */ - private static MultiValueMap copyIntoForm(Map parameters) { - MultiValueMap formData = new LinkedMultiValueMap(); - if (parameters != null) { - parameters.forEach(formData::add); - } - return formData; - } - - /** - * Creates the set of HTTP headers with client-credentials basic authentication - * header. - * - * @return the HTTP headers. - */ - private static HttpHeaders createHeadersWithoutAuthorization() { - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - return headers; - } - - /** - * Creates the set of HTTP headers with Authorization Bearer header. - * - * @return the HTTP headers. - */ - private static HttpHeaders createHeadersWithAuthorization(String token) { - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - addAuthorizationBearerHeader(headers, token); - return headers; - } - - /** - * Adds the {@code Authorization: Bearer } header to the set of headers. - * - * @param headers - * - the set of headers to add the header to. - * @param token - * - the token which should be part of the header. - */ - static void addAuthorizationBearerHeader(HttpHeaders headers, String token) { - final String AUTHORIZATION_BEARER_TOKEN_FORMAT = "Bearer %s"; - headers.add(HttpHeaders.AUTHORIZATION, String.format(AUTHORIZATION_BEARER_TOKEN_FORMAT, token)); - } - @Override public OAuth2TokenResponse retrieveAccessTokenViaClientCredentialsGrant(@NonNull URI tokenEndpointUri, @NonNull ClientCredentials clientCredentials, @@ -122,7 +46,7 @@ public OAuth2TokenResponse retrieveAccessTokenViaClientCredentialsGrant(@NonNull // build parameters Map parameters = new HashMap<>(); parameters.put(GRANT_TYPE, GRANT_TYPE_CLIENT_CREDENTIALS); - addClientCredentials(clientCredentials, parameters); + addClientCredentialsToParameters(clientCredentials, parameters); if (optionalParameters != null) { optionalParameters.forEach(parameters::putIfAbsent); } @@ -168,7 +92,7 @@ public OAuth2TokenResponse retrieveAccessTokenViaRefreshToken(@NonNull URI token Map parameters = new HashMap<>(); parameters.put(GRANT_TYPE, GRANT_TYPE_REFRESH_TOKEN); parameters.put(REFRESH_TOKEN, refreshToken); - addClientCredentials(clientCredentials, parameters); + addClientCredentialsToParameters(clientCredentials, parameters); // build header HttpHeaders headers = createHeadersWithoutAuthorization(); @@ -190,7 +114,7 @@ public OAuth2TokenResponse retrieveAccessTokenViaPasswordGrant(@NonNull URI toke parameters.put(GRANT_TYPE, GRANT_TYPE_PASSWORD); parameters.put(USERNAME, username); parameters.put(PASSWORD, password); - addClientCredentials(clientCredentials, parameters); + addClientCredentialsToParameters(clientCredentials, parameters); if (optionalParameters != null) { optionalParameters.forEach(parameters::putIfAbsent); @@ -202,8 +126,27 @@ public OAuth2TokenResponse retrieveAccessTokenViaPasswordGrant(@NonNull URI toke } /** - * common utilities - **/ + * Utility method that replaces the subdomain of the URI with the given + * subdomain. + * + * @param uri + * the URI to be replaced. + * @param subdomain + * of the tenant. + * @return the URI with the replaced subdomain or the passed URI in case a + * replacement was not possible. + */ + static URI replaceSubdomain(@NonNull URI uri, @Nullable String subdomain) { + Assert.notNull(uri, "the uri parameter must not be null"); + if (StringUtils.hasText(subdomain) && uri.getHost().contains(".")) { + UriBuilder builder = UriComponentsBuilder.newInstance().scheme(uri.getScheme()) + .host(subdomain + uri.getHost().substring(uri.getHost().indexOf('.'))).port(uri.getPort()) + .path(uri.getPath()); + return uri.resolve(builder.build()); + } + logger.warn("the subdomain of the URI '{}' is not replaced by subdomain '{}'", uri, subdomain); + return uri; + } private OAuth2TokenResponse requestAccessToken(URI tokenEndpointUri, HttpHeaders headers, MultiValueMap parameters) throws OAuth2ServiceException { @@ -244,9 +187,64 @@ private OAuth2TokenResponse requestAccessToken(URI tokenEndpointUri, HttpHeaders return new OAuth2TokenResponse(accessToken, expiresIn, refreshToken); } - private void addClientCredentials(ClientCredentials clientCredentials, + /** + * Creates a copy of the given map or an new empty map of type MultiValueMap. + * + * @return a new @link{MultiValueMap} that contains all entries of the optional + * map. + */ + private static MultiValueMap copyIntoForm(Map parameters) { + MultiValueMap formData = new LinkedMultiValueMap(); + if (parameters != null) { + parameters.forEach(formData::add); + } + return formData; + } + + /** + * Creates the set of HTTP headers with client-credentials basic authentication + * header. + * + * @return the HTTP headers. + */ + private static HttpHeaders createHeadersWithoutAuthorization() { + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + return headers; + } + + /** + * Creates the set of HTTP headers with Authorization Bearer header. + * + * @return the HTTP headers. + */ + private static HttpHeaders createHeadersWithAuthorization(String token) { + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + addAuthorizationBearerHeader(headers, token); + return headers; + } + + private void addClientCredentialsToParameters(ClientCredentials clientCredentials, Map parameters) { parameters.put(CLIENT_ID, clientCredentials.getId()); parameters.put(CLIENT_SECRET, clientCredentials.getSecret()); } + + /** common utilities **/ + + /** + * Adds the {@code Authorization: Bearer } header to the set of headers. + * + * @param headers + * - the set of headers to add the header to. + * @param token + * - the token which should be part of the header. + */ + static void addAuthorizationBearerHeader(HttpHeaders headers, String token) { + final String AUTHORIZATION_BEARER_TOKEN_FORMAT = "Bearer %s"; + headers.add(HttpHeaders.AUTHORIZATION, String.format(AUTHORIZATION_BEARER_TOKEN_FORMAT, token)); + } } From 4746763757bcabcbe08bebb260fc54aa4e13e244 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Fri, 13 Sep 2019 13:24:37 +0200 Subject: [PATCH 19/22] incorporate and test new token-client pwd flow integration --- samples/sap-java-buildpack-api-usage/pom.xml | 2 +- .../xsuaa/mock/MockPostProcessor.java | 13 ++--- .../xsuaa/extractor/UaaTokenBroker.java | 48 +++---------------- 3 files changed, 12 insertions(+), 51 deletions(-) diff --git a/samples/sap-java-buildpack-api-usage/pom.xml b/samples/sap-java-buildpack-api-usage/pom.xml index 157dc28baa..a2a168d68a 100755 --- a/samples/sap-java-buildpack-api-usage/pom.xml +++ b/samples/sap-java-buildpack-api-usage/pom.xml @@ -21,7 +21,7 @@ com.sap.cloud.security.xsuaa api - 1.7.0 + 2.0.0-SNAPSHOT provided diff --git a/spring-xsuaa-it/src/main/java/com/sap/cloud/security/xsuaa/mock/MockPostProcessor.java b/spring-xsuaa-it/src/main/java/com/sap/cloud/security/xsuaa/mock/MockPostProcessor.java index 0e76b60ada..1df03d6d93 100644 --- a/spring-xsuaa-it/src/main/java/com/sap/cloud/security/xsuaa/mock/MockPostProcessor.java +++ b/spring-xsuaa-it/src/main/java/com/sap/cloud/security/xsuaa/mock/MockPostProcessor.java @@ -38,25 +38,20 @@ public MockResponse dispatch(RecordedRequest request) { if ("/otherdomain/token_keys".equals(request.getPath())) { return getResponseFromFile("/mock/otherdomain_token_keys.json", HttpStatus.OK); } - if (request.getPath().equals("/oauth/token")) { + if (request.getPath().equals("/oauth/token") && "POST".equals(request.getMethod())) { String body = request.getBody().readString(StandardCharsets.UTF_8); - if ("basic c2ItamF2YS1oZWxsby13b3JsZDpteXNlY3JldC1iYXNpYw==" - .equalsIgnoreCase(request.getHeader("authorization")) && "POST".equals(request.getMethod()) - && body.contains("username=basic.user") && body.contains("password=basic.password")) { - + if (body.contains("grant_type=password") && body.contains("username=basic.user") && body.contains("password=basic.password")) { try { return new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .setResponseCode(HttpStatus.OK.value()) - .setBody(String.format("{\"access_token\": \"%s\"}", + .setBody(String.format("{\"expires_in\": 43199, \"access_token\": \"%s\"}", JWTUtil.createJWT("/password.txt", "testdomain"))); } catch (Exception e) { e.printStackTrace(); getResponse(RESPONSE_500, HttpStatus.INTERNAL_SERVER_ERROR); } } - if ("POST".equals(request.getMethod()) - && body.contains("grant_type=client_credentials")) { - + if (body.contains("grant_type=client_credentials")) { try { return new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .setResponseCode(HttpStatus.OK.value()).setBody(String.format( diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java index fd88f974f7..27b6ccd8f6 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/UaaTokenBroker.java @@ -1,19 +1,9 @@ package com.sap.cloud.security.xsuaa.extractor; import java.net.URI; -import java.util.Base64; -import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestTemplate; import com.sap.cloud.security.xsuaa.client.ClientCredentials; @@ -54,7 +44,8 @@ public String getAccessTokenFromClientCredentials(String tokenURL, String client return oAuth2TokenService.retrieveAccessTokenViaClientCredentialsGrant( URI.create(tokenURL), new ClientCredentials(clientId, clientSecret), null, null).getAccessToken(); } catch (OAuth2ServiceException ex) { - throw new TokenBrokerException("Cannot obtain Token from given clientId / secret", ex); + logger.warn("Cannot obtain Token from given clientId / secret."); + throw new TokenBrokerException("Cannot obtain Token from given clientId / secret.", ex); } } @@ -62,36 +53,11 @@ public String getAccessTokenFromClientCredentials(String tokenURL, String client public String getAccessTokenFromPasswordCredentials(String tokenURL, String clientId, String clientSecret, String username, String password) throws TokenBrokerException { try { - HttpHeaders headers = new HttpHeaders(); - String credentials = clientId + ":" + clientSecret; - String base64Creds = Base64.getEncoder().encodeToString(credentials.getBytes()); - headers.add("ACCEPT", "application/json"); - headers.add("AUTHORIZATION", "Basic " + base64Creds); - - MultiValueMap body = new LinkedMultiValueMap(); - - body.add("grant_type", "password"); - body.add("response_type", "token"); - body.add("client_id", clientId); - body.add("username", username); - body.add("password", password); - - // Note the body object as first parameter! - HttpEntity httpEntity = new HttpEntity(body, headers); - - @SuppressWarnings("rawtypes") - ResponseEntity exchange = restTemplate.exchange(tokenURL, HttpMethod.POST, httpEntity, Map.class); - - return (String) exchange.getBody().get("access_token"); - - } catch (HttpClientErrorException ex) { - logger.warn("Cannot obtain Token from given password credentials"); - throw new TokenBrokerException( - "Error obtaining access token:" + ex.getStatusText() + " " + ex.getResponseBodyAsString()); - } catch (HttpServerErrorException ex) { - logger.warn("Cannot obtain Token from given password credentials"); - throw new TokenBrokerException("Error obtaining access token from server:" + ex.getStatusText() + " " - + ex.getResponseBodyAsString()); + return oAuth2TokenService.retrieveAccessTokenViaPasswordGrant( + URI.create(tokenURL), new ClientCredentials(clientId, clientSecret), username, password, null, null).getAccessToken(); + } catch (OAuth2ServiceException ex) { + logger.warn("Cannot obtain Token from given user / password."); + throw new TokenBrokerException("Cannot obtain Token from given user / password.", ex); } } From 996f8617c9f682fb5a26875b8df86417bd525aa8 Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Fri, 13 Sep 2019 15:17:16 +0200 Subject: [PATCH 20/22] remove some deprecates, improve error handling --- ...ultAuthenticationInformationExtractor.java | 14 +++++++++---- .../security/xsuaa/extractor/TokenBroker.java | 4 ++-- .../xsuaa/extractor/TokenBrokerResolver.java | 6 +++--- .../xsuaa/extractor/UaaTokenBroker.java | 20 +++++++++---------- .../xsuaa/client/XsuaaOAuth2TokenService.java | 18 +++++++---------- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java index fc4b917ca8..b2e4a59910 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/DefaultAuthenticationInformationExtractor.java @@ -11,7 +11,7 @@ * Default Implementation * */ -class DefaultAuthenticationInformationExtractor implements AuthenticationInformationExtractor { +public class DefaultAuthenticationInformationExtractor implements AuthenticationInformationExtractor { private static final String SUBDOMAIN_HEADER = "X-Identity-Zone-Subdomain"; @@ -19,15 +19,21 @@ class DefaultAuthenticationInformationExtractor implements AuthenticationInforma private List authenticationMethods = Arrays.asList(AuthenticationMethod.BASIC, AuthenticationMethod.OAUTH2); - DefaultAuthenticationInformationExtractor() { + public DefaultAuthenticationInformationExtractor() { + super(); this.subDomain = null; } - DefaultAuthenticationInformationExtractor(AuthenticationMethod... authenticationMethods) { + public DefaultAuthenticationInformationExtractor(String subDomain) { + super(); + this.subDomain = subDomain; + } + + public DefaultAuthenticationInformationExtractor(AuthenticationMethod... authenticationMethods) { this(null, authenticationMethods); } - DefaultAuthenticationInformationExtractor(String subDomain, AuthenticationMethod... authenticationMethods) { + public DefaultAuthenticationInformationExtractor(String subDomain, AuthenticationMethod... authenticationMethods) { super(); this.subDomain = subDomain; this.authenticationMethods = Arrays.asList(authenticationMethods); diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java index 36e2a89d0a..60f263fc61 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBroker.java @@ -9,7 +9,7 @@ * * @deprecated in favor of * {@link com.sap.cloud.security.xsuaa.client.OAuth2TokenService} - * API. + * API. Will be removed with version 3.0.0. */ @Deprecated public interface TokenBroker { @@ -50,7 +50,7 @@ public String getAccessTokenFromClientCredentials(String tokenURL, String client * @throws TokenBrokerException * TokenBrokerException * @deprecated in favor of - * {@link com.sap.cloud.security.xsuaa.client.OAuth2TokenService} + * {@link com.sap.cloud.security.xsuaa.client.OAuth2TokenService#retrieveAccessTokenViaPasswordGrant(URI, ClientCredentials, String, String, String, Map)} */ public String getAccessTokenFromPasswordCredentials(String tokenURL, String clientId, String clientSecret, String username, String password) throws TokenBrokerException; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java index f4c21658f3..a39c4e527b 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolver.java @@ -162,7 +162,7 @@ private String getBrokerToken(AuthenticationMethod credentialType, Enumeration Date: Fri, 13 Sep 2019 17:31:09 +0200 Subject: [PATCH 21/22] update version to 2.0.0 --- api/pom.xml | 4 ++-- pom.xml | 2 +- samples/sap-java-buildpack-api-usage/manifest.yml | 6 ++++-- samples/sap-java-buildpack-api-usage/pom.xml | 2 +- samples/spring-security-basic-auth/pom.xml | 2 +- samples/spring-security-xsuaa-usage/manifest.yml | 3 ++- samples/spring-security-xsuaa-usage/pom.xml | 4 ++-- samples/spring-webflux-security-xsuaa-usage/pom.xml | 2 +- samples/vars.yml | 2 +- spring-xsuaa-it/pom.xml | 2 +- .../sap/cloud/security/xsuaa/mock/MockPostProcessor.java | 3 ++- spring-xsuaa-mock/README.md | 2 +- spring-xsuaa-mock/pom.xml | 2 +- spring-xsuaa-starter/pom.xml | 4 ++-- spring-xsuaa-test/README.md | 2 +- spring-xsuaa-test/pom.xml | 2 +- spring-xsuaa/README.md | 4 ++-- spring-xsuaa/pom.xml | 2 +- token-client/README.md | 4 ++-- token-client/pom.xml | 2 +- 20 files changed, 30 insertions(+), 26 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 6a0e89b28e..8643a0c6ef 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -4,12 +4,12 @@ com.sap.cloud.security.xsuaa api - 2.0.0-SNAPSHOT + 2.0.0 com.sap.cloud.security.xsuaa parent - 2.0.0-SNAPSHOT + 2.0.0 jar diff --git a/pom.xml b/pom.xml index 6b5f1263a3..422a94ca24 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.sap.cloud.security.xsuaa parent - 2.0.0-SNAPSHOT + 2.0.0 pom parent diff --git a/samples/sap-java-buildpack-api-usage/manifest.yml b/samples/sap-java-buildpack-api-usage/manifest.yml index d8f9c48e95..a9652a320c 100644 --- a/samples/sap-java-buildpack-api-usage/manifest.yml +++ b/samples/sap-java-buildpack-api-usage/manifest.yml @@ -12,13 +12,15 @@ applications: host: path: target/sap-java-buildpack-api-usage.war - buildpack: sap_java_buildpack + buildpacks: + - sap_java_buildpack services: - xsuaa-buildpack # Application Router as web server - name: approuter-sap-java-buildpack-api-usage path: approuter - buildpack: nodejs_buildpack + buildpacks: + - nodejs_buildpack memory: 128M routes: - route: approuter-sap-java-buildpack-api-usage-((ID)).((LANDSCAPE_APPS_DOMAIN)) diff --git a/samples/sap-java-buildpack-api-usage/pom.xml b/samples/sap-java-buildpack-api-usage/pom.xml index a2a168d68a..b384b8d951 100755 --- a/samples/sap-java-buildpack-api-usage/pom.xml +++ b/samples/sap-java-buildpack-api-usage/pom.xml @@ -21,7 +21,7 @@ com.sap.cloud.security.xsuaa api - 2.0.0-SNAPSHOT + 2.0.0 provided diff --git a/samples/spring-security-basic-auth/pom.xml b/samples/spring-security-basic-auth/pom.xml index 1de4e57567..5344446e1b 100644 --- a/samples/spring-security-basic-auth/pom.xml +++ b/samples/spring-security-basic-auth/pom.xml @@ -11,7 +11,7 @@ spring-security-basic-auth - 2.0.0-SNAPSHOT + 2.0.0 spring-security-basic-auth diff --git a/samples/spring-security-xsuaa-usage/manifest.yml b/samples/spring-security-xsuaa-usage/manifest.yml index baec79d30c..c4009bd37a 100644 --- a/samples/spring-security-xsuaa-usage/manifest.yml +++ b/samples/spring-security-xsuaa-usage/manifest.yml @@ -16,7 +16,8 @@ applications: # Application Router as web server - name: approuter-spring-security-xsuaa-usage path: approuter - buildpack: nodejs_buildpack + buildpacks: + - nodejs_buildpack memory: 128M routes: - route: spring-security-xsuaa-usage-web-((ID)).((LANDSCAPE_APPS_DOMAIN)) diff --git a/samples/spring-security-xsuaa-usage/pom.xml b/samples/spring-security-xsuaa-usage/pom.xml index e7a0c01769..46d43a2cef 100644 --- a/samples/spring-security-xsuaa-usage/pom.xml +++ b/samples/spring-security-xsuaa-usage/pom.xml @@ -14,7 +14,7 @@ com.sap.cloud.security.samples spring-security-xsuaa-usage - 2.0.0-SNAPSHOT + 2.0.0 spring-security-xsuaa-usage @@ -37,7 +37,7 @@ com.sap.cloud.security.xsuaa xsuaa-spring-boot-starter - 2.0.0-SNAPSHOT + 2.0.0 org.springframework.boot diff --git a/samples/spring-webflux-security-xsuaa-usage/pom.xml b/samples/spring-webflux-security-xsuaa-usage/pom.xml index 60eb08876a..be73a34e9c 100644 --- a/samples/spring-webflux-security-xsuaa-usage/pom.xml +++ b/samples/spring-webflux-security-xsuaa-usage/pom.xml @@ -12,7 +12,7 @@ com.sap.cloud.security.samples spring-webflux-security-xsuaa-usage - 2.0.0-SNAPSHOT + 2.0.0 spring-webflux-security-xsuaa-usage diff --git a/samples/vars.yml b/samples/vars.yml index 0a3d5f51c3..bb83adcf86 100644 --- a/samples/vars.yml +++ b/samples/vars.yml @@ -1,7 +1,7 @@ --- # some data to make the urls unique # change to another value, e.g. your User ID -ID: 00-00-00 +ID: d048418 # Choose cfapps.eu10.hana.ondemand.com for the EU10 landscape, cfapps.us10.hana.ondemand.com for US10 LANDSCAPE_APPS_DOMAIN: cfapps.eu10.hana.ondemand.com diff --git a/spring-xsuaa-it/pom.xml b/spring-xsuaa-it/pom.xml index c000c054ee..63c10db3fb 100644 --- a/spring-xsuaa-it/pom.xml +++ b/spring-xsuaa-it/pom.xml @@ -11,7 +11,7 @@ spring-xsuaa-it - 2.0.0-SNAPSHOT + 2.0.0 spring-xsuaa-it diff --git a/spring-xsuaa-it/src/main/java/com/sap/cloud/security/xsuaa/mock/MockPostProcessor.java b/spring-xsuaa-it/src/main/java/com/sap/cloud/security/xsuaa/mock/MockPostProcessor.java index 1df03d6d93..a7ccd3ec7a 100644 --- a/spring-xsuaa-it/src/main/java/com/sap/cloud/security/xsuaa/mock/MockPostProcessor.java +++ b/spring-xsuaa-it/src/main/java/com/sap/cloud/security/xsuaa/mock/MockPostProcessor.java @@ -40,7 +40,8 @@ public MockResponse dispatch(RecordedRequest request) { } if (request.getPath().equals("/oauth/token") && "POST".equals(request.getMethod())) { String body = request.getBody().readString(StandardCharsets.UTF_8); - if (body.contains("grant_type=password") && body.contains("username=basic.user") && body.contains("password=basic.password")) { + if (body.contains("grant_type=password") && body.contains("username=basic.user") + && body.contains("password=basic.password")) { try { return new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .setResponseCode(HttpStatus.OK.value()) diff --git a/spring-xsuaa-mock/README.md b/spring-xsuaa-mock/README.md index bdedda4f37..de637c7c93 100644 --- a/spring-xsuaa-mock/README.md +++ b/spring-xsuaa-mock/README.md @@ -17,7 +17,7 @@ The default implementation offers already valid *token_keys* for JWT tokens, tha com.sap.cloud.security.xsuaa spring-xsuaa-mock - 2.0.0-SNAPSHOT + 2.0.0 org.springframework.boot diff --git a/spring-xsuaa-mock/pom.xml b/spring-xsuaa-mock/pom.xml index 8544e3afb8..46bd49647c 100644 --- a/spring-xsuaa-mock/pom.xml +++ b/spring-xsuaa-mock/pom.xml @@ -7,7 +7,7 @@ com.sap.cloud.security.xsuaa parent - 2.0.0-SNAPSHOT + 2.0.0 spring-xsuaa-mock diff --git a/spring-xsuaa-starter/pom.xml b/spring-xsuaa-starter/pom.xml index d5330dcbf5..75170166f7 100644 --- a/spring-xsuaa-starter/pom.xml +++ b/spring-xsuaa-starter/pom.xml @@ -14,7 +14,7 @@ com.sap.cloud.security.xsuaa parent - 2.0.0-SNAPSHOT + 2.0.0 @@ -30,7 +30,7 @@ xsuaa-spring-boot-starter - 2.0.0-SNAPSHOT + 2.0.0 SAP Spring Boot Xsuaa Starter SAP Starter for integrating application with XSUAA service https://github.com/SAP/cloud-security-xsuaa-integration diff --git a/spring-xsuaa-test/README.md b/spring-xsuaa-test/README.md index 925ff79b20..c90505c7fc 100644 --- a/spring-xsuaa-test/README.md +++ b/spring-xsuaa-test/README.md @@ -27,7 +27,7 @@ This includes for example a `JwtGenerator` that generates JSON Web Tokens (JWT) com.sap.cloud.security.xsuaa spring-xsuaa-test - 2.0.0-SNAPSHOT + 2.0.0 test diff --git a/spring-xsuaa-test/pom.xml b/spring-xsuaa-test/pom.xml index 7d21ca02d0..eff5d19b54 100644 --- a/spring-xsuaa-test/pom.xml +++ b/spring-xsuaa-test/pom.xml @@ -7,7 +7,7 @@ com.sap.cloud.security.xsuaa parent - 2.0.0-SNAPSHOT + 2.0.0 spring-xsuaa-test diff --git a/spring-xsuaa/README.md b/spring-xsuaa/README.md index 5046f290b4..217cf3ec0b 100644 --- a/spring-xsuaa/README.md +++ b/spring-xsuaa/README.md @@ -23,7 +23,7 @@ This library enhances the [spring-security](https://github.com/spring-projects/s com.sap.cloud.security.xsuaa spring-xsuaa - 2.0.0-SNAPSHOT + 2.0.0 org.apache.logging.log4j @@ -38,7 +38,7 @@ This library enhances the [spring-security](https://github.com/spring-projects/s com.sap.cloud.security.xsuaa xsuaa-spring-boot-starter - 2.0.0-SNAPSHOT + 2.0.0 ``` diff --git a/spring-xsuaa/pom.xml b/spring-xsuaa/pom.xml index aa42a547db..f83d8e2643 100644 --- a/spring-xsuaa/pom.xml +++ b/spring-xsuaa/pom.xml @@ -7,7 +7,7 @@ com.sap.cloud.security.xsuaa parent - 2.0.0-SNAPSHOT + 2.0.0 spring-xsuaa diff --git a/token-client/README.md b/token-client/README.md index def216edaf..54153d6454 100644 --- a/token-client/README.md +++ b/token-client/README.md @@ -25,7 +25,7 @@ A Refresh Token ([RFC 6749, section 1.5](https://tools.ietf.org/html/rfc6749#sec com.sap.cloud.security.xsuaa token-client - 2.0.0-SNAPSHOT + 2.0.0 ``` ## Configuration for Spring Boot Applications @@ -36,7 +36,7 @@ In context of a Spring Boot application you may like to leverage auto-configurat com.sap.cloud.security.xsuaa xsuaa-spring-boot-starter - 2.0.0-SNAPSHOT + 2.0.0 ``` diff --git a/token-client/pom.xml b/token-client/pom.xml index d193408a0f..5d61c1a64a 100644 --- a/token-client/pom.xml +++ b/token-client/pom.xml @@ -7,7 +7,7 @@ com.sap.cloud.security.xsuaa parent - 2.0.0-SNAPSHOT + 2.0.0 token-client From e8dfff4672b0df6c3e41d020cbfb3780faf2917c Mon Sep 17 00:00:00 2001 From: Nena Raab Date: Fri, 13 Sep 2019 18:42:10 +0200 Subject: [PATCH 22/22] remove personal data --- samples/vars.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/vars.yml b/samples/vars.yml index bb83adcf86..0a3d5f51c3 100644 --- a/samples/vars.yml +++ b/samples/vars.yml @@ -1,7 +1,7 @@ --- # some data to make the urls unique # change to another value, e.g. your User ID -ID: d048418 +ID: 00-00-00 # Choose cfapps.eu10.hana.ondemand.com for the EU10 landscape, cfapps.us10.hana.ondemand.com for US10 LANDSCAPE_APPS_DOMAIN: cfapps.eu10.hana.ondemand.com