From 2dd27611ee7ef3287a1bc6f66e4d9df04e8a19f0 Mon Sep 17 00:00:00 2001 From: ZhuXiaoBing Date: Wed, 24 Feb 2021 14:48:09 +0800 Subject: [PATCH 1/7] For AAD resource-server, create grantedAuthority by both "roles" and "claims" by default. --- ...JwtBearerTokenAuthenticationConverter.java | 25 +------- .../AADJwtGrantedAuthoritiesConverter.java | 61 +++++++++++++++++++ ...earerTokenAuthenticationConverterTest.java | 23 ++++--- 3 files changed, 78 insertions(+), 31 deletions(-) create mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java index feebcac92b04b..7be63579290e2 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java @@ -2,41 +2,22 @@ // Licensed under the MIT License. package com.azure.spring.aad.webapi; -import java.util.Collection; import org.springframework.core.convert.converter.Converter; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication; -import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; -import org.springframework.util.Assert; + +import java.util.Collection; /** * A {@link Converter} that takes a {@link Jwt} and converts it into a {@link BearerTokenAuthentication}. */ public class AADJwtBearerTokenAuthenticationConverter implements Converter { - private static final String DEFAULT_AUTHORITY_PREFIX = "SCOPE_"; - private Converter> converter - = new JwtGrantedAuthoritiesConverter(); - - public AADJwtBearerTokenAuthenticationConverter() { - } - - public AADJwtBearerTokenAuthenticationConverter(String authoritiesClaimName) { - this(authoritiesClaimName, DEFAULT_AUTHORITY_PREFIX); - } - - public AADJwtBearerTokenAuthenticationConverter(String authoritiesClaimName, String authorityPrefix) { - Assert.notNull(authoritiesClaimName, "authoritiesClaimName cannot be null"); - Assert.notNull(authorityPrefix, "authorityPrefix cannot be null"); - JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); - jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(authoritiesClaimName); - jwtGrantedAuthoritiesConverter.setAuthorityPrefix(authorityPrefix); - this.converter = jwtGrantedAuthoritiesConverter; - } + = new AADJwtGrantedAuthoritiesConverter(); protected Collection extractAuthorities(Jwt jwt) { return this.converter.convert(jwt); diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java new file mode 100644 index 0000000000000..e1f6cc41bce81 --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java @@ -0,0 +1,61 @@ +package com.azure.spring.aad.webapi; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Extracts the {@link GrantedAuthority}s from scope attributes typically found in a {@link Jwt}. + * + * @author Eric Deandrea + * @since 5.2 + */ +public class AADJwtGrantedAuthoritiesConverter implements Converter> { + + private static final String DEFAULT_SCP_AUTHORITY_PREFIX = "SCOPE_"; + + private static final String DEFAULT_ROLES_AUTHORITY_PREFIX = "APPROLE_"; + + private static final Collection WELL_KNOWN_AUTHORITIES_CLAIM_NAMES = Arrays.asList("scp", "roles"); + + @Override + public Collection convert(Jwt jwt) { + Collection grantedAuthorities = new ArrayList<>(); + for (String authority : getAuthorities(jwt)) { + grantedAuthorities.add(new SimpleGrantedAuthority(authority)); + } + return grantedAuthorities; + } + + private Collection getAuthorities(Jwt jwt) { + List authoritiesList = new ArrayList(); + for (String claimName : WELL_KNOWN_AUTHORITIES_CLAIM_NAMES) { + if (jwt.containsClaim(claimName)) { + Object authorities = jwt.getClaim(claimName); + if (authorities instanceof String) { + if (StringUtils.hasText((String) authorities)) { + authoritiesList.addAll(Arrays.asList(((String) authorities).split(" ")) + .stream() + .map(s -> DEFAULT_SCP_AUTHORITY_PREFIX + s) + .collect(Collectors.toList())); + } + } else if (authorities instanceof Collection) { + authoritiesList.addAll(((Collection) authorities) + .stream() + .filter(s -> StringUtils.hasText((String) s)) + .map(s -> DEFAULT_ROLES_AUTHORITY_PREFIX + s) + .collect(Collectors.toList())); + } + } + } + return authoritiesList; + } +} diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java index 00be2accc4829..b84078d7d3bb9 100644 --- a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java +++ b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java @@ -2,23 +2,26 @@ // Licensed under the MIT License. package com.azure.spring.aad.webapi; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; +import net.minidev.json.JSONArray; import org.junit.Before; import org.junit.Test; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.oauth2.jwt.Jwt; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + public class AADJwtBearerTokenAuthenticationConverterTest { private Jwt jwt = mock(Jwt.class); private Map claims = new HashMap<>(); private Map headers = new HashMap<>(); + private JSONArray jsonArray = new JSONArray().appendElement("User.read").appendElement("User.write"); @Before public void init() { @@ -26,7 +29,7 @@ public void init() { claims.put("tid", "fake-tid"); headers.put("kid", "kg2LYs2T0CTjIfj4rt6JIynen38"); when(jwt.getClaim("scp")).thenReturn("Order.read Order.write"); - when(jwt.getClaim("roles")).thenReturn("User.read User.write"); + when(jwt.getClaim("roles")).thenReturn(jsonArray); when(jwt.getTokenValue()).thenReturn("fake-token-value"); when(jwt.getIssuedAt()).thenReturn(Instant.now()); when(jwt.getHeaders()).thenReturn(headers); @@ -56,18 +59,20 @@ public void testExtractDefaultScopeAuthorities() { .getPrincipal(); assertThat(principal.getAttributes()).isNotEmpty(); assertThat(principal.getAttributes()).hasSize(2); + assertThat(principal.getAuthorities()).hasSize(2); } @Test public void testExtractCustomScopeAuthorities() { when(jwt.containsClaim("roles")).thenReturn(true); - AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter("roles", "ROLE_"); + AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter(); AbstractAuthenticationToken authenticationToken = converter.convert(jwt); assertThat(authenticationToken.getPrincipal()).isExactlyInstanceOf(AADOAuth2AuthenticatedPrincipal.class); AADOAuth2AuthenticatedPrincipal principal = (AADOAuth2AuthenticatedPrincipal) authenticationToken .getPrincipal(); assertThat(principal.getAttributes()).isNotEmpty(); assertThat(principal.getAttributes()).hasSize(2); + assertThat(principal.getAuthorities()).hasSize(4); } From 0f035e7fa8c7e24d31c9448f0728074da77deb0f Mon Sep 17 00:00:00 2001 From: ZhuXiaoBing Date: Wed, 24 Feb 2021 14:50:42 +0800 Subject: [PATCH 2/7] Update javadoc. --- .../spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java index e1f6cc41bce81..e0217d6b243e5 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java @@ -14,9 +14,6 @@ /** * Extracts the {@link GrantedAuthority}s from scope attributes typically found in a {@link Jwt}. - * - * @author Eric Deandrea - * @since 5.2 */ public class AADJwtGrantedAuthoritiesConverter implements Converter> { From 50cf55dfa70691237dcdb1a9763f06e1d2dcebf6 Mon Sep 17 00:00:00 2001 From: ZhuXiaoBing Date: Wed, 24 Feb 2021 15:47:39 +0800 Subject: [PATCH 3/7] Update the code: * Simplify the code, remove unnecessary variables. --- .../AADJwtGrantedAuthoritiesConverter.java | 16 ++++++++-------- ...wtBearerTokenAuthenticationConverterTest.java | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java index e0217d6b243e5..8871e88e6360f 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverter.java @@ -1,3 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. package com.azure.spring.aad.webapi; import org.springframework.core.convert.converter.Converter; @@ -9,7 +11,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.List; import java.util.stream.Collectors; /** @@ -33,19 +34,18 @@ public Collection convert(Jwt jwt) { } private Collection getAuthorities(Jwt jwt) { - List authoritiesList = new ArrayList(); + Collection authoritiesList = new ArrayList(); for (String claimName : WELL_KNOWN_AUTHORITIES_CLAIM_NAMES) { if (jwt.containsClaim(claimName)) { - Object authorities = jwt.getClaim(claimName); - if (authorities instanceof String) { - if (StringUtils.hasText((String) authorities)) { - authoritiesList.addAll(Arrays.asList(((String) authorities).split(" ")) + if (jwt.getClaim(claimName) instanceof String) { + if (StringUtils.hasText(jwt.getClaim(claimName))) { + authoritiesList.addAll(Arrays.asList(((String) jwt.getClaim(claimName)).split(" ")) .stream() .map(s -> DEFAULT_SCP_AUTHORITY_PREFIX + s) .collect(Collectors.toList())); } - } else if (authorities instanceof Collection) { - authoritiesList.addAll(((Collection) authorities) + } else if (jwt.getClaim(claimName) instanceof Collection) { + authoritiesList.addAll(((Collection) jwt.getClaim(claimName)) .stream() .filter(s -> StringUtils.hasText((String) s)) .map(s -> DEFAULT_ROLES_AUTHORITY_PREFIX + s) diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java index b84078d7d3bb9..4ef78cf093710 100644 --- a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java +++ b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java @@ -63,7 +63,7 @@ public void testExtractDefaultScopeAuthorities() { } @Test - public void testExtractCustomScopeAuthorities() { + public void testExtractRoleScopeAuthorities() { when(jwt.containsClaim("roles")).thenReturn(true); AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter(); AbstractAuthenticationToken authenticationToken = converter.convert(jwt); From e82614d4c01b00ffe6fd8ffe194d570c3c40c311 Mon Sep 17 00:00:00 2001 From: ZhuXiaoBing Date: Wed, 3 Mar 2021 14:03:14 +0800 Subject: [PATCH 4/7] * Avoid breaking change. * Update unit tests. --- ...JwtBearerTokenAuthenticationConverter.java | 22 ++++++- ...earerTokenAuthenticationConverterTest.java | 5 +- ...AADJwtGrantedAuthoritiesConverterTest.java | 59 +++++++++++++++++++ 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverterTest.java diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java index 7be63579290e2..529fb4c986fdd 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java @@ -8,6 +8,8 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication; +import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; +import org.springframework.util.Assert; import java.util.Collection; @@ -16,8 +18,26 @@ */ public class AADJwtBearerTokenAuthenticationConverter implements Converter { + private static final String DEFAULT_AUTHORITY_PREFIX = "SCOPE_"; + private Converter> converter - = new AADJwtGrantedAuthoritiesConverter(); + = new JwtGrantedAuthoritiesConverter(); + + public AADJwtBearerTokenAuthenticationConverter() { + } + + public AADJwtBearerTokenAuthenticationConverter(String authoritiesClaimName) { + this(authoritiesClaimName, DEFAULT_AUTHORITY_PREFIX); + } + + public AADJwtBearerTokenAuthenticationConverter(String authoritiesClaimName, String authorityPrefix) { + Assert.notNull(authoritiesClaimName, "authoritiesClaimName cannot be null"); + Assert.notNull(authorityPrefix, "authorityPrefix cannot be null"); + JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(authoritiesClaimName); + jwtGrantedAuthoritiesConverter.setAuthorityPrefix(authorityPrefix); + this.converter = jwtGrantedAuthoritiesConverter; + } protected Collection extractAuthorities(Jwt jwt) { return this.converter.convert(jwt); diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java index 4ef78cf093710..e79b9f8b25a86 100644 --- a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java +++ b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java @@ -65,14 +65,15 @@ public void testExtractDefaultScopeAuthorities() { @Test public void testExtractRoleScopeAuthorities() { when(jwt.containsClaim("roles")).thenReturn(true); - AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter(); + AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter("roles", + "APPROLE_"); AbstractAuthenticationToken authenticationToken = converter.convert(jwt); assertThat(authenticationToken.getPrincipal()).isExactlyInstanceOf(AADOAuth2AuthenticatedPrincipal.class); AADOAuth2AuthenticatedPrincipal principal = (AADOAuth2AuthenticatedPrincipal) authenticationToken .getPrincipal(); assertThat(principal.getAttributes()).isNotEmpty(); assertThat(principal.getAttributes()).hasSize(2); - assertThat(principal.getAuthorities()).hasSize(4); + assertThat(principal.getAuthorities()).hasSize(2); } diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverterTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverterTest.java new file mode 100644 index 0000000000000..c8a446be2a811 --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverterTest.java @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.aad.webapi; + +import net.minidev.json.JSONArray; +import org.junit.Before; +import org.junit.Test; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.oauth2.jwt.Jwt; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class AADJwtGrantedAuthoritiesConverterTest { + + private Jwt jwt = mock(Jwt.class); + private Map claims = new HashMap<>(); + private Map headers = new HashMap<>(); + private JSONArray jsonArray = new JSONArray().appendElement("User.read").appendElement("User.write"); + + @Before + public void init() { + claims.put("iss", "fake-issuer"); + claims.put("tid", "fake-tid"); + headers.put("kid", "kg2LYs2T0CTjIfj4rt6JIynen38"); + when(jwt.getClaim("scp")).thenReturn("Order.read Order.write"); + when(jwt.getClaim("roles")).thenReturn(jsonArray); + when(jwt.getTokenValue()).thenReturn("fake-token-value"); + when(jwt.getIssuedAt()).thenReturn(Instant.now()); + when(jwt.getHeaders()).thenReturn(headers); + when(jwt.getExpiresAt()).thenReturn(Instant.MAX); + when(jwt.getClaims()).thenReturn(claims); + when(jwt.containsClaim("scp")).thenReturn(true); + when(jwt.containsClaim("roles")).thenReturn(true); + } + + @Test + public void testExtractRoleScopeAuthorities() { + AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter(); + AADJwtGrantedAuthoritiesConverter aadJwtGrantedAuthoritiesConverter = new AADJwtGrantedAuthoritiesConverter(); + converter.setJwtGrantedAuthoritiesConverter(aadJwtGrantedAuthoritiesConverter); + AbstractAuthenticationToken authenticationToken = converter.convert(jwt); + assertThat(authenticationToken.getPrincipal()).isExactlyInstanceOf(AADOAuth2AuthenticatedPrincipal.class); + AADOAuth2AuthenticatedPrincipal principal = (AADOAuth2AuthenticatedPrincipal) authenticationToken + .getPrincipal(); + assertThat(principal.getAttributes()).isNotEmpty(); + assertThat(principal.getAttributes()).hasSize(2); + assertThat(principal.getAuthorities()).hasSize(4); + } + + +} + + From ff0fd553ce30719a8144226081ca6a06b21d6bea Mon Sep 17 00:00:00 2001 From: ZhuXiaoBing Date: Thu, 4 Mar 2021 16:17:51 +0800 Subject: [PATCH 5/7] * Modify the no-argument constructor. * Remove setJwtGrantedAuthoritiesConverter method. --- .../webapi/AADJwtBearerTokenAuthenticationConverter.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java index 529fb4c986fdd..79eae924cdfff 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java @@ -20,10 +20,10 @@ public class AADJwtBearerTokenAuthenticationConverter implements Converter> converter - = new JwtGrantedAuthoritiesConverter(); + private Converter> converter; public AADJwtBearerTokenAuthenticationConverter() { + this.converter = new AADJwtGrantedAuthoritiesConverter(); } public AADJwtBearerTokenAuthenticationConverter(String authoritiesClaimName) { @@ -52,9 +52,4 @@ public AbstractAuthenticationToken convert(Jwt jwt) { jwt.getHeaders(), jwt.getClaims(), authorities, jwt.getTokenValue()); return new BearerTokenAuthentication(principal, accessToken, authorities); } - - public void setJwtGrantedAuthoritiesConverter( - Converter> jwtGrantedAuthoritiesConverter) { - this.converter = jwtGrantedAuthoritiesConverter; - } } From 9c0b3fb2c90e545811569fdff48b1aae22d6dfc3 Mon Sep 17 00:00:00 2001 From: ZhuXiaoBing Date: Thu, 4 Mar 2021 16:28:25 +0800 Subject: [PATCH 6/7] * Considering the user needs to custom Converter, so add `setJwtGrantedAuthoritiesConverter` method. --- .../aad/webapi/AADJwtBearerTokenAuthenticationConverter.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java index 79eae924cdfff..0744ec42f96c5 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverter.java @@ -52,4 +52,9 @@ public AbstractAuthenticationToken convert(Jwt jwt) { jwt.getHeaders(), jwt.getClaims(), authorities, jwt.getTokenValue()); return new BearerTokenAuthentication(principal, accessToken, authorities); } + + public void setJwtGrantedAuthoritiesConverter( + Converter> jwtGrantedAuthoritiesConverter) { + this.converter = jwtGrantedAuthoritiesConverter; + } } From c41718615353bbed551a6d87a5a8f3d0dffd7f4b Mon Sep 17 00:00:00 2001 From: ZhuXiaoBing Date: Thu, 4 Mar 2021 17:27:04 +0800 Subject: [PATCH 7/7] * Modifying unit tests. --- ...earerTokenAuthenticationConverterTest.java | 48 +++++++++++++-- ...AADJwtGrantedAuthoritiesConverterTest.java | 59 ------------------- 2 files changed, 43 insertions(+), 64 deletions(-) delete mode 100644 sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverterTest.java diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java index e79b9f8b25a86..54583e3763115 100644 --- a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java +++ b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtBearerTokenAuthenticationConverterTest.java @@ -35,7 +35,6 @@ public void init() { when(jwt.getHeaders()).thenReturn(headers); when(jwt.getExpiresAt()).thenReturn(Instant.MAX); when(jwt.getClaims()).thenReturn(claims); - when(jwt.containsClaim("scp")).thenReturn(true); } @Test @@ -51,7 +50,22 @@ public void testCreateUserPrincipal() { } @Test - public void testExtractDefaultScopeAuthorities() { + public void testNoArgumentsConstructorDefaultScopeAndRoleAuthorities() { + when(jwt.containsClaim("scp")).thenReturn(true); + when(jwt.containsClaim("roles")).thenReturn(true); + AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter(); + AbstractAuthenticationToken authenticationToken = converter.convert(jwt); + assertThat(authenticationToken.getPrincipal()).isExactlyInstanceOf(AADOAuth2AuthenticatedPrincipal.class); + AADOAuth2AuthenticatedPrincipal principal = (AADOAuth2AuthenticatedPrincipal) authenticationToken + .getPrincipal(); + assertThat(principal.getAttributes()).isNotEmpty(); + assertThat(principal.getAttributes()).hasSize(2); + assertThat(principal.getAuthorities()).hasSize(4); + } + + @Test + public void testNoArgumentsConstructorExtractScopeAuthorities() { + when(jwt.containsClaim("scp")).thenReturn(true); AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter(); AbstractAuthenticationToken authenticationToken = converter.convert(jwt); assertThat(authenticationToken.getPrincipal()).isExactlyInstanceOf(AADOAuth2AuthenticatedPrincipal.class); @@ -63,10 +77,9 @@ public void testExtractDefaultScopeAuthorities() { } @Test - public void testExtractRoleScopeAuthorities() { + public void testNoArgumentsConstructorExtractRoleAuthorities() { when(jwt.containsClaim("roles")).thenReturn(true); - AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter("roles", - "APPROLE_"); + AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter(); AbstractAuthenticationToken authenticationToken = converter.convert(jwt); assertThat(authenticationToken.getPrincipal()).isExactlyInstanceOf(AADOAuth2AuthenticatedPrincipal.class); AADOAuth2AuthenticatedPrincipal principal = (AADOAuth2AuthenticatedPrincipal) authenticationToken @@ -76,5 +89,30 @@ public void testExtractRoleScopeAuthorities() { assertThat(principal.getAuthorities()).hasSize(2); } + @Test + public void testParameterConstructorExtractScopeAuthorities() { + when(jwt.containsClaim("scp")).thenReturn(true); + AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter("scp"); + AbstractAuthenticationToken authenticationToken = converter.convert(jwt); + assertThat(authenticationToken.getPrincipal()).isExactlyInstanceOf(AADOAuth2AuthenticatedPrincipal.class); + AADOAuth2AuthenticatedPrincipal principal = (AADOAuth2AuthenticatedPrincipal) authenticationToken + .getPrincipal(); + assertThat(principal.getAttributes()).isNotEmpty(); + assertThat(principal.getAttributes()).hasSize(2); + assertThat(principal.getAuthorities()).hasSize(2); + } + @Test + public void testParameterConstructorExtractRoleAuthorities() { + when(jwt.containsClaim("roles")).thenReturn(true); + AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter("roles", + "APPROLE_"); + AbstractAuthenticationToken authenticationToken = converter.convert(jwt); + assertThat(authenticationToken.getPrincipal()).isExactlyInstanceOf(AADOAuth2AuthenticatedPrincipal.class); + AADOAuth2AuthenticatedPrincipal principal = (AADOAuth2AuthenticatedPrincipal) authenticationToken + .getPrincipal(); + assertThat(principal.getAttributes()).isNotEmpty(); + assertThat(principal.getAttributes()).hasSize(2); + assertThat(principal.getAuthorities()).hasSize(2); + } } diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverterTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverterTest.java deleted file mode 100644 index c8a446be2a811..0000000000000 --- a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapi/AADJwtGrantedAuthoritiesConverterTest.java +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.spring.aad.webapi; - -import net.minidev.json.JSONArray; -import org.junit.Before; -import org.junit.Test; -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.oauth2.jwt.Jwt; - -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class AADJwtGrantedAuthoritiesConverterTest { - - private Jwt jwt = mock(Jwt.class); - private Map claims = new HashMap<>(); - private Map headers = new HashMap<>(); - private JSONArray jsonArray = new JSONArray().appendElement("User.read").appendElement("User.write"); - - @Before - public void init() { - claims.put("iss", "fake-issuer"); - claims.put("tid", "fake-tid"); - headers.put("kid", "kg2LYs2T0CTjIfj4rt6JIynen38"); - when(jwt.getClaim("scp")).thenReturn("Order.read Order.write"); - when(jwt.getClaim("roles")).thenReturn(jsonArray); - when(jwt.getTokenValue()).thenReturn("fake-token-value"); - when(jwt.getIssuedAt()).thenReturn(Instant.now()); - when(jwt.getHeaders()).thenReturn(headers); - when(jwt.getExpiresAt()).thenReturn(Instant.MAX); - when(jwt.getClaims()).thenReturn(claims); - when(jwt.containsClaim("scp")).thenReturn(true); - when(jwt.containsClaim("roles")).thenReturn(true); - } - - @Test - public void testExtractRoleScopeAuthorities() { - AADJwtBearerTokenAuthenticationConverter converter = new AADJwtBearerTokenAuthenticationConverter(); - AADJwtGrantedAuthoritiesConverter aadJwtGrantedAuthoritiesConverter = new AADJwtGrantedAuthoritiesConverter(); - converter.setJwtGrantedAuthoritiesConverter(aadJwtGrantedAuthoritiesConverter); - AbstractAuthenticationToken authenticationToken = converter.convert(jwt); - assertThat(authenticationToken.getPrincipal()).isExactlyInstanceOf(AADOAuth2AuthenticatedPrincipal.class); - AADOAuth2AuthenticatedPrincipal principal = (AADOAuth2AuthenticatedPrincipal) authenticationToken - .getPrincipal(); - assertThat(principal.getAttributes()).isNotEmpty(); - assertThat(principal.getAttributes()).hasSize(2); - assertThat(principal.getAuthorities()).hasSize(4); - } - - -} - -