diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index 91e6f354513b..566c09d4db62 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -11,10 +11,12 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module. - Fix bug: RestOperations is not well configured when jwkResolver is null. [#31218](https://github.com/Azure/azure-sdk-for-java/issues/31218). - Fix bug: Duplicated "scope" parameter. [#31191](https://github.com/Azure/azure-sdk-for-java/issues/31191). - Fix bug: NimbusJwtDecoder still uses `RestTemplate()` instead `RestTemplateBuilder` [#31233](https://github.com/Azure/azure-sdk-for-java/issues/31233) -- Fix bug: Proxy setting not work in Azure AD B2C web application [31593](https://github.com/Azure/azure-sdk-for-java/issues/31593) +- Fix bug: Proxy setting not work in Azure AD B2C web application. [31593](https://github.com/Azure/azure-sdk-for-java/issues/31593) +- Fix Bug: NoClassDefFoundError for JSONArray. [31716](https://github.com/Azure/azure-sdk-for-java/issues/31716) - Fix bug: `spring.main.sources` configuration from Spring Cloud Stream Kafka binder cannot take effect. [#31715](https://github.com/Azure/azure-sdk-for-java/pull/31715) + ## 4.4.0 (2022-09-26) Upgrade Spring Boot dependencies version to 2.7.3 and Spring Cloud dependencies version to 2021.0.3 Upgrade Spring Boot dependencies version to 2.7.2 and Spring Cloud dependencies version to 2021.0.3. @@ -472,7 +474,7 @@ This section includes changes in the `spring-cloud-azure-autoconfigure` module. * Property name "spring.cloud.azure.active-directory.graph-base-uri" changed to "spring.cloud.azure.active-directory.profile.environment.microsoft-graph-endpoint". * Property name "spring.cloud.azure.active-directory.graph-membership-uri" changed to "spring.cloud.azure.active-directory.profile.environment.microsoft-graph-endpoint" and "spring.cloud.azure.active-directory.user-group.use-transitive-members". - Change AAD B2C configuration properties to use the namespace for credential and environment properties [#25799](https://github.com/Azure/azure-sdk-for-java/pull/25799). -- Change Event Hubs processor configuration properties `spring.cloud.azure.eventhbs.processor.partition-ownership-expiration-interval` to `spring.cloud.azure.eventhbs.processor.load-balancing.partition-ownership-expiration-interval` [#25851](https://github.com/Azure/azure-sdk-for-java/pull/25851). +- Change Event Hubs processor configuration properties `spring.cloud.azure.eventhubs.processor.partition-ownership-expiration-interval` to `spring.cloud.azure.eventhubs.processor.load-balancing.partition-ownership-expiration-interval` [#25851](https://github.com/Azure/azure-sdk-for-java/pull/25851). - Change Event Hubs configuration properties `spring.cloud.azure.eventhubs.fqdn` to `spring.cloud.azure.eventhubs.fully-qualified-namespace` [#25851](https://github.com/Azure/azure-sdk-for-java/pull/25851). - Rename all `*CP` classes to `*ConfigurationProperties` [#26209](https://github.com/Azure/azure-sdk-for-java/pull/26209). diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/filter/UserPrincipalManager.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/filter/UserPrincipalManager.java index a4772a8c2490..a3d2b1a9a4ed 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/filter/UserPrincipalManager.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/filter/UserPrincipalManager.java @@ -16,7 +16,6 @@ import com.nimbusds.jose.proc.JWSKeySelector; import com.nimbusds.jose.proc.JWSVerificationKeySelector; import com.nimbusds.jose.proc.SecurityContext; -import com.nimbusds.jose.shaded.json.JSONArray; import com.nimbusds.jose.util.ResourceRetriever; import com.nimbusds.jwt.JWT; import com.nimbusds.jwt.JWTClaimsSet; @@ -31,12 +30,13 @@ import java.net.MalformedURLException; import java.net.URL; import java.text.ParseException; -import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * A user principal manager to load user info from JWT. @@ -149,17 +149,28 @@ public UserPrincipal buildUserPrincipal(String aadIssuedBearerToken) throws Pars final JWTClaimsSet jwtClaimsSet = validator.process(aadIssuedBearerToken, null); validator.getJWTClaimsSetVerifier().verify(jwtClaimsSet, null); UserPrincipal userPrincipal = new UserPrincipal(aadIssuedBearerToken, jwsObject, jwtClaimsSet); - Set roles = Optional.of(userPrincipal) - .map(p -> p.getClaim(AadJwtClaimNames.ROLES)) - .map(JSONArray.class::cast) - .map(Collection::stream) - .orElseGet(Stream::empty) - .map(Object::toString) - .collect(Collectors.toSet()); - userPrincipal.setRoles(roles); + userPrincipal.setRoles(getRoles(jwtClaimsSet)); return userPrincipal; } + Set getRoles(JWTClaimsSet set) { + if (set == null) { + return Collections.emptySet(); + } + Object rolesClaim = set.getClaim(AadJwtClaimNames.ROLES); + if (rolesClaim == null) { + return Collections.emptySet(); + } + if (rolesClaim instanceof Iterable) { + return StreamSupport.stream(((Iterable) rolesClaim).spliterator(), false) + .map(Object::toString) + .collect(Collectors.toSet()); + } + return Stream.of(rolesClaim) + .map(Object::toString) + .collect(Collectors.toSet()); + } + /** * Whether the token was issued by AAD. * diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/UserPrincipalManagerTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/UserPrincipalManagerTests.java index 8c4c4f0482ef..7f2feaa60c4d 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/UserPrincipalManagerTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/UserPrincipalManagerTests.java @@ -7,6 +7,7 @@ import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.source.ImmutableJWKSet; import com.nimbusds.jose.proc.SecurityContext; +import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.proc.BadJWTException; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -19,10 +20,17 @@ import java.nio.file.Paths; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; class UserPrincipalManagerTests { @@ -33,8 +41,7 @@ class UserPrincipalManagerTests { static void setupClass() throws Exception { final X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509") .generateCertificate(Files.newInputStream(Paths.get("src/test/resources/aad/test-public-key.txt"))); - immutableJWKSet = new ImmutableJWKSet<>(new JWKSet(JWK.parse( - cert))); + immutableJWKSet = new ImmutableJWKSet<>(new JWKSet(JWK.parse(cert))); } private UserPrincipalManager userPrincipalManager; @@ -44,8 +51,7 @@ static void setupClass() throws Exception { void testAlgIsTakenFromJWT() throws Exception { userPrincipalManager = new UserPrincipalManager(immutableJWKSet); final UserPrincipal userPrincipal = userPrincipalManager.buildUserPrincipal( - new String(Files.readAllBytes( - Paths.get("src/test/resources/aad/jwt-signed.txt")), StandardCharsets.UTF_8)); + readFileToString("src/test/resources/aad/jwt-signed.txt")); assertThat(userPrincipal).isNotNull().extracting(UserPrincipal::getIssuer, UserPrincipal::getSubject) .containsExactly("https://sts.windows.net/test", "test@example.com"); } @@ -73,14 +79,38 @@ void nullIssuer() { .isInstanceOf(BadJWTException.class); } - private String readJwtValidIssuerTxt() throws IOException { - return new String(Files.readAllBytes( - Paths.get("src/test/resources/aad/jwt-null-issuer.txt")), StandardCharsets.UTF_8); + @Test + void getRolesTest() { + rolesExtractedAsExpected(null, new ArrayList<>()); + rolesExtractedAsExpected("role1", Arrays.asList("role1")); + rolesExtractedAsExpected(Arrays.asList("role1", "role2"), Arrays.asList("role1", "role2")); + rolesExtractedAsExpected(new HashSet<>(Arrays.asList("role1", "role2")), Arrays.asList("role1", "role2")); + } + + private void rolesExtractedAsExpected(Object rolesClaimValue, Collection expected) { + JWTClaimsSet set = new JWTClaimsSet.Builder() + .claim("roles", rolesClaimValue) + .build(); + Set actual = new UserPrincipalManager(null).getRoles(set); + assertEquals(expected.size(), actual.size()); + assertTrue(expected.containsAll(actual)); + assertTrue(actual.containsAll(expected)); + } + + private String readJwtValidIssuerTxt() { + return readFileToString("src/test/resources/aad/jwt-null-issuer.txt"); + } + + private static Stream readJwtValidIssuerTxtStream() { + return Stream.of(readFileToString("src/test/resources/aad/jwt-valid-issuer.txt")); } - private static Stream readJwtValidIssuerTxtStream() throws IOException { - return Stream.of(new String(Files.readAllBytes( - Paths.get("src/test/resources/aad/jwt-valid-issuer.txt")), StandardCharsets.UTF_8)); + private static String readFileToString(String path) { + try { + return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new IllegalStateException(e); + } } }