From 5b5d822ac1e616d9f3e035a6e32945eca4465260 Mon Sep 17 00:00:00 2001 From: Moary Chen Date: Mon, 10 May 2021 15:03:01 +0800 Subject: [PATCH] Optimization of Spring AAD B2C configuration condition (#21089) --- .../CHANGELOG.md | 2 +- .../b2c/AADB2CAutoConfiguration.java | 2 + .../autoconfigure/b2c/AADB2CConditions.java | 73 ++++++++++++--- .../b2c/AADB2COAuth2ClientConfiguration.java | 2 + ...AADB2CResourceServerAutoConfiguration.java | 2 + .../b2c/AADB2CAutoConfigurationTest.java | 93 +++++++++++++++---- ...2CResourceServerAutoConfigurationTest.java | 54 ++++++++++- ...ctAADB2COAuth2ClientTestConfiguration.java | 7 +- 8 files changed, 196 insertions(+), 39 deletions(-) diff --git a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/CHANGELOG.md b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/CHANGELOG.md index a4cb8e61296e..d9d09385e231 100644 --- a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/CHANGELOG.md +++ b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/CHANGELOG.md @@ -4,7 +4,7 @@ ### New Features - Upgrade to [spring-boot-dependencies:2.4.5](https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-dependencies/2.4.5/spring-boot-dependencies-2.4.5.pom). - Upgrade to [spring-cloud-dependencies:2020.0.2](https://repo.maven.apache.org/maven2/org/springframework/cloud/spring-cloud-dependencies/2020.0.2/spring-cloud-dependencies-2020.0.2.pom). - +- Support OAuth 2.0 Client Credentials Flow. ## 3.4.0 (2021-04-19) diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CAutoConfiguration.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CAutoConfiguration.java index 9f95524618ef..96543d0a6757 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CAutoConfiguration.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CAutoConfiguration.java @@ -4,6 +4,7 @@ import com.azure.spring.telemetry.TelemetrySender; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -27,6 +28,7 @@ * and import {@link AADB2COAuth2ClientConfiguration} class for AAD B2C OAuth2 client support. */ @Configuration +@ConditionalOnResource(resources = "classpath:aadb2c.enable.config") @Conditional({ AADB2CConditions.CommonCondition.class, AADB2CConditions.UserFlowCondition.class }) @EnableConfigurationProperties(AADB2CProperties.class) @Import(AADB2COAuth2ClientConfiguration.class) diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CConditions.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CConditions.java index 4ff9500d001e..3288a26f73fa 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CConditions.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CConditions.java @@ -3,9 +3,9 @@ package com.azure.spring.autoconfigure.b2c; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; +import org.springframework.boot.autoconfigure.condition.ConditionMessage; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.context.properties.bind.Binder; @@ -13,6 +13,8 @@ import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.CollectionUtils; +import java.util.Map; + /** * Conditions for activating AAD B2C beans. */ @@ -30,7 +32,6 @@ static final class CommonCondition extends AnyNestedCondition { * Web application scenario condition. */ @ConditionalOnWebApplication - @ConditionalOnResource(resources = "classpath:aadb2c.enable.config") @ConditionalOnProperty( prefix = AADB2CProperties.PREFIX, value = { @@ -46,7 +47,6 @@ static class WebAppMode { * Web resource server scenario condition. */ @ConditionalOnWebApplication - @ConditionalOnResource(resources = "classpath:aadb2c.enable.config") @ConditionalOnProperty(prefix = AADB2CProperties.PREFIX, value = { "tenant-id" }) static class WebApiMode { @@ -61,12 +61,28 @@ static final class ClientRegistrationCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(final ConditionContext context, final AnnotatedTypeMetadata metadata) { - AADB2CProperties aadb2CProperties = Binder.get(context.getEnvironment()) - .bind("azure.activedirectory.b2c", AADB2CProperties.class) - .orElseGet(AADB2CProperties::new); - return new ConditionOutcome(!CollectionUtils.isEmpty(aadb2CProperties.getUserFlows()) - || !CollectionUtils.isEmpty(aadb2CProperties.getAuthorizationClients()), - "Configure at least one attribute 'user-flow' or 'authorization-clients'."); + ConditionMessage.Builder message = ConditionMessage.forCondition( + "AAD B2C OAuth 2.0 Clients Configured Condition"); + AADB2CProperties aadb2CProperties = getAADB2CProperties(context); + if (aadb2CProperties == null) { + return ConditionOutcome.noMatch(message.notAvailable("aad b2c properties")); + } + + if (CollectionUtils.isEmpty(aadb2CProperties.getUserFlows()) + && CollectionUtils.isEmpty(aadb2CProperties.getAuthorizationClients())) { + return ConditionOutcome.noMatch(message.didNotFind("registered clients") + .items("user-flows", "authorization-clients")); + } + + StringBuilder details = new StringBuilder(); + if (!CollectionUtils.isEmpty(aadb2CProperties.getUserFlows())) { + details.append(getConditionResult("user-flows", aadb2CProperties.getUserFlows())); + } + if (!CollectionUtils.isEmpty(aadb2CProperties.getAuthorizationClients())) { + details.append(getConditionResult("authorization-clients", + aadb2CProperties.getAuthorizationClients())); + } + return ConditionOutcome.match(message.foundExactly(details.toString())); } } @@ -78,11 +94,40 @@ static final class UserFlowCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(final ConditionContext context, final AnnotatedTypeMetadata metadata) { - AADB2CProperties aadb2CProperties = Binder.get(context.getEnvironment()) - .bind("azure.activedirectory.b2c", AADB2CProperties.class) - .orElseGet(AADB2CProperties::new); - return new ConditionOutcome(!CollectionUtils.isEmpty(aadb2CProperties.getUserFlows()), - "Configure at least one attribute 'user-flow'."); + ConditionMessage.Builder message = ConditionMessage.forCondition( + "AAD B2C User Flow Clients Configured Condition"); + AADB2CProperties aadb2CProperties = getAADB2CProperties(context); + if (aadb2CProperties == null) { + return ConditionOutcome.noMatch(message.notAvailable("aad b2c properties")); + } + + if (CollectionUtils.isEmpty(aadb2CProperties.getUserFlows())) { + return ConditionOutcome.noMatch(message.didNotFind("user flows").atAll()); + } + + return ConditionOutcome.match(message.foundExactly( + getConditionResult("user-flows", aadb2CProperties.getUserFlows()))); } } + + /** + * Return the bound AADB2CProperties instance. + * @param context Condition context + * @return AADB2CProperties instance + */ + private static AADB2CProperties getAADB2CProperties(ConditionContext context) { + return Binder.get(context.getEnvironment()) + .bind("azure.activedirectory.b2c", AADB2CProperties.class) + .orElse(null); + } + + /** + * Return combined name and the string of the keys of the map which concatenated with ','. + * @param name name to concatenate + * @param map Map to concatenate + * @return the concatenated string. + */ + private static String getConditionResult(String name, Map map) { + return name + ": " + String.join(", ", map.keySet()) + " "; + } } diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2COAuth2ClientConfiguration.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2COAuth2ClientConfiguration.java index 8e5bdb8cdb86..8c3a721ca6e3 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2COAuth2ClientConfiguration.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2COAuth2ClientConfiguration.java @@ -7,6 +7,7 @@ import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -31,6 +32,7 @@ * Configuration for AAD B2C OAuth2 client support, when depends on the Spring OAuth2 Client module. */ @Configuration +@ConditionalOnResource(resources = "classpath:aadb2c.enable.config") @Conditional({ AADB2CConditions.CommonCondition.class, AADB2CConditions.ClientRegistrationCondition.class }) @EnableConfigurationProperties(AADB2CProperties.class) @ConditionalOnClass({ OAuth2LoginAuthenticationFilter.class }) diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CResourceServerAutoConfiguration.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CResourceServerAutoConfiguration.java index 7bd650f2f45a..d0f9ed4bb8c9 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CResourceServerAutoConfiguration.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/b2c/AADB2CResourceServerAutoConfiguration.java @@ -13,6 +13,7 @@ import com.nimbusds.jwt.proc.JWTProcessor; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -38,6 +39,7 @@ * and import {@link AADB2COAuth2ClientConfiguration} class for AAD B2C OAuth2 client support. */ @Configuration +@ConditionalOnResource(resources = "classpath:aadb2c.enable.config") @Conditional(AADB2CConditions.CommonCondition.class) @ConditionalOnClass(BearerTokenAuthenticationToken.class) @EnableConfigurationProperties(AADB2CProperties.class) diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AADB2CAutoConfigurationTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AADB2CAutoConfigurationTest.java index 33cfd21a934d..1bc8e894f22d 100644 --- a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AADB2CAutoConfigurationTest.java +++ b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AADB2CAutoConfigurationTest.java @@ -2,11 +2,16 @@ // Licensed under the MIT License. package com.azure.spring.autoconfigure.b2c; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.BeanUtils; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.core.io.ClassPathResource; import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; import java.util.Arrays; @@ -14,30 +19,40 @@ import java.util.Map; import java.util.Set; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + public class AADB2CAutoConfigurationTest extends AbstractAADB2COAuth2ClientTestConfiguration { public AADB2CAutoConfigurationTest() { contextRunner = new WebApplicationContextRunner() .withConfiguration(AutoConfigurations.of(WebOAuth2ClientApp.class, AADB2CAutoConfiguration.class)) .withClassLoader(new FilteredClassLoader(BearerTokenAuthenticationToken.class)) - .withPropertyValues( - String.format("%s=%s", AADB2CConstants.BASE_URI, AADB2CConstants.TEST_BASE_URI), - String.format("%s=%s", AADB2CConstants.TENANT_ID, AADB2CConstants.TEST_TENANT_ID), - String.format("%s=%s", AADB2CConstants.CLIENT_ID, AADB2CConstants.TEST_CLIENT_ID), - String.format("%s=%s", AADB2CConstants.CLIENT_SECRET, AADB2CConstants.TEST_CLIENT_SECRET), - String.format("%s=%s", AADB2CConstants.LOGOUT_SUCCESS_URL, AADB2CConstants.TEST_LOGOUT_SUCCESS_URL), - String.format("%s=%s", AADB2CConstants.LOGIN_FLOW, AADB2CConstants.TEST_KEY_SIGN_UP_OR_IN), - String.format("%s.%s=%s", AADB2CConstants.USER_FLOWS, - AADB2CConstants.TEST_KEY_SIGN_UP_OR_IN, AADB2CConstants.TEST_SIGN_UP_OR_IN_NAME), - String.format("%s.%s=%s", AADB2CConstants.USER_FLOWS, - AADB2CConstants.TEST_KEY_SIGN_IN, AADB2CConstants.TEST_SIGN_IN_NAME), - String.format("%s.%s=%s", AADB2CConstants.USER_FLOWS, - AADB2CConstants.TEST_KEY_SIGN_UP, AADB2CConstants.TEST_SIGN_UP_NAME), - String.format("%s=%s", AADB2CConstants.CONFIG_PROMPT, AADB2CConstants.TEST_PROMPT), - String.format("%s=%s", AADB2CConstants.CONFIG_LOGIN_HINT, AADB2CConstants.TEST_LOGIN_HINT), - String.format("%s=%s", AADB2CConstants.USER_NAME_ATTRIBUTE_NAME, AADB2CConstants.TEST_ATTRIBUTE_NAME), - String.format("%s=%s", AADB2CConstants.USER_NAME_ATTRIBUTE_NAME, AADB2CConstants.TEST_ATTRIBUTE_NAME) - ); + .withPropertyValues(getWebappCommonPropertyValues()); + } + + @NotNull + private String[] getWebappCommonPropertyValues() { + return new String[] { String.format("%s=%s", AADB2CConstants.BASE_URI, AADB2CConstants.TEST_BASE_URI), + String.format("%s=%s", AADB2CConstants.TENANT_ID, AADB2CConstants.TEST_TENANT_ID), + String.format("%s=%s", AADB2CConstants.CLIENT_ID, AADB2CConstants.TEST_CLIENT_ID), + String.format("%s=%s", AADB2CConstants.CLIENT_SECRET, AADB2CConstants.TEST_CLIENT_SECRET), + String.format("%s=%s", AADB2CConstants.LOGOUT_SUCCESS_URL, AADB2CConstants.TEST_LOGOUT_SUCCESS_URL), + String.format("%s=%s", AADB2CConstants.LOGIN_FLOW, AADB2CConstants.TEST_KEY_SIGN_UP_OR_IN), + String.format("%s.%s=%s", AADB2CConstants.USER_FLOWS, + AADB2CConstants.TEST_KEY_SIGN_UP_OR_IN, AADB2CConstants.TEST_SIGN_UP_OR_IN_NAME), + String.format("%s.%s=%s", AADB2CConstants.USER_FLOWS, + AADB2CConstants.TEST_KEY_SIGN_IN, AADB2CConstants.TEST_SIGN_IN_NAME), + String.format("%s.%s=%s", AADB2CConstants.USER_FLOWS, + AADB2CConstants.TEST_KEY_SIGN_UP, AADB2CConstants.TEST_SIGN_UP_NAME), + String.format("%s=%s", AADB2CConstants.CONFIG_PROMPT, AADB2CConstants.TEST_PROMPT), + String.format("%s=%s", AADB2CConstants.CONFIG_LOGIN_HINT, AADB2CConstants.TEST_LOGIN_HINT), + String.format("%s=%s", AADB2CConstants.USER_NAME_ATTRIBUTE_NAME, AADB2CConstants.TEST_ATTRIBUTE_NAME) }; } @Test @@ -88,4 +103,46 @@ public void testLogoutSuccessHandlerBean() { Assertions.assertNotNull(handler); }); } + + @Test + public void testWebappConditionsIsInvokedWhenAADB2CEnableFileExists() { + try (MockedStatic beanUtils = mockStatic(BeanUtils.class, Mockito.CALLS_REAL_METHODS)) { + AADB2CConditions.UserFlowCondition userFlowCondition = spy(AADB2CConditions.UserFlowCondition.class); + AADB2CConditions.ClientRegistrationCondition clientRegistrationCondition = + spy(AADB2CConditions.ClientRegistrationCondition.class); + beanUtils.when(() -> BeanUtils.instantiateClass(AADB2CConditions.UserFlowCondition.class)) + .thenReturn(userFlowCondition); + beanUtils.when(() -> BeanUtils.instantiateClass(AADB2CConditions.ClientRegistrationCondition.class)) + .thenReturn(clientRegistrationCondition); + this.contextRunner + .run(c -> { + Assertions.assertTrue(c.getResource(AAD_B2C_ENABLE_CONFIG_FILE_NAME).exists()); + verify(userFlowCondition, atLeastOnce()).getMatchOutcome(any(), any()); + verify(clientRegistrationCondition, atLeastOnce()).getMatchOutcome(any(), any()); + }); + } + } + + @Test + public void testWebappConditionsIsNotInvokedWhenAADB2CEnableFileDoesNotExists() { + try (MockedStatic beanUtils = mockStatic(BeanUtils.class, Mockito.CALLS_REAL_METHODS)) { + AADB2CConditions.UserFlowCondition userFlowCondition = mock(AADB2CConditions.UserFlowCondition.class); + AADB2CConditions.ClientRegistrationCondition clientRegistrationCondition = + spy(AADB2CConditions.ClientRegistrationCondition.class); + beanUtils.when(() -> BeanUtils.instantiateClass(AADB2CConditions.UserFlowCondition.class)) + .thenReturn(userFlowCondition); + beanUtils.when(() -> BeanUtils.instantiateClass(AADB2CConditions.ClientRegistrationCondition.class)) + .thenReturn(clientRegistrationCondition); + new WebApplicationContextRunner() + .withClassLoader(new FilteredClassLoader(new ClassPathResource(AAD_B2C_ENABLE_CONFIG_FILE_NAME))) + .withConfiguration(AutoConfigurations.of(WebResourceServerApp.class, + AADB2CResourceServerAutoConfiguration.class)) + .withPropertyValues(getWebappCommonPropertyValues()) + .run(c -> { + Assertions.assertFalse(c.getResource(AAD_B2C_ENABLE_CONFIG_FILE_NAME).exists()); + verify(userFlowCondition, never()).getMatchOutcome(any(), any()); + verify(clientRegistrationCondition, never()).getMatchOutcome(any(), any()); + }); + } + } } diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AADB2CResourceServerAutoConfigurationTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AADB2CResourceServerAutoConfigurationTest.java index 7d916ad22a5e..17bf59fdf9dc 100644 --- a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AADB2CResourceServerAutoConfigurationTest.java +++ b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AADB2CResourceServerAutoConfigurationTest.java @@ -6,14 +6,26 @@ import com.azure.spring.aad.AADTrustedIssuerRepository; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.BeanUtils; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.context.ApplicationContext; +import org.springframework.core.io.ClassPathResource; import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; import org.springframework.security.oauth2.jwt.JwtDecoder; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + public class AADB2CResourceServerAutoConfigurationTest extends AbstractAADB2COAuth2ClientTestConfiguration { private final WebApplicationContextRunner resourceServerContextRunner = new WebApplicationContextRunner() @@ -69,7 +81,7 @@ private ContextConsumer b2CResourceServerBean() { @Test public void testB2COAuth2ClientAutoConfigurationBean() { - this.contextRunner.withPropertyValues(getClientCredentialConfig()) + this.contextRunner.withPropertyValues(getAuthorizationClientPropertyValues()) .run(b2CAutoConfigurationBean()); } @@ -80,7 +92,7 @@ public void testB2COnlyAutoConfigurationBean() { @Test public void testB2COAuth2ClientResourceServerPropertiesBean() { - this.contextRunner.withPropertyValues(getClientCredentialConfig()) + this.contextRunner.withPropertyValues(getAuthorizationClientPropertyValues()) .run(b2CResourceServerPropertiesBean()); } @@ -91,7 +103,7 @@ public void testB2COnlyResourceServerPropertiesBean() { @Test public void testB2COAuth2ClientResourceServerBean() { - this.contextRunner.withPropertyValues(getClientCredentialConfig()) + this.contextRunner.withPropertyValues(getAuthorizationClientPropertyValues()) .run(b2CResourceServerBean()); } @@ -99,4 +111,40 @@ public void testB2COAuth2ClientResourceServerBean() { public void testB2COnlyResourceServerBean() { this.resourceServerContextRunner.run(b2CResourceServerBean()); } + + @Test + public void testResourceServerConditionsIsInvokedWhenAADB2CEnableFileExists() { + try (MockedStatic beanUtils = mockStatic(BeanUtils.class, Mockito.CALLS_REAL_METHODS)) { + AADB2CConditions.ClientRegistrationCondition clientRegistrationCondition = + spy(AADB2CConditions.ClientRegistrationCondition.class); + beanUtils.when(() -> BeanUtils.instantiateClass(AADB2CConditions.ClientRegistrationCondition.class)) + .thenReturn(clientRegistrationCondition); + this.contextRunner + .withPropertyValues(getAuthorizationClientPropertyValues()) + .run(c -> { + Assertions.assertTrue(c.getResource(AAD_B2C_ENABLE_CONFIG_FILE_NAME).exists()); + verify(clientRegistrationCondition, atLeastOnce()).getMatchOutcome(any(), any()); + }); + } + } + + @Test + public void testResourceServerConditionsIsNotInvokedWhenAADB2CEnableFileDoesNotExists() { + try (MockedStatic beanUtils = mockStatic(BeanUtils.class, Mockito.CALLS_REAL_METHODS)) { + AADB2CConditions.ClientRegistrationCondition clientRegistrationCondition = + mock(AADB2CConditions.ClientRegistrationCondition.class); + beanUtils.when(() -> BeanUtils.instantiateClass(AADB2CConditions.ClientRegistrationCondition.class)) + .thenReturn(clientRegistrationCondition); + new WebApplicationContextRunner() + .withClassLoader(new FilteredClassLoader(new ClassPathResource(AAD_B2C_ENABLE_CONFIG_FILE_NAME))) + .withConfiguration(AutoConfigurations.of(WebOAuth2ClientApp.class, + AADB2CResourceServerAutoConfiguration.class)) + .withPropertyValues(getB2CResourceServerProperties()) + .withPropertyValues(getAuthorizationClientPropertyValues()) + .run(c -> { + Assertions.assertFalse(c.getResource(AAD_B2C_ENABLE_CONFIG_FILE_NAME).exists()); + verify(clientRegistrationCondition, never()).getMatchOutcome(any(), any()); + }); + } + } } diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AbstractAADB2COAuth2ClientTestConfiguration.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AbstractAADB2COAuth2ClientTestConfiguration.java index f05ac2b13b6b..e1ed758bc433 100644 --- a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AbstractAADB2COAuth2ClientTestConfiguration.java +++ b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/autoconfigure/b2c/AbstractAADB2COAuth2ClientTestConfiguration.java @@ -29,9 +29,10 @@ public static class WebResourceServerApp { } + protected static final String AAD_B2C_ENABLE_CONFIG_FILE_NAME = "aadb2c.enable.config"; protected WebApplicationContextRunner contextRunner; - protected String[] getClientCredentialConfig() { + protected String[] getAuthorizationClientPropertyValues() { return new String[]{ String.format("%s.%s.scopes=%s", AADB2CConstants.AUTHORIZATION_CLIENTS, AADB2CConstants.CLIENT_CREDENTIAL_NAME, AADB2CConstants.TEST_CLIENT_CREDENTIAL_SCOPES), String.format("%s.%s.authorization-grant-type=%s", AADB2CConstants.AUTHORIZATION_CLIENTS, @@ -42,7 +43,7 @@ protected String[] getClientCredentialConfig() { @Test public void testClientCredentialProperties() { this.contextRunner - .withPropertyValues(getClientCredentialConfig()) + .withPropertyValues(getAuthorizationClientPropertyValues()) .run(c -> { final AADB2CProperties properties = c.getBean(AADB2CProperties.class); Assertions.assertNotNull(properties); @@ -61,7 +62,7 @@ public void testClientCredentialProperties() { @Test public void testClientRelatedBeans() { this.contextRunner - .withPropertyValues(getClientCredentialConfig()) + .withPropertyValues(getAuthorizationClientPropertyValues()) .run(c -> { final AADB2COAuth2ClientConfiguration config = c.getBean(AADB2COAuth2ClientConfiguration.class); final ClientRegistrationRepository clientRepo = c.getBean(ClientRegistrationRepository.class);