Repository: SAP/cloud-security-xsuaa-integration
Pull request #417 (2020-11-19 14:48:42)

Spring o auth2 lib cleanup


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 376ec83e..52c3e587 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,8 +9,19 @@ All notable changes to this project will be documented in this file.
 - [java-security-test] In case you like to overwrite the client id using `JwtGenerator` using `withClaimValue()` or `withClaimValues()` method, it's recommended to set the `azp` claim instead using `withClaimValue(TokenClaims.AUTHORIZATION_PARTY, "T000310")`.
 - [spring-xsuaa] 
   - `getClientId()` method implementation of `Token` interface has been changed. Using `azp` and as fallback `aud` and `cid` claims to obtain client id.
-  - Applications can exchange incoming id tokens from `identity` service to an `xsuaa` token, so that authorization checks with `xsuaa` scopes can be performed. In order to activate that feature provide a `XSUAA_IAS_XCHANGE_ENABLED = true` system variable.
-
+- :warning: **backward incompatible change:** usage of deprecated org.springframework.security.oauth:spring-security-oauth2 dependency in `OAuth2AuthenticationConverter` was removed. 
+  `OAuth2AuthenticationConverter.convert()` method return type has changed from `org.springframework.security.oauth2.provider.OAuth2Authentication` to `org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication`
+  - Migration tips
+    - when necessary, org.springframework.security.oauth:spring-security-oauth2 dependency need to be provided explicitly 
+    - `OAuth2WebSecurityExpressionHandler()` won't work in conjunction with `OAuth2AuthenticationConverter`, as it expects `OAuth2Authentication` class instead of `BearerTokenAuthentication` when deriving authorization claims. Use `hasAuthority()` or `hasAnyAuthority()` instead of explicitly defined `expressionHandler(new OAuth2WebSecurityExpressionHandler())` and `access()` expression for authorized requests. 
+- The following dependency was removed:
+    - org.springframework.security.oauth:spring-security-oauth2
+- The following dependencies were updated:
+    - spring.boot.version 2.3.4.RELEASE --> 2.3.5.RELEASE
+    - spring.core.version 5.2.9.RELEASE --> 5.2.10.RELEASE
+    - spring.security.version 5.3.4.RELEASE --> 5.3.5.RELEASE
+    - caffeine.version 2.8.2 --> 2.8.6
+    
 ## 2.7.8
 - [java-security-test] Supports JUnit 5 Tests with `XsuaaExtension`, `IasExtension` and `SecurityTestExtension` as documented [here](/java-security-test#junit-5).
 - [spring-xsuaa-starter] Upgrade Spring versions:
diff --git a/pom.xml b/pom.xml
index 0679554c..4a954f8b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -56,9 +56,9 @@
 		<maven.compiler.source>1.8</maven.compiler.source>
 		<maven.source.plugin.version>3.1.0</maven.source.plugin.version>
 		<!-- make sure that spring core and spring boot versions are compatible-->
-		<spring.boot.version>2.3.4.RELEASE</spring.boot.version>
-		<spring.core.version>5.2.9.RELEASE</spring.core.version>
-		<spring.security.version>5.3.4.RELEASE</spring.security.version>
+		<spring.boot.version>2.3.5.RELEASE</spring.boot.version>
+		<spring.core.version>5.2.10.RELEASE</spring.core.version>
+		<spring.security.version>5.3.5.RELEASE</spring.security.version>
 		<spring.security.oauth2.version>2.5.0.RELEASE</spring.security.oauth2.version>
 		<spring.security.jwt.version>1.1.1.RELEASE</spring.security.jwt.version>
 		<reactor.version>3.3.7.RELEASE</reactor.version>
@@ -67,7 +67,7 @@
 		<org.json.version>20200518</org.json.version>
 		<google.jsr305.version>3.0.2</google.jsr305.version>
 		<apache.httpclient.version>4.5.9</apache.httpclient.version>
-		<caffeine.version>2.8.2</caffeine.version>
+		<caffeine.version>2.8.6</caffeine.version>
 		<commons.io.version>2.6</commons.io.version>
 		<javax.servlet.api.version>3.1.0</javax.servlet.api.version>
 		<mockwebserver.version>3.9.1</mockwebserver.version>
diff --git a/spring-xsuaa/pom.xml b/spring-xsuaa/pom.xml
index 5eabb022..cffa1d6c 100644
--- a/spring-xsuaa/pom.xml
+++ b/spring-xsuaa/pom.xml
@@ -20,11 +20,6 @@
 			<artifactId>spring-core</artifactId>
 			<scope>provided</scope>
 		</dependency>
-		<dependency>
-			<groupId>org.springframework.security.oauth</groupId>
-			<artifactId>spring-security-oauth2</artifactId>
-			<scope>provided</scope>
-		</dependency>
 		<dependency>
 			<groupId>org.springframework.security</groupId>
 			<artifactId>spring-security-oauth2-jose</artifactId>
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/OAuth2AuthenticationConverter.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/OAuth2AuthenticationConverter.java
index 0c79c318..f5167465 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/OAuth2AuthenticationConverter.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/OAuth2AuthenticationConverter.java
@@ -1,20 +1,21 @@
 package com.sap.cloud.security.xsuaa.token;
 
-import static com.sap.cloud.security.xsuaa.token.TokenClaims.CLAIM_CLIENT_ID;
-
-import java.util.Objects;
-import java.util.stream.Collectors;
-
+import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
 import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.security.oauth2.provider.AuthorizationRequest;
-import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;
 
-import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor;
+import java.util.Objects;
 
 /**
- * An authentication converter that sets a OAuth2Authentication object. Required
- * to keep compatibility with UAA.
+ * @deprecated OAuth2AuthenticationConverter won't be supported in future
+ * @see <a href="https://spring.io/projects/spring-security-oauth">Spring
+ *      Security OAuth2 deprecation notice</a>
+ * @see <a href=
+ *      "https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Migration-Guide">OAuth
+ *      2.0 Migration Guide </a>
  */
+@Deprecated
 public class OAuth2AuthenticationConverter extends TokenAuthenticationConverter {
 
 	public OAuth2AuthenticationConverter(AuthoritiesExtractor authoritiesExtractor) {
@@ -22,14 +23,14 @@ public class OAuth2AuthenticationConverter extends TokenAuthenticationConverter
 	}
 
 	@Override
-	public OAuth2Authentication convert(Jwt jwt) {
+	public BearerTokenAuthentication convert(Jwt jwt) {
+
 		AuthenticationToken authenticationToken = (AuthenticationToken) super.convert(jwt);
-		String clientId = jwt.getClaimAsString(CLAIM_CLIENT_ID);
-		AuthorizationRequest authorizationRequest = new AuthorizationRequest(clientId,
-				authenticationToken.getAuthorities().stream().map(Objects::toString).collect(Collectors.toList()));
-		authorizationRequest.setApproved(true);
-		authorizationRequest.setAuthorities(authenticationToken.getAuthorities());
+		Objects.requireNonNull(authenticationToken, "OAuth2 Authentication token can't be null");
+		OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(),
+				jwt.getIssuedAt(), jwt.getExpiresAt());
 
-		return new OAuth2Authentication(authorizationRequest.createOAuth2Request(), authenticationToken);
+		return new BearerTokenAuthentication(new OAuth2Principal(authenticationToken), accessToken,
+				authenticationToken.getAuthorities());
 	}
-}
\ No newline at end of file
+}
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/OAuth2Principal.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/OAuth2Principal.java
new file mode 100644
index 00000000..70a74184
--- /dev/null
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/OAuth2Principal.java
@@ -0,0 +1,36 @@
+package com.sap.cloud.security.xsuaa.token;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
+
+import java.util.Collection;
+import java.util.Map;
+
+class OAuth2Principal extends XsuaaToken implements OAuth2AuthenticatedPrincipal {
+
+	private final Collection<GrantedAuthority> authorities;
+	private final Map<String, Object> attributes;
+	private final String name;
+
+	public OAuth2Principal(AuthenticationToken authenticationToken) {
+		super(authenticationToken.getToken());
+		this.authorities = authenticationToken.getAuthorities();
+		this.name = authenticationToken.getName();
+		this.attributes = authenticationToken.getTokenAttributes();
+	}
+
+	@Override
+	public Map<String, Object> getAttributes() {
+		return this.attributes;
+	}
+
+	@Override
+	public Collection<? extends GrantedAuthority> getAuthorities() {
+		return this.authorities;
+	}
+
+	@Override
+	public String getName() {
+		return this.name;
+	}
+}


Test patch:
diff --git a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolverTest.java b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolverTest.java
index 4b77ebea..f4aa4781 100644
--- a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolverTest.java
+++ b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/TokenBrokerResolverTest.java
@@ -1,7 +1,7 @@
 package com.sap.cloud.security.xsuaa.extractor;
 
-import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
-import com.sap.cloud.security.xsuaa.client.OAuth2TokenService;
+import static org.assertj.core.api.Assertions.assertThat;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -10,7 +10,8 @@ import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit4.SpringRunner;
 
-import static org.assertj.core.api.Assertions.assertThat;
+import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
+import com.sap.cloud.security.xsuaa.client.OAuth2TokenService;
 
 @RunWith(SpringRunner.class)
 @ContextConfiguration(classes = { TokenBrokerTestConfiguration.class })
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/OAuth2TokenAuthenticationConverterTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/OAuth2TokenAuthenticationConverterTest.java
index 310c8140..0744076b 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/OAuth2TokenAuthenticationConverterTest.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/OAuth2TokenAuthenticationConverterTest.java
@@ -1,43 +1,50 @@
 package com.sap.cloud.security.xsuaa.token;
 
-import static org.hamcrest.CoreMatchers.hasItem;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
+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;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;
 
-import com.sap.cloud.security.xsuaa.extractor.DefaultAuthoritiesExtractor;
-import com.sap.cloud.security.xsuaa.test.JwtGenerator;
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class OAuth2TokenAuthenticationConverterTest {
-	private String xsAppName = "my-app-name!400";
-	private OAuth2AuthenticationConverter tokenConverterOauth2;
-	String scopeAdmin = xsAppName + "." + "Admin";
-	String scopeRead = xsAppName + "." + "Read";
+	private static final String XS_APP_NAME = "my-app-name!400";
+	private static AbstractAuthenticationToken authenticationToken;
+	String scopeAdmin = XS_APP_NAME + "." + "Admin";
+	String scopeRead = XS_APP_NAME + "." + "Read";
 	String scopeOther = "other-app!234" + "." + "Other";
 
 	@Before
 	public void setup() {
-		tokenConverterOauth2 = new OAuth2AuthenticationConverter(new DefaultAuthoritiesExtractor());
+		OAuth2AuthenticationConverter tokenConverterOauth2 = new OAuth2AuthenticationConverter(
+				new DefaultAuthoritiesExtractor());
+		Jwt jwt = new JwtGenerator().addScopes(scopeAdmin, scopeRead, scopeOther).getToken();
+		authenticationToken = tokenConverterOauth2.convert(jwt);
 	}
 
 	@Test
 	public void extractAuthoritiesWithScopesOAuth2Authentication() {
-		Jwt jwt = new JwtGenerator().addScopes(scopeAdmin, scopeRead, scopeOther).getToken();
-
-		AbstractAuthenticationToken authenticationToken = tokenConverterOauth2.convert(jwt);
 		assertThat(authenticationToken.getAuthorities().size(), is(3));
 		assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority(scopeRead)));
 		assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority(scopeAdmin)));
 		assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority(scopeOther)));
+		assertTrue(authenticationToken instanceof BearerTokenAuthentication);
+	}
 
-		assertTrue(authenticationToken instanceof OAuth2Authentication);
-		assertThat(((OAuth2Authentication) authenticationToken).getOAuth2Request().getScope(), hasItem(scopeRead));
+	@Test
+	public void extractPrincipalInfoTest() {
+		OAuth2Principal principal = (OAuth2Principal) authenticationToken.getPrincipal();
+		assertThat(principal.getName(), endsWith("testuser"));
+		assertThat(principal.getHeaders(), notNullValue());
+		assertThat(principal.getScopes(), notNullValue());
+		assertTrue(principal.getScopes().contains(scopeAdmin));
+		assertEquals("sb-xsapplication!t895", principal.getClaim(TokenClaims.CLAIM_AUTHORIZATION_PARTY));
 	}
 }